347 lines
7.2 KiB
Go
347 lines
7.2 KiB
Go
package timefmt
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestBuilder_BasicFormats(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
builder func() *Builder
|
|
wantGo string
|
|
wantLDML string
|
|
}{
|
|
{
|
|
name: "ISO 8601 date",
|
|
builder: func() *Builder {
|
|
return NewBuilder().
|
|
Year4().Dash().MonthNumeric2().Dash().DayNumeric2()
|
|
},
|
|
wantGo: "2006-01-02",
|
|
wantLDML: "yyyy-MM-dd",
|
|
},
|
|
{
|
|
name: "24-hour time",
|
|
builder: func() *Builder {
|
|
return NewBuilder().
|
|
Hour24().Colon().Minute().Colon().Second()
|
|
},
|
|
wantGo: "15:04:05",
|
|
wantLDML: "HH:mm:ss",
|
|
},
|
|
{
|
|
name: "Full datetime",
|
|
builder: func() *Builder {
|
|
return NewBuilder().
|
|
Year4().Dash().MonthNumeric2().Dash().DayNumeric2().
|
|
Space().
|
|
Hour24().Colon().Minute().Colon().Second()
|
|
},
|
|
wantGo: "2006-01-02 15:04:05",
|
|
wantLDML: "yyyy-MM-dd HH:mm:ss",
|
|
},
|
|
{
|
|
name: "12-hour with AM/PM",
|
|
builder: func() *Builder {
|
|
return NewBuilder().
|
|
Hour12().Colon().Minute().Space().AMPM()
|
|
},
|
|
wantGo: "3:04 PM",
|
|
wantLDML: "h:mm a",
|
|
},
|
|
{
|
|
name: "US date format",
|
|
builder: func() *Builder {
|
|
return NewBuilder().
|
|
MonthNumeric2().Slash().DayNumeric2().Slash().Year4()
|
|
},
|
|
wantGo: "01/02/2006",
|
|
wantLDML: "MM/dd/yyyy",
|
|
},
|
|
{
|
|
name: "European date format",
|
|
builder: func() *Builder {
|
|
return NewBuilder().
|
|
DayNumeric2().Slash().MonthNumeric2().Slash().Year4()
|
|
},
|
|
wantGo: "02/01/2006",
|
|
wantLDML: "dd/MM/yyyy",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
format := tt.builder().Build()
|
|
|
|
gotGo := format.GoFormat()
|
|
if gotGo != tt.wantGo {
|
|
t.Errorf("GoFormat() = %q, want %q", gotGo, tt.wantGo)
|
|
}
|
|
|
|
gotLDML := format.LDML()
|
|
if gotLDML != tt.wantLDML {
|
|
t.Errorf("LDML() = %q, want %q", gotLDML, tt.wantLDML)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuilder_ChainableMethods(t *testing.T) {
|
|
// Test that all methods return *Builder for chaining
|
|
builder := NewBuilder()
|
|
|
|
result := builder.
|
|
Year4().Year2().
|
|
MonthNumeric().MonthNumeric2().MonthShort().MonthFull().
|
|
DayNumeric().DayNumeric2().DaySpacePadded().DayOfYear().
|
|
WeekdayShort().WeekdayFull().
|
|
Hour24().Hour12().Hour12Padded().
|
|
Minute().MinuteUnpadded().
|
|
Second().SecondUnpadded().
|
|
Millisecond().Microsecond().Nanosecond().
|
|
AMPM().AMPMLower().
|
|
TimezoneOffset().TimezoneOffsetColon().TimezoneName().
|
|
Literal("test").Dash().Slash().Colon().Space().T().Comma().Period()
|
|
|
|
if result == nil {
|
|
t.Error("Builder methods should return *Builder for chaining")
|
|
}
|
|
|
|
// Verify it's the same builder instance (mutable pattern)
|
|
if result != builder {
|
|
t.Error("Builder methods should return the same builder instance")
|
|
}
|
|
}
|
|
|
|
func TestBuilder_LiteralShortcuts(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
builder func() *Builder
|
|
want string
|
|
}{
|
|
{
|
|
name: "Dash",
|
|
builder: func() *Builder { return NewBuilder().Dash() },
|
|
want: "-",
|
|
},
|
|
{
|
|
name: "Slash",
|
|
builder: func() *Builder { return NewBuilder().Slash() },
|
|
want: "/",
|
|
},
|
|
{
|
|
name: "Colon",
|
|
builder: func() *Builder { return NewBuilder().Colon() },
|
|
want: ":",
|
|
},
|
|
{
|
|
name: "Space",
|
|
builder: func() *Builder { return NewBuilder().Space() },
|
|
want: " ",
|
|
},
|
|
{
|
|
name: "T",
|
|
builder: func() *Builder { return NewBuilder().T() },
|
|
want: "T",
|
|
},
|
|
{
|
|
name: "Comma",
|
|
builder: func() *Builder { return NewBuilder().Comma() },
|
|
want: ", ",
|
|
},
|
|
{
|
|
name: "Period",
|
|
builder: func() *Builder { return NewBuilder().Period() },
|
|
want: ".",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
format := tt.builder().Build()
|
|
got := format.GoFormat()
|
|
if got != tt.want {
|
|
t.Errorf("GoFormat() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuilder_ComplexFormat(t *testing.T) {
|
|
// Build a complex format with multiple components
|
|
format := NewBuilder().
|
|
WeekdayFull().Comma().
|
|
MonthFull().Space().DayNumeric().Comma().Year4().
|
|
Space().Literal("at").Space().
|
|
Hour12().Colon().Minute().Space().AMPM().
|
|
Space().Literal("(").TimezoneName().Literal(")").
|
|
Build()
|
|
|
|
wantGo := "Monday, January 2, 2006 at 3:04 PM (MST)"
|
|
gotGo := format.GoFormat()
|
|
if gotGo != wantGo {
|
|
t.Errorf("GoFormat() = %q, want %q", gotGo, wantGo)
|
|
}
|
|
|
|
// Test that it actually formats correctly
|
|
testTime := time.Date(2026, time.February, 8, 15, 4, 5, 0, time.FixedZone("MST", -7*3600))
|
|
formatted := format.Format(testTime)
|
|
expected := "Sunday, February 8, 2026 at 3:04 PM (MST)"
|
|
if formatted != expected {
|
|
t.Errorf("Format() = %q, want %q", formatted, expected)
|
|
}
|
|
}
|
|
|
|
func TestPrebuiltFormats(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
format *Format
|
|
wantGo string
|
|
}{
|
|
{
|
|
name: "ISO8601",
|
|
format: ISO8601,
|
|
wantGo: "2006-01-02T15:04:05Z07:00",
|
|
},
|
|
{
|
|
name: "RFC3339",
|
|
format: RFC3339,
|
|
wantGo: "2006-01-02T15:04:05Z07:00",
|
|
},
|
|
{
|
|
name: "RFC3339Nano",
|
|
format: RFC3339Nano,
|
|
wantGo: "2006-01-02T15:04:05.999999999Z07:00",
|
|
},
|
|
{
|
|
name: "DateOnly",
|
|
format: DateOnly,
|
|
wantGo: "2006-01-02",
|
|
},
|
|
{
|
|
name: "TimeOnly",
|
|
format: TimeOnly,
|
|
wantGo: "15:04:05",
|
|
},
|
|
{
|
|
name: "DateTime",
|
|
format: DateTime,
|
|
wantGo: "2006-01-02 15:04:05",
|
|
},
|
|
{
|
|
name: "DateTimeWithMillis",
|
|
format: DateTimeWithMillis,
|
|
wantGo: "2006-01-02 15:04:05.000",
|
|
},
|
|
{
|
|
name: "DateUS",
|
|
format: DateUS,
|
|
wantGo: "01/02/2006",
|
|
},
|
|
{
|
|
name: "DateEU",
|
|
format: DateEU,
|
|
wantGo: "02/01/2006",
|
|
},
|
|
{
|
|
name: "DateTimeUS",
|
|
format: DateTimeUS,
|
|
wantGo: "01/02/2006 3:04:05 PM",
|
|
},
|
|
{
|
|
name: "DateTimeEU",
|
|
format: DateTimeEU,
|
|
wantGo: "02/01/2006 15:04:05",
|
|
},
|
|
{
|
|
name: "Kitchen",
|
|
format: Kitchen,
|
|
wantGo: "3:04 PM",
|
|
},
|
|
{
|
|
name: "Stamp",
|
|
format: Stamp,
|
|
wantGo: "Jan _2 15:04:05",
|
|
},
|
|
{
|
|
name: "StampMilli",
|
|
format: StampMilli,
|
|
wantGo: "Jan _2 15:04:05.000",
|
|
},
|
|
{
|
|
name: "StampMicro",
|
|
format: StampMicro,
|
|
wantGo: "Jan _2 15:04:05.000000",
|
|
},
|
|
{
|
|
name: "StampNano",
|
|
format: StampNano,
|
|
wantGo: "Jan _2 15:04:05.000000000",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.format == nil {
|
|
t.Fatal("Format is nil")
|
|
}
|
|
|
|
gotGo := tt.format.GoFormat()
|
|
if gotGo != tt.wantGo {
|
|
t.Errorf("GoFormat() = %q, want %q", gotGo, tt.wantGo)
|
|
}
|
|
|
|
// Verify LDML and Description don't panic
|
|
_ = tt.format.LDML()
|
|
_ = tt.format.Description()
|
|
_ = tt.format.Example()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPrebuiltFormats_Formatting(t *testing.T) {
|
|
testTime := time.Date(2026, time.February, 8, 15, 4, 5, 123456789, time.UTC)
|
|
|
|
tests := []struct {
|
|
name string
|
|
format *Format
|
|
want string
|
|
}{
|
|
{
|
|
name: "DateOnly",
|
|
format: DateOnly,
|
|
want: "2026-02-08",
|
|
},
|
|
{
|
|
name: "TimeOnly",
|
|
format: TimeOnly,
|
|
want: "15:04:05",
|
|
},
|
|
{
|
|
name: "DateTime",
|
|
format: DateTime,
|
|
want: "2026-02-08 15:04:05",
|
|
},
|
|
{
|
|
name: "DateUS",
|
|
format: DateUS,
|
|
want: "02/08/2026",
|
|
},
|
|
{
|
|
name: "DateEU",
|
|
format: DateEU,
|
|
want: "08/02/2026",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := tt.format.Format(testTime)
|
|
if got != tt.want {
|
|
t.Errorf("Format() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|