OSDN Git Service

log into file (#357)
[bytom/vapor.git] / vendor / github.com / lestrrat-go / strftime / strftime.go
1 package strftime
2
3 import (
4         "io"
5         "strings"
6         "time"
7
8         "github.com/pkg/errors"
9 )
10
11 var directives = map[byte]appender{
12         'A': timefmt("Monday"),
13         'a': timefmt("Mon"),
14         'B': timefmt("January"),
15         'b': timefmt("Jan"),
16         'C': &century{},
17         'c': timefmt("Mon Jan _2 15:04:05 2006"),
18         'D': timefmt("01/02/06"),
19         'd': timefmt("02"),
20         'e': timefmt("_2"),
21         'F': timefmt("2006-01-02"),
22         'H': timefmt("15"),
23         'h': timefmt("Jan"), // same as 'b'
24         'I': timefmt("3"),
25         'j': &dayofyear{},
26         'k': hourwblank(false),
27         'l': hourwblank(true),
28         'M': timefmt("04"),
29         'm': timefmt("01"),
30         'n': verbatim("\n"),
31         'p': timefmt("PM"),
32         'R': timefmt("15:04"),
33         'r': timefmt("3:04:05 PM"),
34         'S': timefmt("05"),
35         'T': timefmt("15:04:05"),
36         't': verbatim("\t"),
37         'U': weeknumberOffset(0), // week number of the year, Sunday first
38         'u': weekday(1),
39         'V': &weeknumber{},
40         'v': timefmt("_2-Jan-2006"),
41         'W': weeknumberOffset(1), // week number of the year, Monday first
42         'w': weekday(0),
43         'X': timefmt("15:04:05"), // national representation of the time XXX is this correct?
44         'x': timefmt("01/02/06"), // national representation of the date XXX is this correct?
45         'Y': timefmt("2006"),     // year with century
46         'y': timefmt("06"),       // year w/o century
47         'Z': timefmt("MST"),      // time zone name
48         'z': timefmt("-0700"),    // time zone ofset from UTC
49         '%': verbatim("%"),
50 }
51
52 type combiningAppend struct {
53         list           appenderList
54         prev           appender
55         prevCanCombine bool
56 }
57
58 func (ca *combiningAppend) Append(w appender) {
59         if ca.prevCanCombine {
60                 if wc, ok := w.(combiner); ok && wc.canCombine() {
61                         ca.prev = ca.prev.(combiner).combine(wc)
62                         ca.list[len(ca.list)-1] = ca.prev
63                         return
64                 }
65         }
66
67         ca.list = append(ca.list, w)
68         ca.prev = w
69         ca.prevCanCombine = false
70         if comb, ok := w.(combiner); ok {
71                 if comb.canCombine() {
72                         ca.prevCanCombine = true
73                 }
74         }
75 }
76
77 func compile(wl *appenderList, p string) error {
78         var ca combiningAppend
79         for l := len(p); l > 0; l = len(p) {
80                 i := strings.IndexByte(p, '%')
81                 if i < 0 {
82                         ca.Append(verbatim(p))
83                         // this is silly, but I don't trust break keywords when there's a
84                         // possibility of this piece of code being rearranged
85                         p = p[l:]
86                         continue
87                 }
88                 if i == l-1 {
89                         return errors.New(`stray % at the end of pattern`)
90                 }
91
92                 // we found a '%'. we need the next byte to decide what to do next
93                 // we already know that i < l - 1
94                 // everything up to the i is verbatim
95                 if i > 0 {
96                         ca.Append(verbatim(p[:i]))
97                         p = p[i:]
98                 }
99
100                 directive, ok := directives[p[1]]
101                 if !ok {
102                         return errors.Errorf(`unknown time format specification '%c'`, p[1])
103                 }
104                 ca.Append(directive)
105                 p = p[2:]
106         }
107
108         *wl = ca.list
109
110         return nil
111 }
112
113 // Format takes the format `s` and the time `t` to produce the
114 // format date/time. Note that this function re-compiles the
115 // pattern every time it is called.
116 //
117 // If you know beforehand that you will be reusing the pattern
118 // within your application, consider creating a `Strftime` object
119 // and reusing it.
120 func Format(p string, t time.Time) (string, error) {
121         var dst []byte
122         // TODO: optimize for 64 byte strings
123         dst = make([]byte, 0, len(p)+10)
124         // Compile, but execute as we go
125         for l := len(p); l > 0; l = len(p) {
126                 i := strings.IndexByte(p, '%')
127                 if i < 0 {
128                         dst = append(dst, p...)
129                         // this is silly, but I don't trust break keywords when there's a
130                         // possibility of this piece of code being rearranged
131                         p = p[l:]
132                         continue
133                 }
134                 if i == l-1 {
135                         return "", errors.New(`stray % at the end of pattern`)
136                 }
137
138                 // we found a '%'. we need the next byte to decide what to do next
139                 // we already know that i < l - 1
140                 // everything up to the i is verbatim
141                 if i > 0 {
142                         dst = append(dst, p[:i]...)
143                         p = p[i:]
144                 }
145
146                 directive, ok := directives[p[1]]
147                 if !ok {
148                         return "", errors.Errorf(`unknown time format specification '%c'`, p[1])
149                 }
150                 dst = directive.Append(dst, t)
151                 p = p[2:]
152         }
153
154         return string(dst), nil
155 }
156
157 // Strftime is the object that represents a compiled strftime pattern
158 type Strftime struct {
159         pattern  string
160         compiled appenderList
161 }
162
163 // New creates a new Strftime object. If the compilation fails, then
164 // an error is returned in the second argument.
165 func New(f string) (*Strftime, error) {
166         var wl appenderList
167         if err := compile(&wl, f); err != nil {
168                 return nil, errors.Wrap(err, `failed to compile format`)
169         }
170         return &Strftime{
171                 pattern:  f,
172                 compiled: wl,
173         }, nil
174 }
175
176 // Pattern returns the original pattern string
177 func (f *Strftime) Pattern() string {
178         return f.pattern
179 }
180
181 // Format takes the destination `dst` and time `t`. It formats the date/time
182 // using the pre-compiled pattern, and outputs the results to `dst`
183 func (f *Strftime) Format(dst io.Writer, t time.Time) error {
184         const bufSize = 64
185         var b []byte
186         max := len(f.pattern) + 10
187         if max < bufSize {
188                 var buf [bufSize]byte
189                 b = buf[:0]
190         } else {
191                 b = make([]byte, 0, max)
192         }
193         if _, err := dst.Write(f.format(b, t)); err != nil {
194                 return err
195         }
196         return nil
197 }
198
199 func (f *Strftime) format(b []byte, t time.Time) []byte {
200         for _, w := range f.compiled {
201                 b = w.Append(b, t)
202         }
203         return b
204 }
205
206 // FormatString takes the time `t` and formats it, returning the
207 // string containing the formated data.
208 func (f *Strftime) FormatString(t time.Time) string {
209         const bufSize = 64
210         var b []byte
211         max := len(f.pattern) + 10
212         if max < bufSize {
213                 var buf [bufSize]byte
214                 b = buf[:0]
215         } else {
216                 b = make([]byte, 0, max)
217         }
218         return string(f.format(b, t))
219 }