-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from lestrrat-go/topic/configurable-patterns
Configurable Specifications
- Loading branch information
Showing
11 changed files
with
727 additions
and
380 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
language: go | ||
sudo: false | ||
go: | ||
- 1.7.x | ||
- tip | ||
- 1.13.x | ||
- tip |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
bench: | ||
go test -tags bench -benchmem -bench . | ||
@git checkout go.mod | ||
@rm go.sum |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
package strftime | ||
|
||
import ( | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
// These are all of the standard, POSIX compliant specifications. | ||
// Extensions should be in extensions.go | ||
var ( | ||
fullWeekDayName = StdlibFormat("Monday") | ||
abbrvWeekDayName = StdlibFormat("Mon") | ||
fullMonthName = StdlibFormat("January") | ||
abbrvMonthName = StdlibFormat("Jan") | ||
centuryDecimal = AppendFunc(appendCentury) | ||
timeAndDate = StdlibFormat("Mon Jan _2 15:04:05 2006") | ||
mdy = StdlibFormat("01/02/06") | ||
dayOfMonthZeroPad = StdlibFormat("02") | ||
dayOfMonthSpacePad = StdlibFormat("_2") | ||
ymd = StdlibFormat("2006-01-02") | ||
twentyFourHourClockZeroPad = StdlibFormat("15") | ||
twelveHourClockZeroPad = StdlibFormat("3") | ||
dayOfYear = AppendFunc(appendDayOfYear) | ||
twentyFourHourClockSpacePad = hourwblank(false) | ||
twelveHourClockSpacePad = hourwblank(true) | ||
minutesZeroPad = StdlibFormat("04") | ||
monthNumberZeroPad = StdlibFormat("01") | ||
newline = Verbatim("\n") | ||
ampm = StdlibFormat("PM") | ||
hm = StdlibFormat("15:04") | ||
imsp = StdlibFormat("3:04:05 PM") | ||
secondsNumberZeroPad = StdlibFormat("05") | ||
hms = StdlibFormat("15:04:05") | ||
tab = Verbatim("\t") | ||
weekNumberSundayOrigin = weeknumberOffset(0) // week number of the year, Sunday first | ||
weekdayMondayOrigin = weekday(1) | ||
// monday as the first day, and 01 as the first value | ||
weekNumberMondayOriginOneOrigin = AppendFunc(appendWeekNumber) | ||
eby = StdlibFormat("_2-Jan-2006") | ||
// monday as the first day, and 00 as the first value | ||
weekNumberMondayOrigin = weeknumberOffset(1) // week number of the year, Monday first | ||
weekdaySundayOrigin = weekday(0) | ||
natReprTime = StdlibFormat("15:04:05") // national representation of the time XXX is this correct? | ||
natReprDate = StdlibFormat("01/02/06") // national representation of the date XXX is this correct? | ||
year = StdlibFormat("2006") // year with century | ||
yearNoCentury = StdlibFormat("06") // year w/o century | ||
timezone = StdlibFormat("MST") // time zone name | ||
timezoneOffset = StdlibFormat("-0700") // time zone ofset from UTC | ||
percent = Verbatim("%") | ||
) | ||
|
||
// Appender is the interface that must be fulfilled by components that | ||
// implement the translation of specifications to actual time value. | ||
// | ||
// The Append method takes the accumulated byte buffer, and the time to | ||
// use to generate the textual representation. The resulting byte | ||
// sequence must be returned by this method, normally by using the | ||
// append() builtin function. | ||
type Appender interface { | ||
Append([]byte, time.Time) []byte | ||
} | ||
|
||
// AppendFunc is an utility type to allow users to create a | ||
// function-only version of an Appender | ||
type AppendFunc func([]byte, time.Time) []byte | ||
|
||
func (af AppendFunc) Append(b []byte, t time.Time) []byte { | ||
return af(b, t) | ||
} | ||
|
||
type appenderList []Appender | ||
|
||
// does the time.Format thing | ||
type stdlibFormat struct { | ||
s string | ||
} | ||
|
||
// StdlibFormat returns an Appender that simply goes through `time.Format()` | ||
// For example, if you know you want to display the abbreviated month name for %b, | ||
// you can create a StdlibFormat with the pattern `Jan` and register that | ||
// for specification `b`: | ||
// | ||
// a := StdlibFormat(`Jan`) | ||
// ss := NewSpecificationSet() | ||
// ss.Set('b', a) // does %b -> abbreviated month name | ||
func StdlibFormat(s string) Appender { | ||
return &stdlibFormat{s: s} | ||
} | ||
|
||
func (v stdlibFormat) Append(b []byte, t time.Time) []byte { | ||
return t.AppendFormat(b, v.s) | ||
} | ||
|
||
func (v stdlibFormat) str() string { | ||
return v.s | ||
} | ||
|
||
func (v stdlibFormat) canCombine() bool { | ||
return true | ||
} | ||
|
||
func (v stdlibFormat) combine(w combiner) Appender { | ||
return StdlibFormat(v.s + w.str()) | ||
} | ||
|
||
type verbatimw struct { | ||
s string | ||
} | ||
|
||
// Verbatim returns an Appender suitable for generating static text. | ||
// For static text, this method is slightly favorable than creating | ||
// your own appender, as adjacent verbatim blocks will be combined | ||
// at compile time to produce more efficient Appenders | ||
func Verbatim(s string) Appender { | ||
return &verbatimw{s: s} | ||
} | ||
|
||
func (v verbatimw) Append(b []byte, _ time.Time) []byte { | ||
return append(b, v.s...) | ||
} | ||
|
||
func (v verbatimw) canCombine() bool { | ||
return canCombine(v.s) | ||
} | ||
|
||
func (v verbatimw) combine(w combiner) Appender { | ||
if _, ok := w.(*stdlibFormat); ok { | ||
return StdlibFormat(v.s + w.str()) | ||
} | ||
return Verbatim(v.s + w.str()) | ||
} | ||
|
||
func (v verbatimw) str() string { | ||
return v.s | ||
} | ||
|
||
// These words below, as well as any decimal character | ||
var combineExclusion = []string{ | ||
"Mon", | ||
"Monday", | ||
"Jan", | ||
"January", | ||
"MST", | ||
"PM", | ||
"pm", | ||
} | ||
|
||
func canCombine(s string) bool { | ||
if strings.ContainsAny(s, "0123456789") { | ||
return false | ||
} | ||
for _, word := range combineExclusion { | ||
if strings.Contains(s, word) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
type combiner interface { | ||
canCombine() bool | ||
combine(combiner) Appender | ||
str() string | ||
} | ||
|
||
// this is container for the compiler to keep track of appenders, | ||
// and combine them as we parse and compile the pattern | ||
type combiningAppend struct { | ||
list appenderList | ||
prev Appender | ||
prevCanCombine bool | ||
} | ||
|
||
func (ca *combiningAppend) Append(w Appender) { | ||
if ca.prevCanCombine { | ||
if wc, ok := w.(combiner); ok && wc.canCombine() { | ||
ca.prev = ca.prev.(combiner).combine(wc) | ||
ca.list[len(ca.list)-1] = ca.prev | ||
return | ||
} | ||
} | ||
|
||
ca.list = append(ca.list, w) | ||
ca.prev = w | ||
ca.prevCanCombine = false | ||
if comb, ok := w.(combiner); ok { | ||
if comb.canCombine() { | ||
ca.prevCanCombine = true | ||
} | ||
} | ||
} | ||
|
||
func appendCentury(b []byte, t time.Time) []byte { | ||
n := t.Year() / 100 | ||
if n < 10 { | ||
b = append(b, '0') | ||
} | ||
return append(b, strconv.Itoa(n)...) | ||
} | ||
|
||
type weekday int | ||
|
||
func (v weekday) Append(b []byte, t time.Time) []byte { | ||
n := int(t.Weekday()) | ||
if n < int(v) { | ||
n += 7 | ||
} | ||
return append(b, byte(n+48)) | ||
} | ||
|
||
type weeknumberOffset int | ||
|
||
func (v weeknumberOffset) Append(b []byte, t time.Time) []byte { | ||
yd := t.YearDay() | ||
offset := int(t.Weekday()) - int(v) | ||
if offset < 0 { | ||
offset += 7 | ||
} | ||
|
||
if yd < offset { | ||
return append(b, '0', '0') | ||
} | ||
|
||
n := ((yd - offset) / 7) + 1 | ||
if n < 10 { | ||
b = append(b, '0') | ||
} | ||
return append(b, strconv.Itoa(n)...) | ||
} | ||
|
||
func appendWeekNumber(b []byte, t time.Time) []byte { | ||
_, n := t.ISOWeek() | ||
if n < 10 { | ||
b = append(b, '0') | ||
} | ||
return append(b, strconv.Itoa(n)...) | ||
} | ||
|
||
func appendDayOfYear(b []byte, t time.Time) []byte { | ||
n := t.YearDay() | ||
if n < 10 { | ||
b = append(b, '0', '0') | ||
} else if n < 100 { | ||
b = append(b, '0') | ||
} | ||
return append(b, strconv.Itoa(n)...) | ||
} | ||
|
||
type hourwblank bool | ||
|
||
func (v hourwblank) Append(b []byte, t time.Time) []byte { | ||
h := t.Hour() | ||
if bool(v) && h > 12 { | ||
h = h - 12 | ||
} | ||
if h < 10 { | ||
b = append(b, ' ') | ||
} | ||
return append(b, strconv.Itoa(h)...) | ||
} |
Oops, something went wrong.