/
strftime.go
119 lines (103 loc) · 3.56 KB
/
strftime.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
Package strftime implements Python's strftime in Go
Example:
str, err := strftime.Format("%Y/%m/%d", time.Now()) // 2019/07/07
Directives:
%a - Locale’s abbreviated weekday name
%A - Locale’s full weekday name
%b - Locale’s abbreviated month name
%B - Locale’s full month name
%c - Locale’s appropriate date and time representation
%d - Day of the month as a decimal number [01,31]
%H - Hour (24-hour clock) as a decimal number [00,23]
%I - Hour (12-hour clock) as a decimal number [01,12]
%j - Day of year
%m - Month as a decimal number [01,12]
%M - Minute as a decimal number [00,59]
%p - Locale’s equivalent of either AM or PM
%S - Second as a decimal number [00,61]
%U - Week number of the year
%w - Weekday as a decimal number
%W - Week number of the year
%x - Locale’s appropriate date representation
%X - Locale’s appropriate time representation
%y - Year without century as a decimal number [00,99]
%Y - Year with century as a decimal number
%Z - Time zone name (no characters if no time zone exists)
Note that %c returns RFC1123 which is a bit different from what Python does
*/
package strftime
import (
"fmt"
"regexp"
"time"
)
const (
// Version is the package version
Version = "0.1.5"
// Week in time.Duration
Week = time.Hour * 24 * 7
)
// See http://docs.python.org/3/library/time.html#time.strftime
var conv = map[string]string{
"%a": "Mon", // Locale’s abbreviated weekday name
"%A": "Monday", // Locale’s full weekday name
"%b": "Jan", // Locale’s abbreviated month name
"%B": "January", // Locale’s full month name
"%c": time.RFC1123, // Locale’s appropriate date and time representation
"%d": "02", // Day of the month as a decimal number [01,31]
"%H": "15", // Hour (24-hour clock) as a decimal number [00,23]
"%I": "03", // Hour (12-hour clock) as a decimal number [01,12]
"%m": "01", // Month as a decimal number [01,12]
"%M": "04", // Minute as a decimal number [00,59]
"%p": "PM", // Locale’s equivalent of either AM or PM
"%S": "05", // Second as a decimal number [00,61]
"%x": "01/02/06", // Locale’s appropriate date representation
"%X": "15:04:05", // Locale’s appropriate time representation
"%y": "06", // Year without century as a decimal number [00,99]
"%Y": "2006", // Year with century as a decimal number
"%Z": "MST", // Time zone name (no characters if no time zone exists)
}
var (
fmtRe = regexp.MustCompile("%[%a-zA-Z]")
)
// repl replaces % directives with right time, will panic on unknown directive
func repl(match string, t time.Time) (string, error) {
if match == "%%" {
return "%", nil
}
format, ok := conv[match]
if ok {
return t.Format(format), nil
}
switch match {
case "%j":
start := time.Date(t.Year(), time.January, 1, 0, 0, 0, 0, time.UTC)
day := int(t.Sub(start).Hours()/24) + 1
return fmt.Sprintf("%03d", day), nil
case "%w":
return fmt.Sprintf("%d", t.Weekday()), nil
case "%W", "%U":
start := time.Date(t.Year(), time.January, 1, 23, 0, 0, 0, time.UTC)
week := 0
for start.Before(t) {
week++
start = start.Add(Week)
}
return fmt.Sprintf("%02d", week), nil
}
return "", fmt.Errorf("unknown directive - %s", match)
}
// Format return string with % directives expanded.
// Will return error on unknown directive.
func Format(format string, t time.Time) (string, error) {
var err error
fn := func(match string) string {
s, rerr := repl(match, t)
if rerr != nil {
err = rerr
}
return s
}
return fmtRe.ReplaceAllStringFunc(format, fn), err
}