diff --git a/cmd/zenity/main.go b/cmd/zenity/main.go index 0e14bfe..474ec25 100644 --- a/cmd/zenity/main.go +++ b/cmd/zenity/main.go @@ -18,8 +18,8 @@ import ( "syscall" "time" + "github.com/ncruces/go-strftime" "github.com/ncruces/zenity" - "github.com/ncruces/zenity/internal/strftime" "github.com/ncruces/zenity/internal/zenutil" ) diff --git a/date_darwin.go b/date_darwin.go index 55edf18..7bb8b7f 100644 --- a/date_darwin.go +++ b/date_darwin.go @@ -3,7 +3,7 @@ package zenity import ( "time" - "github.com/ncruces/zenity/internal/strftime" + "github.com/ncruces/go-strftime" "github.com/ncruces/zenity/internal/zenutil" ) diff --git a/date_unix.go b/date_unix.go index 5e0ae42..90d95de 100644 --- a/date_unix.go +++ b/date_unix.go @@ -6,7 +6,7 @@ import ( "strconv" "time" - "github.com/ncruces/zenity/internal/strftime" + "github.com/ncruces/go-strftime" "github.com/ncruces/zenity/internal/zenutil" ) diff --git a/go.mod b/go.mod index a77fc33..d820fd4 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 github.com/josephspurrier/goversioninfo v1.3.0 + github.com/ncruces/go-strftime v0.1.0 github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 go.uber.org/goleak v1.1.11 // test golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d diff --git a/go.sum b/go.sum index 5fbd90e..10717f7 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/josephspurrier/goversioninfo v1.3.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/ncruces/go-strftime v0.1.0 h1:RmHX+0N9AZvUnBk3UGapiv+I2nSyzjCM9HDCHp5H7sM= +github.com/ncruces/go-strftime v0.1.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 h1:GranzK4hv1/pqTIhMTXt2X8MmMOuH3hMeUR0o9SP5yc= diff --git a/internal/strftime/parser.go b/internal/strftime/parser.go deleted file mode 100644 index 4146f7a..0000000 --- a/internal/strftime/parser.go +++ /dev/null @@ -1,70 +0,0 @@ -package strftime - -type parser struct { - fmt string - specs map[byte]string - unpadded map[byte]string - writeLit func(byte) error - writeFmt func(string) error - fallback func(spec byte, pad bool) error -} - -func (p *parser) parse() error { - const ( - initial = iota - specifier - nopadding - ) - - var err error - state := initial - for _, b := range []byte(p.fmt) { - switch state { - default: - if b == '%' { - state = specifier - } else { - err = p.writeLit(b) - } - - case specifier: - if b == '-' { - state = nopadding - continue - } - if fmt, ok := p.specs[b]; ok { - err = p.writeFmt(fmt) - } else { - err = p.fallback(b, true) - } - state = initial - - case nopadding: - if fmt, ok := p.unpadded[b]; ok { - err = p.writeFmt(fmt) - } else if fmt, ok := p.specs[b]; ok { - err = p.writeFmt(fmt) - } else { - err = p.fallback(b, false) - } - state = initial - } - - if err != nil { - return err - } - } - - switch state { - default: - return nil - case specifier: - return p.writeLit('%') - case nopadding: - err := p.writeLit('%') - if err != nil { - return err - } - return p.writeLit('-') - } -} diff --git a/internal/strftime/specifiers.go b/internal/strftime/specifiers.go deleted file mode 100644 index de1896b..0000000 --- a/internal/strftime/specifiers.go +++ /dev/null @@ -1,137 +0,0 @@ -package strftime - -import ( - "strconv" - "time" -) - -func (p *parser) goSpecifiers() { - // https://strftime.org/ - p.specs = map[byte]string{ - 'B': "January", - 'b': "Jan", - 'h': "Jan", - 'm': "01", - 'A': "Monday", - 'a': "Mon", - 'e': "_2", - 'd': "02", - 'j': "002", - 'H': "15", - 'I': "03", - 'M': "04", - 'S': "05", - 'Y': "2006", - 'y': "06", - 'p': "PM", - 'P': "pm", - 'Z': "MST", - 'z': "-0700", - 'L': "000", - 'f': "000000", - 'N': "000000000", - - '+': "Mon Jan _2 15:04:05 MST 2006", - 'c': "Mon Jan _2 15:04:05 2006", - 'v': "_2-Jan-2006", - 'F': "2006-01-02", - 'D': "01/02/06", - 'x': "01/02/06", - 'r': "03:04:05 PM", - 'T': "15:04:05", - 'X': "15:04:05", - 'R': "15:04", - - '%': "%", - 't': "\t", - 'n': "\n", - } - - p.unpadded = map[byte]string{ - 'm': "1", - 'd': "2", - 'I': "3", - 'M': "4", - 'S': "5", - } -} - -func (p *parser) uts35Specifiers() { - // https://nsdateformatter.com/ - p.specs = map[byte]string{ - 'B': "MMMM", - 'b': "MMM", - 'h': "MMM", - 'm': "MM", - 'A': "EEEE", - 'a': "E", - 'd': "dd", - 'j': "DDD", - 'H': "HH", - 'I': "hh", - 'M': "mm", - 'S': "ss", - 'Y': "yyyy", - 'y': "yy", - 'G': "YYYY", - 'g': "YY", - 'V': "ww", - 'p': "a", - 'Z': "zzz", - 'z': "Z", - 'L': "SSS", - 'f': "SSSSSS", - 'N': "SSSSSSSSS", - - '+': "E MMM d HH:mm:ss zzz yyyy", - 'c': "E MMM d HH:mm:ss yyyy", - 'v': "d-MMM-yyyy", - 'F': "yyyy-MM-dd", - 'D': "MM/dd/yy", - 'x': "MM/dd/yy", - 'r': "hh:mm:ss a", - 'T': "HH:mm:ss", - 'X': "HH:mm:ss", - 'R': "HH:mm", - - '%': "%", - 't': "\t", - 'n': "\n", - } - - p.unpadded = map[byte]string{ - 'm': "M", - 'd': "d", - 'j': "D", - 'H': "H", - 'I': "h", - 'M': "m", - 'S': "s", - } -} - -func weekNumber(t time.Time, pad, monday bool) string { - day := t.YearDay() - offset := int(t.Weekday()) - if monday { - if offset == 0 { - offset = 6 - } else { - offset-- - } - } - - if day < offset { - if pad { - return "00" - } else { - return "0" - } - } - - n := (day-offset)/7 + 1 - if n < 10 && pad { - return "0" + strconv.Itoa(n) - } - return strconv.Itoa(n) -} diff --git a/internal/strftime/strftime.go b/internal/strftime/strftime.go deleted file mode 100644 index 6df9c7f..0000000 --- a/internal/strftime/strftime.go +++ /dev/null @@ -1,195 +0,0 @@ -package strftime - -import ( - "errors" - "strconv" - "strings" - "time" -) - -func Format(fmt string, t time.Time) string { - var res strings.Builder - - var parser parser - parser.fmt = fmt - parser.goSpecifiers() - - parser.writeLit = res.WriteByte - - parser.writeFmt = func(fmt string) error { - switch fmt { - case "000", "000000", "000000000": - res.WriteString(t.Format("." + fmt)[1:]) - default: - res.WriteString(t.Format(fmt)) - } - return nil - } - - parser.fallback = func(spec byte, pad bool) error { - switch spec { - default: - res.WriteByte('%') - if !pad { - res.WriteByte('-') - } - res.WriteByte(spec) - case 'C': - s := t.Format("2006") - res.WriteString(s[:len(s)-2]) - case 'g': - y, _ := t.ISOWeek() - res.WriteString(time.Date(y, 1, 1, 0, 0, 0, 0, time.UTC).Format("06")) - case 'G': - y, _ := t.ISOWeek() - res.WriteString(time.Date(y, 1, 1, 0, 0, 0, 0, time.UTC).Format("2006")) - case 'V': - _, w := t.ISOWeek() - if w < 10 && pad { - res.WriteByte('0') - } - res.WriteString(strconv.Itoa(w)) - case 'W': - res.WriteString(weekNumber(t, pad, true)) - case 'U': - res.WriteString(weekNumber(t, pad, false)) - case 'w': - w := int(t.Weekday()) - res.WriteString(strconv.Itoa(w)) - case 'u': - if w := int(t.Weekday()); w == 0 { - res.WriteByte('7') - } else { - res.WriteString(strconv.Itoa(w)) - } - case 'k': - h := t.Hour() - if h < 10 { - res.WriteByte(' ') - } - res.WriteString(strconv.Itoa(h)) - case 'l': - h := t.Hour() - if h == 0 { - h = 12 - } else if h > 12 { - h -= 12 - } - if h < 10 { - res.WriteByte(' ') - } - res.WriteString(strconv.Itoa(h)) - case 's': - res.WriteString(strconv.FormatInt(t.Unix(), 10)) - } - return nil - } - - parser.parse() - return res.String() -} - -func Parse(fmt, value string) (time.Time, error) { - layout, err := Layout(fmt) - if err != nil { - return time.Time{}, err - } - return time.Parse(layout, value) -} - -func Layout(fmt string) (string, error) { - var res strings.Builder - - var parser parser - parser.fmt = fmt - parser.goSpecifiers() - - parser.writeLit = func(b byte) error { - if '0' <= b && b <= '9' { - return errors.New("strftime: unsupported literal digit: '" + string(b) + "'") - } - res.WriteByte(b) - if b == 'M' || b == 'T' || b == 'm' || b == 'n' { - cur := res.String() - switch { - case strings.HasSuffix(cur, "Jan"): - return errors.New("strftime: unsupported literal: 'Jan'") - case strings.HasSuffix(cur, "Mon"): - return errors.New("strftime: unsupported literal: 'Mon'") - case strings.HasSuffix(cur, "MST"): - return errors.New("strftime: unsupported literal: 'MST'") - case strings.HasSuffix(cur, "PM"): - return errors.New("strftime: unsupported literal: 'PM'") - case strings.HasSuffix(cur, "pm"): - return errors.New("strftime: unsupported literal: 'pm'") - } - } - return nil - } - - parser.writeFmt = func(fmt string) error { - switch fmt { - case "000", "000000", "000000000": - if cur := res.String(); !(strings.HasSuffix(cur, ".") || strings.HasSuffix(cur, ",")) { - return errors.New("strftime: unsupported specifier: fractional seconds must follow '.' or ','") - } - } - res.WriteString(fmt) - return nil - } - - parser.fallback = func(spec byte, pad bool) error { - return errors.New("strftime: unsupported specifier: %" + string(spec)) - } - - if err := parser.parse(); err != nil { - return "", err - } - - parser.writeFmt("") - return res.String(), nil -} - -func UTS35(fmt string) (string, error) { - var parser parser - parser.fmt = fmt - parser.uts35Specifiers() - - const quote = '\'' - var literal bool - var res strings.Builder - - parser.writeLit = func(b byte) error { - if b == quote { - res.WriteByte(quote) - res.WriteByte(quote) - return nil - } - if !literal && ('a' <= b && b <= 'z' || 'A' <= b && b <= 'Z') { - literal = true - res.WriteByte(quote) - } - res.WriteByte(b) - return nil - } - - parser.writeFmt = func(fmt string) error { - if literal { - literal = false - res.WriteByte(quote) - } - res.WriteString(fmt) - return nil - } - - parser.fallback = func(spec byte, pad bool) error { - return errors.New("strftime: unsupported specifier: %" + string(spec)) - } - - if err := parser.parse(); err != nil { - return "", err - } - - parser.writeFmt("") - return res.String(), nil -} diff --git a/internal/strftime/strftime_test.go b/internal/strftime/strftime_test.go deleted file mode 100644 index 8a78464..0000000 --- a/internal/strftime/strftime_test.go +++ /dev/null @@ -1,378 +0,0 @@ -package strftime - -import ( - "net/http" - "testing" - "time" -) - -var reference = time.Date(2009, 8, 7, 6, 5, 4, 300000000, time.UTC) - -var timeTests = []struct { - format string - layout string - uts35 string - time string -}{ - // Date and time formats - {"%c", time.ANSIC, "E MMM d HH:mm:ss yyyy", "Fri Aug 7 06:05:04 2009"}, - {"%+", time.UnixDate, "E MMM d HH:mm:ss zzz yyyy", "Fri Aug 7 06:05:04 UTC 2009"}, - {"%FT%TZ", time.RFC3339[:20], "yyyy-MM-dd'T'HH:mm:ss'Z'", "2009-08-07T06:05:04Z"}, - {"%a %b %e %T %Y", time.ANSIC, "", "Fri Aug 7 06:05:04 2009"}, - {"%a %b %e %T %Z %Y", time.UnixDate, "", "Fri Aug 7 06:05:04 UTC 2009"}, - {"%a %b %d %T %z %Y", time.RubyDate, "E MMM dd HH:mm:ss Z yyyy", "Fri Aug 07 06:05:04 +0000 2009"}, - {"%a, %d %b %Y %T %Z", time.RFC1123, "E, dd MMM yyyy HH:mm:ss zzz", "Fri, 07 Aug 2009 06:05:04 UTC"}, - {"%a, %d %b %Y %T GMT", http.TimeFormat, "E, dd MMM yyyy HH:mm:ss 'GMT'", "Fri, 07 Aug 2009 06:05:04 GMT"}, - {"%Y-%m-%dT%H:%M:%SZ", time.RFC3339[:20], "yyyy-MM-dd'T'HH:mm:ss'Z'", "2009-08-07T06:05:04Z"}, - // Date formats - {"%v", "_2-Jan-2006", "d-MMM-yyyy", " 7-Aug-2009"}, - {"%F", "2006-01-02", "yyyy-MM-dd", "2009-08-07"}, - {"%D", "01/02/06", "MM/dd/yy", "08/07/09"}, - {"%x", "01/02/06", "MM/dd/yy", "08/07/09"}, - {"%e-%b-%Y", "_2-Jan-2006", "", " 7-Aug-2009"}, - {"%Y-%m-%d", "2006-01-02", "yyyy-MM-dd", "2009-08-07"}, - {"%m/%d/%y", "01/02/06", "MM/dd/yy", "08/07/09"}, - // Time formats - {"%R", "15:04", "HH:mm", "06:05"}, - {"%T", "15:04:05", "HH:mm:ss", "06:05:04"}, - {"%X", "15:04:05", "HH:mm:ss", "06:05:04"}, - {"%r", "03:04:05 PM", "hh:mm:ss a", "06:05:04 AM"}, - {"%H:%M", "15:04", "HH:mm", "06:05"}, - {"%H:%M:%S", "15:04:05", "HH:mm:ss", "06:05:04"}, - {"%I:%M:%S %p", "03:04:05 PM", "hh:mm:ss a", "06:05:04 AM"}, - // Misc - {"%g", "", "YY", "09"}, - {"%V/%G", "", "ww/YYYY", "32/2009"}, - {"%Cth Century Fox", "", "", "20th Century Fox"}, - {"%-I o'clock", "3 o'clock", "h 'o''clock'", "6 o'clock"}, - {"%-A, the %uth day of the week", "", "", "Friday, the 5th day of the week"}, - {"%Nns since %T", "", "SSSSSSSSS'ns since 'HH:mm:ss", "300000000ns since 06:05:04"}, - {"%-S.%Ls since %R", "5.000s since 15:04", "s.SSS's since 'HH:mm", "4.300s since 06:05"}, - // Parsing - {"", "", "", ""}, - {"%", "%", "%", "%"}, - {"%-", "%-", "%-", "%-"}, - {"%n", "\n", "\n", "\n"}, - {"%t", "\t", "\t", "\t"}, - {"%q", "", "", "%q"}, - {"%-q", "", "", "%-q"}, - {"'", "'", "''", "'"}, - {"100%", "", "100%", "100%"}, - {"Monday", "", "'Monday'", "Monday"}, - {"January", "", "'January'", "January"}, - {"MST", "", "'MST'", "MST"}, - {"AM", "AM", "'AM'", "AM"}, - {"am", "am", "'am'", "am"}, - {"PM", "", "'PM'", "PM"}, - {"pm", "", "'pm'", "pm"}, -} - -func TestFormat(t *testing.T) { - for _, test := range timeTests { - if got := Format(test.format, reference); got != test.time { - t.Errorf("Format(%q) = %q, want %q", test.format, got, test.time) - } - } -} - -func TestLayout(t *testing.T) { - for _, test := range timeTests { - if got, err := Layout(test.format); err != nil && test.layout != "" { - t.Errorf("Layout(%q) = %v", test.format, err) - } else if got != test.layout { - t.Errorf("Layout(%q) = %q, want %q", test.format, got, test.layout) - } - } -} - -func TestUTS35(t *testing.T) { - for _, test := range timeTests { - if got, err := UTS35(test.format); err != nil && test.uts35 != "" { - t.Errorf("UTS35(%q) = %v", test.format, err) - } else if got != test.uts35 { - t.Errorf("UTS35(%q) = %q, want %q", test.format, got, test.uts35) - } - } -} - -func TestFormat_ruby(t *testing.T) { - // https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-i-strftime - reference := time.Date(2007, 11, 19, 8, 37, 48, 0, time.FixedZone("", -6*3600)) - tests := []struct { - format string - time string - }{ - {"Printed on %m/%d/%Y", "Printed on 11/19/2007"}, - {"at %I:%M%p", "at 08:37AM"}, - // Various ISO 8601 formats: - {"%Y%m%d", "20071119"}, // Calendar date (basic) - {"%F", "2007-11-19"}, // Calendar date (extended) - {"%Y-%m", "2007-11"}, // Calendar date, reduced accuracy, specific month - {"%Y", "2007"}, // Calendar date, reduced accuracy, specific year - {"%C", "20"}, // Calendar date, reduced accuracy, specific century - {"%Y%j", "2007323"}, // Ordinal date (basic) - {"%Y-%j", "2007-323"}, // Ordinal date (extended) - {"%GW%V%u", "2007W471"}, // Week date (basic) - {"%G-W%V-%u", "2007-W47-1"}, // Week date (extended) - {"%GW%V", "2007W47"}, // Week date, reduced accuracy, specific week (basic) - {"%G-W%V", "2007-W47"}, // Week date, reduced accuracy, specific week (extended) - {"%H%M%S", "083748"}, // Local time (basic) - {"%T", "08:37:48"}, // Local time (extended) - {"%H%M", "0837"}, // Local time, reduced accuracy, specific minute (basic) - {"%H:%M", "08:37"}, // Local time, reduced accuracy, specific minute (extended) - {"%H", "08"}, // Local time, reduced accuracy, specific hour - {"%H%M%S,%L", "083748,000"}, // Local time with decimal fraction, comma as decimal sign (basic) - {"%T,%L", "08:37:48,000"}, // Local time with decimal fraction, comma as decimal sign (extended) - {"%H%M%S.%L", "083748.000"}, // Local time with decimal fraction, full stop as decimal sign (basic) - {"%T.%L", "08:37:48.000"}, // Local time with decimal fraction, full stop as decimal sign (extended) - {"%H%M%S%z", "083748-0600"}, // Local time and the difference from UTC (basic) - {"%Y%m%dT%H%M%S%z", "20071119T083748-0600"}, // Date and time of day for calendar date (basic) - {"%Y%jT%H%M%S%z", "2007323T083748-0600"}, // Date and time of day for ordinal date (basic) - {"%GW%V%uT%H%M%S%z", "2007W471T083748-0600"}, // Date and time of day for week date (basic) - {"%Y%m%dT%H%M", "20071119T0837"}, // Calendar date and local time (basic) - {"%FT%R", "2007-11-19T08:37"}, // Calendar date and local time (extended) - {"%Y%jT%H%MZ", "2007323T0837Z"}, // Ordinal date and UTC of day (basic) - {"%Y-%jT%RZ", "2007-323T08:37Z"}, // Ordinal date and UTC of day (extended) - {"%GW%V%uT%H%M%z", "2007W471T0837-0600"}, // Week date and local time and difference from UTC (basic) - // {"%T%:z", "08:37:48-06:00"}, // Local time and the difference from UTC (extended) - // {"%FT%T%:z", "2007-11-19T08:37:48-06:00"}, // Date and time of day for calendar date (extended) - // {"%Y-%jT%T%:z", "2007-323T08:37:48-06:00"}, // Date and time of day for ordinal date (extended) - // {"%G-W%V-%uT%T%:z", "2007-W47-1T08:37:48-06:00"}, // Date and time of day for week date (extended) - // {"%G-W%V-%uT%R%:z", "2007-W47-1T08:37-06:00"}, // Week date and local time and difference from - } - - for _, test := range tests { - if got := Format(test.format, reference); got != test.time { - t.Errorf("Format(%q) = %q, want %q", test.format, got, test.time) - } - } -} - -func TestFormat_tebeka(t *testing.T) { - // github.com/tebeka/strftime - // github.com/hhkbp2/go-strftime - reference := time.Date(2009, time.November, 10, 23, 1, 2, 3, time.UTC) - tests := []struct { - format string - time string - }{ - {"%a", "Tue"}, - {"%A", "Tuesday"}, - {"%b", "Nov"}, - {"%B", "November"}, - {"%c", "Tue Nov 10 23:01:02 2009"}, // we use a different format - {"%d", "10"}, - {"%H", "23"}, - {"%I", "11"}, - {"%j", "314"}, - {"%m", "11"}, - {"%M", "01"}, - {"%p", "PM"}, - {"%S", "02"}, - {"%U", "45"}, - {"%w", "2"}, - {"%W", "45"}, - {"%x", "11/10/09"}, - {"%X", "23:01:02"}, - {"%y", "09"}, - {"%Y", "2009"}, - {"%Z", "UTC"}, - {"%L", "000"}, // we use a different specifier - {"%f", "000000"}, // we use a different specifier - {"%N", "000000003"}, // we use a different specifier - - // Escape - {"%%%Y", "%2009"}, - {"%3%%", "%3%"}, - {"%3%L", "%3000"}, // we use a different specifier - {"%3xy%L", "%3xy000"}, // we use a different specifier - - // Embedded - {"/path/%Y/%m/report", "/path/2009/11/report"}, - - // Empty - {"", ""}, - } - - for _, test := range tests { - if got := Format(test.format, reference); got != test.time { - t.Errorf("Format(%q) = %q, want %q", test.format, got, test.time) - } - } -} - -func TestFormat_fastly(t *testing.T) { - // github.com/fastly/go-utils/strftime - timezone, err := time.LoadLocation("MST") - if err != nil { - t.Skip("could not load timezone:", err) - } - - reference := time.Unix(1136239445, 0).In(timezone) - - tests := []struct { - format string - time string - }{ - {"", ``}, - - // invalid formats - {"%", `%`}, - {"%^", `%^`}, - {"%^ ", `%^ `}, - {"%^ x", `%^ x`}, - {"x%^ x", `x%^ x`}, - - // supported locale-invariant formats - {"%a", `Mon`}, - {"%A", `Monday`}, - {"%b", `Jan`}, - {"%B", `January`}, - {"%C", `20`}, - {"%d", `02`}, - {"%D", `01/02/06`}, - {"%e", ` 2`}, - {"%F", `2006-01-02`}, - {"%G", `2006`}, - {"%g", `06`}, - {"%h", `Jan`}, - {"%H", `15`}, - {"%I", `03`}, - {"%j", `002`}, - {"%k", `15`}, - {"%l", ` 3`}, - {"%m", `01`}, - {"%M", `04`}, - {"%n", "\n"}, - {"%p", `PM`}, - {"%r", `03:04:05 PM`}, - {"%R", `15:04`}, - {"%s", `1136239445`}, - {"%S", `05`}, - {"%t", "\t"}, - {"%T", `15:04:05`}, - {"%u", `1`}, - {"%U", `01`}, - {"%V", `01`}, - {"%w", `1`}, - {"%W", `01`}, - {"%x", `01/02/06`}, - {"%X", `15:04:05`}, - {"%y", `06`}, - {"%Y", `2006`}, - {"%z", `-0700`}, - {"%Z", `MST`}, - {"%%", `%`}, - - // supported locale-varying formats - {"%c", `Mon Jan 2 15:04:05 2006`}, - {"%E", `%E`}, - {"%EF", `%EF`}, - {"%O", `%O`}, - {"%OF", `%OF`}, - {"%P", `pm`}, - {"%+", `Mon Jan 2 15:04:05 MST 2006`}, - { - "%a|%A|%b|%B|%c|%C|%d|%D|%e|%E|%EF|%F|%G|%g|%h|%H|%I|%j|%k|%l|%m|%M|%O|%OF|%p|%P|%r|%R|%s|%S|%t|%T|%u|%U|%V|%w|%W|%x|%X|%y|%Y|%z|%Z|%%", - `Mon|Monday|Jan|January|Mon Jan 2 15:04:05 2006|20|02|01/02/06| 2|%E|%EF|2006-01-02|2006|06|Jan|15|03|002|15| 3|01|04|%O|%OF|PM|pm|03:04:05 PM|15:04|1136239445|05| |15:04:05|1|01|01|1|01|01/02/06|15:04:05|06|2006|-0700|MST|%`, - }, - } - - for _, test := range tests { - if got := Format(test.format, reference); got != test.time { - t.Errorf("Format(%q) = %q, want %q", test.format, got, test.time) - } - } -} - -func TestFormat_jehiah(t *testing.T) { - // github.com/jehiah/go-strftime - reference := time.Unix(1340244776, 0).UTC() - tests := []struct { - format string - time string - }{ - {"%Y-%m-%d %H:%M:%S", "2012-06-21 02:12:56"}, - {"aaabbb0123456789%Y", "aaabbb01234567892012"}, - {"%H:%M:%S.%L", "02:12:56.000"}, // jehiah disagrees with Ruby on this one - {"%0%1%%%2", "%0%1%%2"}, - } - - for _, test := range tests { - if got := Format(test.format, reference); got != test.time { - t.Errorf("Format(%q) = %q, want %q", test.format, got, test.time) - } - } -} - -func TestFormat_lestrrat(t *testing.T) { - // github.com/lestrrat-go/strftime - reference := time.Unix(1136239445, 123456789).UTC() - tests := []struct { - format string - time string - }{ - { - `%A %a %B %b %C %c %D %d %e %F %H %h %I %j %k %l %M %m %n %p %R %r %S %T %t %U %u %V %v %W %w %X %x %Y %y %Z %z`, - "Monday Mon January Jan 20 Mon Jan 2 22:04:05 2006 01/02/06 02 2 2006-01-02 22 Jan 10 002 22 10 04 01 \n PM 22:04 10:04:05 PM 05 22:04:05 \t 01 1 01 2-Jan-2006 01 1 22:04:05 01/02/06 2006 06 UTC +0000", - }, - } - - for _, test := range tests { - if got := Format(test.format, reference); got != test.time { - t.Errorf("Format(%q) = %q, want %q", test.format, got, test.time) - } - } -} - -func FuzzFormat(f *testing.F) { - for _, test := range timeTests { - f.Add(test.format) - } - - f.Fuzz(func(t *testing.T, fmt string) { - s := Format(fmt, reference) - if s == "" && fmt != "" { - t.Errorf("Format(%q) = %q", fmt, s) - } - }) -} - -func FuzzParse(f *testing.F) { - for _, test := range timeTests { - f.Add(test.format, Format(test.format, reference)) - } - - f.Fuzz(func(t *testing.T, format, value string) { - tm, err := Parse(format, value) - if tm.IsZero() && err == nil { - t.Errorf("Parse(%q, %q) = (%v, %v)", format, value, tm, err) - } - }) -} - -func FuzzLayout(f *testing.F) { - for _, test := range timeTests { - f.Add(test.format) - } - - f.Fuzz(func(t *testing.T, format string) { - layout, err := Layout(format) - if format != "" && layout == "" && err == nil { - t.Errorf("Layout(%q) = (%q, %v)", format, layout, err) - } - }) -} - -func FuzzUTS35(f *testing.F) { - for _, test := range timeTests { - f.Add(test.format) - } - - f.Fuzz(func(t *testing.T, format string) { - pattern, err := UTS35(format) - if format != "" && pattern == "" && err == nil { - t.Errorf("UTS35(%q) = (%q, %v)", format, pattern, err) - } - }) -}