This commit is contained in:
Nuno Cruces 2022-03-31 02:11:14 +01:00
parent 81c6f396fe
commit 3d0e153dd0
3 changed files with 113 additions and 5 deletions

View File

@ -19,6 +19,7 @@ func (p *parser) goSpecifiers() {
'Y': "2006", 'Y': "2006",
'y': "06", 'y': "06",
'p': "PM", 'p': "PM",
'P': "pm",
'Z': "MST", 'Z': "MST",
'z': "-0700", 'z': "-0700",
'L': "000", 'L': "000",

View File

@ -1,7 +1,6 @@
package strftime package strftime
import ( import (
"bytes"
"errors" "errors"
"strconv" "strconv"
"strings" "strings"
@ -79,18 +78,32 @@ func Layout(fmt string) (string, error) {
parser.goSpecifiers() parser.goSpecifiers()
parser.writeLit = func(b byte) error { parser.writeLit = func(b byte) error {
if bytes.IndexByte([]byte("MonJan_0123456789"), b) >= 0 { if '0' <= b && b <= '9' {
return errors.New("strftime: unsupported literal: '" + string(b) + "'") return errors.New("strftime: unsupported literal digit: '" + string(b) + "'")
} }
res.WriteByte(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 return nil
} }
parser.writeFmt = func(fmt string) error { parser.writeFmt = func(fmt string) error {
switch fmt { switch fmt {
case "000", "000000", "000000000": case "000", "000000", "000000000":
res := res.String() if cur := res.String(); !(strings.HasSuffix(cur, ".") || strings.HasSuffix(cur, ",")) {
if len := len(res); len <= 0 || res[len-1] != '.' && res[len-1] != ',' {
return errors.New("strftime: unsupported specifier: fractional seconds must follow '.' or ','") return errors.New("strftime: unsupported specifier: fractional seconds must follow '.' or ','")
} }
} }

View File

@ -0,0 +1,94 @@
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 %H:%M:%S %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 %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, %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 %H:%M:%S 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
{"%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"},
{"%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", "", "", ""},
{"'", "'", "''", "'"},
{"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)
}
}
}