|
| 1 | +package strftime |
| 2 | + |
| 3 | +import ( |
| 4 | + "strconv" |
| 5 | + "strings" |
| 6 | + "time" |
| 7 | +) |
| 8 | + |
| 9 | +// These are all of the standard, POSIX compliant specifications. |
| 10 | +// Extensions should be in extensions.go |
| 11 | +var ( |
| 12 | + fullWeekDayName = StdlibFormat("Monday") |
| 13 | + abbrvWeekDayName = StdlibFormat("Mon") |
| 14 | + fullMonthName = StdlibFormat("January") |
| 15 | + abbrvMonthName = StdlibFormat("Jan") |
| 16 | + centuryDecimal = AppendFunc(appendCentury) |
| 17 | + timeAndDate = StdlibFormat("Mon Jan _2 15:04:05 2006") |
| 18 | + mdy = StdlibFormat("01/02/06") |
| 19 | + dayOfMonthZeroPad = StdlibFormat("02") |
| 20 | + dayOfMonthSpacePad = StdlibFormat("_2") |
| 21 | + ymd = StdlibFormat("2006-01-02") |
| 22 | + twentyFourHourClockZeroPad = StdlibFormat("15") |
| 23 | + twelveHourClockZeroPad = StdlibFormat("3") |
| 24 | + dayOfYear = AppendFunc(appendDayOfYear) |
| 25 | + twentyFourHourClockSpacePad = hourwblank(false) |
| 26 | + twelveHourClockSpacePad = hourwblank(true) |
| 27 | + minutesZeroPad = StdlibFormat("04") |
| 28 | + monthNumberZeroPad = StdlibFormat("01") |
| 29 | + newline = Verbatim("\n") |
| 30 | + ampm = StdlibFormat("PM") |
| 31 | + hm = StdlibFormat("15:04") |
| 32 | + imsp = StdlibFormat("3:04:05 PM") |
| 33 | + secondsNumberZeroPad = StdlibFormat("05") |
| 34 | + hms = StdlibFormat("15:04:05") |
| 35 | + tab = Verbatim("\t") |
| 36 | + weekNumberSundayOrigin = weeknumberOffset(0) // week number of the year, Sunday first |
| 37 | + weekdayMondayOrigin = weekday(1) |
| 38 | + // monday as the first day, and 01 as the first value |
| 39 | + weekNumberMondayOriginOneOrigin = AppendFunc(appendWeekNumber) |
| 40 | + eby = StdlibFormat("_2-Jan-2006") |
| 41 | + // monday as the first day, and 00 as the first value |
| 42 | + weekNumberMondayOrigin = weeknumberOffset(1) // week number of the year, Monday first |
| 43 | + weekdaySundayOrigin = weekday(0) |
| 44 | + natReprTime = StdlibFormat("15:04:05") // national representation of the time XXX is this correct? |
| 45 | + natReprDate = StdlibFormat("01/02/06") // national representation of the date XXX is this correct? |
| 46 | + year = StdlibFormat("2006") // year with century |
| 47 | + yearNoCentury = StdlibFormat("06") // year w/o century |
| 48 | + timezone = StdlibFormat("MST") // time zone name |
| 49 | + timezoneOffset = StdlibFormat("-0700") // time zone ofset from UTC |
| 50 | + percent = Verbatim("%") |
| 51 | +) |
| 52 | + |
| 53 | +// Appender is the interface that must be fulfilled by components that |
| 54 | +// implement the translation of specifications to actual time value. |
| 55 | +// |
| 56 | +// The Append method takes the accumulated byte buffer, and the time to |
| 57 | +// use to generate the textual representation. The resulting byte |
| 58 | +// sequence must be returned by this method, normally by using the |
| 59 | +// append() builtin function. |
| 60 | +type Appender interface { |
| 61 | + Append([]byte, time.Time) []byte |
| 62 | +} |
| 63 | + |
| 64 | +// AppendFunc is an utility type to allow users to create a |
| 65 | +// function-only version of an Appender |
| 66 | +type AppendFunc func([]byte, time.Time) []byte |
| 67 | + |
| 68 | +func (af AppendFunc) Append(b []byte, t time.Time) []byte { |
| 69 | + return af(b, t) |
| 70 | +} |
| 71 | + |
| 72 | +type appenderList []Appender |
| 73 | + |
| 74 | +// does the time.Format thing |
| 75 | +type stdlibFormat struct { |
| 76 | + s string |
| 77 | +} |
| 78 | + |
| 79 | +// StdlibFormat returns an Appender that simply goes through `time.Format()` |
| 80 | +// For example, if you know you want to display the abbreviated month name for %b, |
| 81 | +// you can create a StdlibFormat with the pattern `Jan` and register that |
| 82 | +// for specification `b`: |
| 83 | +// |
| 84 | +// a := StdlibFormat(`Jan`) |
| 85 | +// ss := NewSpecificationSet() |
| 86 | +// ss.Set('b', a) // does %b -> abbreviated month name |
| 87 | +func StdlibFormat(s string) Appender { |
| 88 | + return &stdlibFormat{s: s} |
| 89 | +} |
| 90 | + |
| 91 | +func (v stdlibFormat) Append(b []byte, t time.Time) []byte { |
| 92 | + return t.AppendFormat(b, v.s) |
| 93 | +} |
| 94 | + |
| 95 | +func (v stdlibFormat) str() string { |
| 96 | + return v.s |
| 97 | +} |
| 98 | + |
| 99 | +func (v stdlibFormat) canCombine() bool { |
| 100 | + return true |
| 101 | +} |
| 102 | + |
| 103 | +func (v stdlibFormat) combine(w combiner) Appender { |
| 104 | + return StdlibFormat(v.s + w.str()) |
| 105 | +} |
| 106 | + |
| 107 | +type verbatimw struct { |
| 108 | + s string |
| 109 | +} |
| 110 | + |
| 111 | +// Verbatim returns an Appender suitable for generating static text. |
| 112 | +// For static text, this method is slightly favorable than creating |
| 113 | +// your own appender, as adjacent verbatim blocks will be combined |
| 114 | +// at compile time to produce more efficient Appenders |
| 115 | +func Verbatim(s string) Appender { |
| 116 | + return &verbatimw{s: s} |
| 117 | +} |
| 118 | + |
| 119 | +func (v verbatimw) Append(b []byte, _ time.Time) []byte { |
| 120 | + return append(b, v.s...) |
| 121 | +} |
| 122 | + |
| 123 | +func (v verbatimw) canCombine() bool { |
| 124 | + return canCombine(v.s) |
| 125 | +} |
| 126 | + |
| 127 | +func (v verbatimw) combine(w combiner) Appender { |
| 128 | + if _, ok := w.(*stdlibFormat); ok { |
| 129 | + return StdlibFormat(v.s + w.str()) |
| 130 | + } |
| 131 | + return Verbatim(v.s + w.str()) |
| 132 | +} |
| 133 | + |
| 134 | +func (v verbatimw) str() string { |
| 135 | + return v.s |
| 136 | +} |
| 137 | + |
| 138 | +// These words below, as well as any decimal character |
| 139 | +var combineExclusion = []string{ |
| 140 | + "Mon", |
| 141 | + "Monday", |
| 142 | + "Jan", |
| 143 | + "January", |
| 144 | + "MST", |
| 145 | + "PM", |
| 146 | + "pm", |
| 147 | +} |
| 148 | + |
| 149 | +func canCombine(s string) bool { |
| 150 | + if strings.ContainsAny(s, "0123456789") { |
| 151 | + return false |
| 152 | + } |
| 153 | + for _, word := range combineExclusion { |
| 154 | + if strings.Contains(s, word) { |
| 155 | + return false |
| 156 | + } |
| 157 | + } |
| 158 | + return true |
| 159 | +} |
| 160 | + |
| 161 | +type combiner interface { |
| 162 | + canCombine() bool |
| 163 | + combine(combiner) Appender |
| 164 | + str() string |
| 165 | +} |
| 166 | + |
| 167 | +// this is container for the compiler to keep track of appenders, |
| 168 | +// and combine them as we parse and compile the pattern |
| 169 | +type combiningAppend struct { |
| 170 | + list appenderList |
| 171 | + prev Appender |
| 172 | + prevCanCombine bool |
| 173 | +} |
| 174 | + |
| 175 | +func (ca *combiningAppend) Append(w Appender) { |
| 176 | + if ca.prevCanCombine { |
| 177 | + if wc, ok := w.(combiner); ok && wc.canCombine() { |
| 178 | + ca.prev = ca.prev.(combiner).combine(wc) |
| 179 | + ca.list[len(ca.list)-1] = ca.prev |
| 180 | + return |
| 181 | + } |
| 182 | + } |
| 183 | + |
| 184 | + ca.list = append(ca.list, w) |
| 185 | + ca.prev = w |
| 186 | + ca.prevCanCombine = false |
| 187 | + if comb, ok := w.(combiner); ok { |
| 188 | + if comb.canCombine() { |
| 189 | + ca.prevCanCombine = true |
| 190 | + } |
| 191 | + } |
| 192 | +} |
| 193 | + |
| 194 | +func appendCentury(b []byte, t time.Time) []byte { |
| 195 | + n := t.Year() / 100 |
| 196 | + if n < 10 { |
| 197 | + b = append(b, '0') |
| 198 | + } |
| 199 | + return append(b, strconv.Itoa(n)...) |
| 200 | +} |
| 201 | + |
| 202 | +type weekday int |
| 203 | + |
| 204 | +func (v weekday) Append(b []byte, t time.Time) []byte { |
| 205 | + n := int(t.Weekday()) |
| 206 | + if n < int(v) { |
| 207 | + n += 7 |
| 208 | + } |
| 209 | + return append(b, byte(n+48)) |
| 210 | +} |
| 211 | + |
| 212 | +type weeknumberOffset int |
| 213 | + |
| 214 | +func (v weeknumberOffset) Append(b []byte, t time.Time) []byte { |
| 215 | + yd := t.YearDay() |
| 216 | + offset := int(t.Weekday()) - int(v) |
| 217 | + if offset < 0 { |
| 218 | + offset += 7 |
| 219 | + } |
| 220 | + |
| 221 | + if yd < offset { |
| 222 | + return append(b, '0', '0') |
| 223 | + } |
| 224 | + |
| 225 | + n := ((yd - offset) / 7) + 1 |
| 226 | + if n < 10 { |
| 227 | + b = append(b, '0') |
| 228 | + } |
| 229 | + return append(b, strconv.Itoa(n)...) |
| 230 | +} |
| 231 | + |
| 232 | +func appendWeekNumber(b []byte, t time.Time) []byte { |
| 233 | + _, n := t.ISOWeek() |
| 234 | + if n < 10 { |
| 235 | + b = append(b, '0') |
| 236 | + } |
| 237 | + return append(b, strconv.Itoa(n)...) |
| 238 | +} |
| 239 | + |
| 240 | +func appendDayOfYear(b []byte, t time.Time) []byte { |
| 241 | + n := t.YearDay() |
| 242 | + if n < 10 { |
| 243 | + b = append(b, '0', '0') |
| 244 | + } else if n < 100 { |
| 245 | + b = append(b, '0') |
| 246 | + } |
| 247 | + return append(b, strconv.Itoa(n)...) |
| 248 | +} |
| 249 | + |
| 250 | +type hourwblank bool |
| 251 | + |
| 252 | +func (v hourwblank) Append(b []byte, t time.Time) []byte { |
| 253 | + h := t.Hour() |
| 254 | + if bool(v) && h > 12 { |
| 255 | + h = h - 12 |
| 256 | + } |
| 257 | + if h < 10 { |
| 258 | + b = append(b, ' ') |
| 259 | + } |
| 260 | + return append(b, strconv.Itoa(h)...) |
| 261 | +} |
0 commit comments