initial commit
This commit is contained in:
302
converter_test.go
Normal file
302
converter_test.go
Normal file
@@ -0,0 +1,302 @@
|
||||
package timefmt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParseGoFormat(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
goFormat string
|
||||
wantLDML string
|
||||
wantDesc string
|
||||
shouldError bool
|
||||
}{
|
||||
{
|
||||
name: "ISO 8601 date",
|
||||
goFormat: "2006-01-02",
|
||||
wantLDML: "yyyy-MM-dd",
|
||||
wantDesc: "Year (4-digit), dash, Month (2-digit), dash, Day (2-digit)",
|
||||
},
|
||||
{
|
||||
name: "24-hour time",
|
||||
goFormat: "15:04:05",
|
||||
wantLDML: "HH:mm:ss",
|
||||
wantDesc: "Hour (24-hour, 2-digit), colon, Minute (2-digit), colon, Second (2-digit)",
|
||||
},
|
||||
{
|
||||
name: "Full datetime",
|
||||
goFormat: "2006-01-02 15:04:05",
|
||||
wantLDML: "yyyy-MM-dd HH:mm:ss",
|
||||
wantDesc: "Year (4-digit), dash, Month (2-digit), dash, Day (2-digit), space, Hour (24-hour, 2-digit), colon, Minute (2-digit), colon, Second (2-digit)",
|
||||
},
|
||||
{
|
||||
name: "12-hour with AM/PM",
|
||||
goFormat: "3:04 PM",
|
||||
wantLDML: "h:mm a",
|
||||
wantDesc: "Hour (12-hour), colon, Minute (2-digit), space, AM/PM (uppercase)",
|
||||
},
|
||||
{
|
||||
name: "US date format",
|
||||
goFormat: "01/02/2006",
|
||||
wantLDML: "MM/dd/yyyy",
|
||||
wantDesc: "Month (2-digit), slash, Day (2-digit), slash, Year (4-digit)",
|
||||
},
|
||||
{
|
||||
name: "European date format",
|
||||
goFormat: "02/01/2006",
|
||||
wantLDML: "dd/MM/yyyy",
|
||||
wantDesc: "Day (2-digit), slash, Month (2-digit), slash, Year (4-digit)",
|
||||
},
|
||||
{
|
||||
name: "RFC3339",
|
||||
goFormat: "2006-01-02T15:04:05Z07:00",
|
||||
wantLDML: "yyyy-MM-dd'T'HH:mm:ssZZZZZ",
|
||||
wantDesc: "Year (4-digit), dash, Month (2-digit), dash, Day (2-digit), literal 'T', Hour (24-hour, 2-digit), colon, Minute (2-digit), colon, Second (2-digit), ISO 8601 timezone (Z or ±HH:MM)",
|
||||
},
|
||||
{
|
||||
name: "With milliseconds",
|
||||
goFormat: "2006-01-02 15:04:05.000",
|
||||
wantLDML: "yyyy-MM-dd HH:mm:ss.SSS",
|
||||
wantDesc: "Year (4-digit), dash, Month (2-digit), dash, Day (2-digit), space, Hour (24-hour, 2-digit), colon, Minute (2-digit), colon, Second (2-digit), Millisecond (3-digit)",
|
||||
},
|
||||
{
|
||||
name: "With nanoseconds",
|
||||
goFormat: "2006-01-02 15:04:05.000000000",
|
||||
wantLDML: "yyyy-MM-dd HH:mm:ss.SSSSSSSSS",
|
||||
wantDesc: "Year (4-digit), dash, Month (2-digit), dash, Day (2-digit), space, Hour (24-hour, 2-digit), colon, Minute (2-digit), colon, Second (2-digit), Nanosecond (9-digit)",
|
||||
},
|
||||
{
|
||||
name: "Month names",
|
||||
goFormat: "January 2, 2006",
|
||||
wantLDML: "MMMM d, yyyy",
|
||||
wantDesc: "Month (full name), space, Day (numeric), comma-space, Year (4-digit)",
|
||||
},
|
||||
{
|
||||
name: "Abbreviated month and weekday",
|
||||
goFormat: "Mon, Jan 2 2006",
|
||||
wantLDML: "EEE, MMM d yyyy",
|
||||
wantDesc: "Weekday (abbreviated), comma-space, Month (abbreviated), space, Day (numeric), space, Year (4-digit)",
|
||||
},
|
||||
{
|
||||
name: "Full weekday and month",
|
||||
goFormat: "Monday, January 2, 2006",
|
||||
wantLDML: "EEEE, MMMM d, yyyy",
|
||||
wantDesc: "Weekday (full name), comma-space, Month (full name), space, Day (numeric), comma-space, Year (4-digit)",
|
||||
},
|
||||
{
|
||||
name: "With timezone name",
|
||||
goFormat: "2006-01-02 15:04:05 MST",
|
||||
wantLDML: "yyyy-MM-dd HH:mm:ss zzz",
|
||||
wantDesc: "Year (4-digit), dash, Month (2-digit), dash, Day (2-digit), space, Hour (24-hour, 2-digit), colon, Minute (2-digit), colon, Second (2-digit), space, Timezone abbreviation",
|
||||
},
|
||||
{
|
||||
name: "With timezone offset",
|
||||
goFormat: "2006-01-02 15:04:05 -0700",
|
||||
wantLDML: "yyyy-MM-dd HH:mm:ss ZZZ",
|
||||
wantDesc: "Year (4-digit), dash, Month (2-digit), dash, Day (2-digit), space, Hour (24-hour, 2-digit), colon, Minute (2-digit), colon, Second (2-digit), space, Timezone offset (±HHMM)",
|
||||
},
|
||||
{
|
||||
name: "Kitchen time",
|
||||
goFormat: "3:04PM",
|
||||
wantLDML: "h:mma",
|
||||
wantDesc: "Hour (12-hour), colon, Minute (2-digit), AM/PM (uppercase)",
|
||||
},
|
||||
{
|
||||
name: "With literal text",
|
||||
goFormat: "2006-01-02 at 15:04",
|
||||
wantLDML: "yyyy-MM-dd' at 'HH:mm",
|
||||
wantDesc: "Year (4-digit), dash, Month (2-digit), dash, Day (2-digit), literal ' at ', Hour (24-hour, 2-digit), colon, Minute (2-digit)",
|
||||
},
|
||||
{
|
||||
name: "Space-padded day",
|
||||
goFormat: "Jan _2 15:04:05",
|
||||
wantLDML: "MMM d HH:mm:ss",
|
||||
wantDesc: "Month (abbreviated), space, Day (space-padded), space, Hour (24-hour, 2-digit), colon, Minute (2-digit), colon, Second (2-digit)",
|
||||
},
|
||||
{
|
||||
name: "Empty string",
|
||||
goFormat: "",
|
||||
shouldError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
format, err := ParseGoFormat(tt.goFormat)
|
||||
|
||||
if tt.shouldError {
|
||||
if err == nil {
|
||||
t.Error("Expected error but got none")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("ParseGoFormat() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify the format can reproduce the original Go format
|
||||
gotGoFormat := format.GoFormat()
|
||||
if gotGoFormat != tt.goFormat {
|
||||
t.Errorf("GoFormat() = %q, want %q", gotGoFormat, tt.goFormat)
|
||||
}
|
||||
|
||||
// Verify LDML conversion
|
||||
gotLDML := format.LDML()
|
||||
if gotLDML != tt.wantLDML {
|
||||
t.Errorf("LDML() = %q, want %q", gotLDML, tt.wantLDML)
|
||||
}
|
||||
|
||||
// Verify description
|
||||
gotDesc := format.Description()
|
||||
if gotDesc != tt.wantDesc {
|
||||
t.Errorf("Description() = %q, want %q", gotDesc, tt.wantDesc)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseGoFormat_StdlibFormats(t *testing.T) {
|
||||
// Test that we can parse all the standard library time format constants
|
||||
tests := []struct {
|
||||
name string
|
||||
goFormat string
|
||||
}{
|
||||
{"time.ANSIC", "Mon Jan _2 15:04:05 2006"},
|
||||
{"time.UnixDate", "Mon Jan _2 15:04:05 MST 2006"},
|
||||
{"time.RubyDate", "Mon Jan 02 15:04:05 -0700 2006"},
|
||||
{"time.RFC822", "02 Jan 06 15:04 MST"},
|
||||
{"time.RFC822Z", "02 Jan 06 15:04 -0700"},
|
||||
{"time.RFC850", "Monday, 02-Jan-06 15:04:05 MST"},
|
||||
{"time.RFC1123", "Mon, 02 Jan 2006 15:04:05 MST"},
|
||||
{"time.RFC1123Z", "Mon, 02 Jan 2006 15:04:05 -0700"},
|
||||
{"time.RFC3339", "2006-01-02T15:04:05Z07:00"},
|
||||
{"time.RFC3339Nano", "2006-01-02T15:04:05.999999999Z07:00"},
|
||||
{"time.Kitchen", "3:04PM"},
|
||||
{"time.Stamp", "Jan _2 15:04:05"},
|
||||
{"time.StampMilli", "Jan _2 15:04:05.000"},
|
||||
{"time.StampMicro", "Jan _2 15:04:05.000000"},
|
||||
{"time.StampNano", "Jan _2 15:04:05.000000000"},
|
||||
{"time.DateTime", "2006-01-02 15:04:05"},
|
||||
{"time.DateOnly", "2006-01-02"},
|
||||
{"time.TimeOnly", "15:04:05"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
format, err := ParseGoFormat(tt.goFormat)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseGoFormat() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify roundtrip
|
||||
gotGoFormat := format.GoFormat()
|
||||
if gotGoFormat != tt.goFormat {
|
||||
t.Errorf("GoFormat() = %q, want %q", gotGoFormat, tt.goFormat)
|
||||
}
|
||||
|
||||
// Verify it can actually format a time
|
||||
testTime := time.Date(2026, time.February, 8, 15, 4, 5, 123456789, time.FixedZone("MST", -7*3600))
|
||||
formatted := format.Format(testTime)
|
||||
if formatted == "" {
|
||||
t.Error("Format() returned empty string")
|
||||
}
|
||||
|
||||
// Verify LDML and Description don't panic
|
||||
_ = format.LDML()
|
||||
_ = format.Description()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseGoFormat_RoundTrip(t *testing.T) {
|
||||
// Test that parsing a format and converting back to Go format is lossless
|
||||
formats := []string{
|
||||
"2006-01-02",
|
||||
"15:04:05",
|
||||
"2006-01-02 15:04:05",
|
||||
"01/02/2006 3:04:05 PM",
|
||||
"Monday, January 2, 2006",
|
||||
"Jan _2 15:04:05.000",
|
||||
"2006-01-02T15:04:05Z07:00",
|
||||
"02 Jan 06 15:04 MST",
|
||||
}
|
||||
|
||||
for _, original := range formats {
|
||||
t.Run(original, func(t *testing.T) {
|
||||
format, err := ParseGoFormat(original)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseGoFormat() error = %v", err)
|
||||
}
|
||||
|
||||
result := format.GoFormat()
|
||||
if result != original {
|
||||
t.Errorf("Round trip failed: got %q, want %q", result, original)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustParseGoFormat(t *testing.T) {
|
||||
t.Run("Valid format", func(t *testing.T) {
|
||||
// Should not panic
|
||||
format := MustParseGoFormat("2006-01-02")
|
||||
if format == nil {
|
||||
t.Error("MustParseGoFormat returned nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Invalid format panics", func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("MustParseGoFormat should panic on empty string")
|
||||
}
|
||||
}()
|
||||
MustParseGoFormat("")
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseGoFormat_EdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
goFormat string
|
||||
wantGo string
|
||||
}{
|
||||
{
|
||||
name: "Consecutive literals",
|
||||
goFormat: "2006-01-02T15:04:05",
|
||||
wantGo: "2006-01-02T15:04:05",
|
||||
},
|
||||
{
|
||||
name: "Just literals",
|
||||
goFormat: "Hello, World!",
|
||||
wantGo: "Hello, World!",
|
||||
},
|
||||
{
|
||||
name: "Mixed tokens and literals",
|
||||
goFormat: "Year: 2006, Month: 01",
|
||||
wantGo: "Year: 2006, Month: 01",
|
||||
},
|
||||
{
|
||||
name: "Special characters",
|
||||
goFormat: "2006/01/02 @ 15:04:05",
|
||||
wantGo: "2006/01/02 @ 15:04:05",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
format, err := ParseGoFormat(tt.goFormat)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseGoFormat() error = %v", err)
|
||||
}
|
||||
|
||||
gotGo := format.GoFormat()
|
||||
if gotGo != tt.wantGo {
|
||||
t.Errorf("GoFormat() = %q, want %q", gotGo, tt.wantGo)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user