Tests.
This commit is contained in:
parent
3d0e153dd0
commit
fbc65bce31
4 changed files with 196 additions and 11 deletions
|
@ -56,10 +56,15 @@ func (p *parser) parse() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch state {
|
switch state {
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
case specifier:
|
case specifier:
|
||||||
return p.writeLit('%')
|
return p.writeLit('%')
|
||||||
case nopadding:
|
case nopadding:
|
||||||
|
err := p.writeLit('%')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return p.writeLit('-')
|
return p.writeLit('-')
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package strftime
|
package strftime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func (p *parser) goSpecifiers() {
|
func (p *parser) goSpecifiers() {
|
||||||
// https://strftime.org/
|
// https://strftime.org/
|
||||||
p.specs = map[byte]string{
|
p.specs = map[byte]string{
|
||||||
|
@ -28,6 +33,7 @@ func (p *parser) goSpecifiers() {
|
||||||
|
|
||||||
'+': "Mon Jan _2 15:04:05 MST 2006",
|
'+': "Mon Jan _2 15:04:05 MST 2006",
|
||||||
'c': "Mon Jan _2 15:04:05 2006",
|
'c': "Mon Jan _2 15:04:05 2006",
|
||||||
|
'v': "_2-Jan-2006",
|
||||||
'F': "2006-01-02",
|
'F': "2006-01-02",
|
||||||
'D': "01/02/06",
|
'D': "01/02/06",
|
||||||
'x': "01/02/06",
|
'x': "01/02/06",
|
||||||
|
@ -50,7 +56,8 @@ func (p *parser) goSpecifiers() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) uts35Specifiers() { // https://nsdateformatter.com/
|
func (p *parser) uts35Specifiers() {
|
||||||
|
// https://nsdateformatter.com/
|
||||||
p.specs = map[byte]string{
|
p.specs = map[byte]string{
|
||||||
'B': "MMMM",
|
'B': "MMMM",
|
||||||
'b': "MMM",
|
'b': "MMM",
|
||||||
|
@ -78,6 +85,7 @@ func (p *parser) uts35Specifiers() { // https://nsdateformatter.com/
|
||||||
|
|
||||||
'+': "E MMM d HH:mm:ss zzz yyyy",
|
'+': "E MMM d HH:mm:ss zzz yyyy",
|
||||||
'c': "E MMM d HH:mm:ss yyyy",
|
'c': "E MMM d HH:mm:ss yyyy",
|
||||||
|
'v': "d-MMM-yyyy",
|
||||||
'F': "yyyy-MM-dd",
|
'F': "yyyy-MM-dd",
|
||||||
'D': "MM/dd/yy",
|
'D': "MM/dd/yy",
|
||||||
'x': "MM/dd/yy",
|
'x': "MM/dd/yy",
|
||||||
|
@ -101,3 +109,29 @@ func (p *parser) uts35Specifiers() { // https://nsdateformatter.com/
|
||||||
'S': "s",
|
'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)
|
||||||
|
}
|
||||||
|
|
|
@ -29,7 +29,11 @@ func Format(fmt string, t time.Time) string {
|
||||||
parser.fallback = func(spec byte, pad bool) error {
|
parser.fallback = func(spec byte, pad bool) error {
|
||||||
switch spec {
|
switch spec {
|
||||||
default:
|
default:
|
||||||
return errors.New("strftime: unsupported specifier: %" + string(spec))
|
res.WriteByte('%')
|
||||||
|
if !pad {
|
||||||
|
res.WriteByte('-')
|
||||||
|
}
|
||||||
|
res.WriteByte(spec)
|
||||||
case 'C':
|
case 'C':
|
||||||
s := t.Format("2006")
|
s := t.Format("2006")
|
||||||
res.WriteString(s[:len(s)-2])
|
res.WriteString(s[:len(s)-2])
|
||||||
|
@ -45,6 +49,10 @@ func Format(fmt string, t time.Time) string {
|
||||||
res.WriteByte('0')
|
res.WriteByte('0')
|
||||||
}
|
}
|
||||||
res.WriteString(strconv.Itoa(w))
|
res.WriteString(strconv.Itoa(w))
|
||||||
|
case 'W':
|
||||||
|
res.WriteString(weekNumber(t, pad, true))
|
||||||
|
case 'U':
|
||||||
|
res.WriteString(weekNumber(t, pad, false))
|
||||||
case 'w':
|
case 'w':
|
||||||
w := int(t.Weekday())
|
w := int(t.Weekday())
|
||||||
res.WriteString(strconv.Itoa(w))
|
res.WriteString(strconv.Itoa(w))
|
||||||
|
@ -54,6 +62,16 @@ func Format(fmt string, t time.Time) string {
|
||||||
} else {
|
} else {
|
||||||
res.WriteString(strconv.Itoa(w))
|
res.WriteString(strconv.Itoa(w))
|
||||||
}
|
}
|
||||||
|
case 'k':
|
||||||
|
res.WriteString(strconv.Itoa(t.Hour()))
|
||||||
|
case 'l':
|
||||||
|
h := t.Hour()
|
||||||
|
if h == 0 {
|
||||||
|
h = 12
|
||||||
|
} else if h > 12 {
|
||||||
|
h -= 12
|
||||||
|
}
|
||||||
|
res.WriteString(strconv.Itoa(h))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,18 @@ var timeTests = []struct {
|
||||||
{"%c", time.ANSIC, "E MMM d HH:mm:ss yyyy", "Fri Aug 7 06:05:04 2009"},
|
{"%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"},
|
{"%+", 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"},
|
{"%FT%TZ", time.RFC3339[:20], "yyyy-MM-dd'T'HH:mm:ss'Z'", "2009-08-07T06:05:04Z"},
|
||||||
{"%a %b %e %H:%M:%S %Y", time.ANSIC, "", "Fri Aug 7 06:05:04 2009"},
|
{"%a %b %e %T %Y", time.ANSIC, "", "Fri Aug 7 06:05:04 2009"},
|
||||||
{"%a %b %e %H:%M:%S %Z %Y", time.UnixDate, "", "Fri Aug 7 06:05:04 UTC 2009"},
|
{"%a %b %e %T %Z %Y", time.UnixDate, "", "Fri Aug 7 06:05:04 UTC 2009"},
|
||||||
{"%a %b %d %H:%M:%S %z %Y", time.RubyDate, "E MMM dd HH:mm:ss Z yyyy", "Fri Aug 07 06:05:04 +0000 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 %H:%M:%S %Z", time.RFC1123, "E, dd MMM yyyy HH:mm:ss zzz", "Fri, 07 Aug 2009 06:05:04 UTC"},
|
{"%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 %H:%M:%S GMT", http.TimeFormat, "E, dd MMM yyyy HH:mm:ss 'GMT'", "Fri, 07 Aug 2009 06:05:04 GMT"},
|
{"%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"},
|
{"%Y-%m-%dT%H:%M:%SZ", time.RFC3339[:20], "yyyy-MM-dd'T'HH:mm:ss'Z'", "2009-08-07T06:05:04Z"},
|
||||||
// Date formats
|
// Date formats
|
||||||
|
{"%v", "_2-Jan-2006", "d-MMM-yyyy", " 7-Aug-2009"},
|
||||||
{"%F", "2006-01-02", "yyyy-MM-dd", "2009-08-07"},
|
{"%F", "2006-01-02", "yyyy-MM-dd", "2009-08-07"},
|
||||||
{"%D", "01/02/06", "MM/dd/yy", "08/07/09"},
|
{"%D", "01/02/06", "MM/dd/yy", "08/07/09"},
|
||||||
{"%x", "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"},
|
{"%Y-%m-%d", "2006-01-02", "yyyy-MM-dd", "2009-08-07"},
|
||||||
{"%m/%d/%y", "01/02/06", "MM/dd/yy", "08/07/09"},
|
{"%m/%d/%y", "01/02/06", "MM/dd/yy", "08/07/09"},
|
||||||
// Time formats
|
// Time formats
|
||||||
|
@ -49,11 +51,11 @@ var timeTests = []struct {
|
||||||
// Parsing
|
// Parsing
|
||||||
{"", "", "", ""},
|
{"", "", "", ""},
|
||||||
{"%", "%", "%", "%"},
|
{"%", "%", "%", "%"},
|
||||||
{"%-", "-", "-", "-"},
|
{"%-", "%-", "%-", "%-"},
|
||||||
{"%n", "\n", "\n", "\n"},
|
{"%n", "\n", "\n", "\n"},
|
||||||
{"%t", "\t", "\t", "\t"},
|
{"%t", "\t", "\t", "\t"},
|
||||||
{"%q", "", "", ""},
|
{"%q", "", "", "%q"},
|
||||||
{"%-q", "", "", ""},
|
{"%-q", "", "", "%-q"},
|
||||||
{"'", "'", "''", "'"},
|
{"'", "'", "''", "'"},
|
||||||
{"100%", "", "100%", "100%"},
|
{"100%", "", "100%", "100%"},
|
||||||
{"Monday", "", "'Monday'", "Monday"},
|
{"Monday", "", "'Monday'", "Monday"},
|
||||||
|
@ -92,3 +94,129 @@ func TestUTS35(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// https://github.com/tebeka/strftime
|
||||||
|
// https://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_lestrrat(t *testing.T) {
|
||||||
|
// https://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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue