quando/inspect_test.go

441 lines
12 KiB
Go
Raw Normal View History

package quando
import (
"testing"
"time"
)
func TestWeekNumber(t *testing.T) {
tests := []struct {
name string
date time.Time
want int
}{
// Test all 7 days of week 7 in 2026 (Feb 9-15)
{
name: "2026-02-09 Monday week 7",
date: time.Date(2026, 2, 9, 0, 0, 0, 0, time.UTC),
want: 7,
},
{
name: "2026-02-10 Tuesday week 7",
date: time.Date(2026, 2, 10, 0, 0, 0, 0, time.UTC),
want: 7,
},
{
name: "2026-02-11 Wednesday week 7",
date: time.Date(2026, 2, 11, 0, 0, 0, 0, time.UTC),
want: 7,
},
{
name: "2026-02-12 Thursday week 7",
date: time.Date(2026, 2, 12, 0, 0, 0, 0, time.UTC),
want: 7,
},
{
name: "2026-02-13 Friday week 7",
date: time.Date(2026, 2, 13, 0, 0, 0, 0, time.UTC),
want: 7,
},
{
name: "2026-02-14 Saturday week 7",
date: time.Date(2026, 2, 14, 0, 0, 0, 0, time.UTC),
want: 7,
},
{
name: "2026-02-15 Sunday week 7",
date: time.Date(2026, 2, 15, 0, 0, 0, 0, time.UTC),
want: 7,
},
// Year boundary cases
{
name: "2023-01-01 Sunday belongs to 2022 week 52",
date: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
want: 52,
},
{
name: "2023-01-02 Monday is week 1 of 2023",
date: time.Date(2023, 1, 2, 0, 0, 0, 0, time.UTC),
want: 1,
},
{
name: "2024-01-01 Monday is week 1",
date: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
want: 1,
},
{
name: "2025-01-01 Wednesday is week 1",
date: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC),
want: 1,
},
{
name: "2026-01-01 Thursday is week 1",
date: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC),
want: 1,
},
// Week 53 scenarios
{
name: "2020-12-31 Thursday is week 53",
date: time.Date(2020, 12, 31, 0, 0, 0, 0, time.UTC),
want: 53,
},
{
name: "2026-12-31 Thursday is week 53",
date: time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC),
want: 53,
},
// Mid-year dates with known week numbers
{
name: "2026-06-15 Monday is week 25",
date: time.Date(2026, 6, 15, 0, 0, 0, 0, time.UTC),
want: 25,
},
{
name: "2026-12-28 Monday is week 53",
date: time.Date(2026, 12, 28, 0, 0, 0, 0, time.UTC),
want: 53,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := From(tt.date)
got := d.WeekNumber()
if got != tt.want {
t.Errorf("WeekNumber() = %d, want %d", got, tt.want)
}
})
}
}
func TestQuarter(t *testing.T) {
tests := []struct {
name string
date time.Time
want int
}{
// Test all 12 months
{"January Q1", time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC), 1},
{"February Q1", time.Date(2026, 2, 15, 0, 0, 0, 0, time.UTC), 1},
{"March Q1", time.Date(2026, 3, 15, 0, 0, 0, 0, time.UTC), 1},
{"April Q2", time.Date(2026, 4, 15, 0, 0, 0, 0, time.UTC), 2},
{"May Q2", time.Date(2026, 5, 15, 0, 0, 0, 0, time.UTC), 2},
{"June Q2", time.Date(2026, 6, 15, 0, 0, 0, 0, time.UTC), 2},
{"July Q3", time.Date(2026, 7, 15, 0, 0, 0, 0, time.UTC), 3},
{"August Q3", time.Date(2026, 8, 15, 0, 0, 0, 0, time.UTC), 3},
{"September Q3", time.Date(2026, 9, 15, 0, 0, 0, 0, time.UTC), 3},
{"October Q4", time.Date(2026, 10, 15, 0, 0, 0, 0, time.UTC), 4},
{"November Q4", time.Date(2026, 11, 15, 0, 0, 0, 0, time.UTC), 4},
{"December Q4", time.Date(2026, 12, 15, 0, 0, 0, 0, time.UTC), 4},
// Quarter boundaries
{"March 31 last day of Q1", time.Date(2026, 3, 31, 0, 0, 0, 0, time.UTC), 1},
{"April 1 first day of Q2", time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC), 2},
{"June 30 last day of Q2", time.Date(2026, 6, 30, 0, 0, 0, 0, time.UTC), 2},
{"July 1 first day of Q3", time.Date(2026, 7, 1, 0, 0, 0, 0, time.UTC), 3},
{"September 30 last day of Q3", time.Date(2026, 9, 30, 0, 0, 0, 0, time.UTC), 3},
{"October 1 first day of Q4", time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC), 4},
{"December 31 last day of Q4", time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC), 4},
// Leap year Feb 29
{"Feb 29 leap year Q1", time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), 1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := From(tt.date)
got := d.Quarter()
if got != tt.want {
t.Errorf("Quarter() = %d, want %d", got, tt.want)
}
})
}
}
func TestDayOfYear(t *testing.T) {
tests := []struct {
name string
date time.Time
want int
}{
// Jan 1 is always day 1
{"Jan 1 is day 1", time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), 1},
// Dec 31 in non-leap year is day 365
{"Dec 31 non-leap year is 365", time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC), 365},
{"Dec 31 2025 is 365", time.Date(2025, 12, 31, 0, 0, 0, 0, time.UTC), 365},
// Dec 31 in leap year is day 366
{"Dec 31 leap year is 366", time.Date(2024, 12, 31, 0, 0, 0, 0, time.UTC), 366},
{"Dec 31 2020 is 366", time.Date(2020, 12, 31, 0, 0, 0, 0, time.UTC), 366},
// Feb 29 in leap year is day 60
{"Feb 29 leap year is 60", time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), 60},
{"Feb 29 2020 is 60", time.Date(2020, 2, 29, 0, 0, 0, 0, time.UTC), 60},
// Random dates with known values
{"Feb 9 2026 is 40", time.Date(2026, 2, 9, 0, 0, 0, 0, time.UTC), 40},
{"Mar 1 2026 is 60", time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC), 60},
{"Mar 1 2024 leap year is 61", time.Date(2024, 3, 1, 0, 0, 0, 0, time.UTC), 61},
{"Jun 15 2026 is 166", time.Date(2026, 6, 15, 0, 0, 0, 0, time.UTC), 166},
{"Dec 25 2026 is 359", time.Date(2026, 12, 25, 0, 0, 0, 0, time.UTC), 359},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := From(tt.date)
got := d.DayOfYear()
if got != tt.want {
t.Errorf("DayOfYear() = %d, want %d", got, tt.want)
}
})
}
}
func TestIsWeekend(t *testing.T) {
tests := []struct {
name string
date time.Time
want bool
}{
// Test all 7 weekdays (Feb 9-15, 2026)
{"Monday not weekend", time.Date(2026, 2, 9, 0, 0, 0, 0, time.UTC), false},
{"Tuesday not weekend", time.Date(2026, 2, 10, 0, 0, 0, 0, time.UTC), false},
{"Wednesday not weekend", time.Date(2026, 2, 11, 0, 0, 0, 0, time.UTC), false},
{"Thursday not weekend", time.Date(2026, 2, 12, 0, 0, 0, 0, time.UTC), false},
{"Friday not weekend", time.Date(2026, 2, 13, 0, 0, 0, 0, time.UTC), false},
{"Saturday is weekend", time.Date(2026, 2, 14, 0, 0, 0, 0, time.UTC), true},
{"Sunday is weekend", time.Date(2026, 2, 15, 0, 0, 0, 0, time.UTC), true},
// Year boundaries on weekends
{"Jan 1 2023 Sunday is weekend", time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), true},
{"Dec 31 2022 Saturday is weekend", time.Date(2022, 12, 31, 0, 0, 0, 0, time.UTC), true},
{"Jan 1 2024 Monday not weekend", time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), false},
{"Dec 31 2024 Tuesday not weekend", time.Date(2024, 12, 31, 0, 0, 0, 0, time.UTC), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := From(tt.date)
got := d.IsWeekend()
if got != tt.want {
t.Errorf("IsWeekend() = %v, want %v", got, tt.want)
}
})
}
}
func TestIsLeapYear(t *testing.T) {
tests := []struct {
name string
year int
want bool
}{
// Regular leap years (divisible by 4, not by 100)
{"2024 is leap year", 2024, true},
{"2020 is leap year", 2020, true},
{"2028 is leap year", 2028, true},
{"2004 is leap year", 2004, true},
// Non-leap years
{"2026 not leap year", 2026, false},
{"2025 not leap year", 2025, false},
{"2023 not leap year", 2023, false},
{"2001 not leap year", 2001, false},
// Century rules (divisible by 100 but not 400)
{"1900 not leap year (century)", 1900, false},
{"2100 not leap year (century)", 2100, false},
{"2200 not leap year (century)", 2200, false},
{"2300 not leap year (century)", 2300, false},
// Century rules (divisible by 400)
{"2000 is leap year (400 rule)", 2000, true},
{"2400 is leap year (400 rule)", 2400, true},
{"1600 is leap year (400 rule)", 1600, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := From(time.Date(tt.year, 1, 1, 0, 0, 0, 0, time.UTC))
got := d.IsLeapYear()
if got != tt.want {
t.Errorf("IsLeapYear() for year %d = %v, want %v", tt.year, got, tt.want)
}
})
}
}
func TestInfo(t *testing.T) {
tests := []struct {
name string
date time.Time
want DateInfo
}{
{
name: "2026-02-09 Monday",
date: time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC),
want: DateInfo{
WeekNumber: 7,
Quarter: 1,
DayOfYear: 40,
IsWeekend: false,
IsLeapYear: false,
Unix: time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC).Unix(),
},
},
{
name: "2024-02-29 leap year Thursday",
date: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC),
want: DateInfo{
WeekNumber: 9,
Quarter: 1,
DayOfYear: 60,
IsWeekend: false,
IsLeapYear: true,
Unix: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC).Unix(),
},
},
{
name: "2026-12-31 Thursday week 53",
date: time.Date(2026, 12, 31, 23, 59, 59, 0, time.UTC),
want: DateInfo{
WeekNumber: 53,
Quarter: 4,
DayOfYear: 365,
IsWeekend: false,
IsLeapYear: false,
Unix: time.Date(2026, 12, 31, 23, 59, 59, 0, time.UTC).Unix(),
},
},
{
name: "2026-06-14 Sunday weekend Q2",
date: time.Date(2026, 6, 14, 0, 0, 0, 0, time.UTC),
want: DateInfo{
WeekNumber: 24,
Quarter: 2,
DayOfYear: 165,
IsWeekend: true,
IsLeapYear: false,
Unix: time.Date(2026, 6, 14, 0, 0, 0, 0, time.UTC).Unix(),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := From(tt.date)
got := d.Info()
if got.WeekNumber != tt.want.WeekNumber {
t.Errorf("Info().WeekNumber = %d, want %d", got.WeekNumber, tt.want.WeekNumber)
}
if got.Quarter != tt.want.Quarter {
t.Errorf("Info().Quarter = %d, want %d", got.Quarter, tt.want.Quarter)
}
if got.DayOfYear != tt.want.DayOfYear {
t.Errorf("Info().DayOfYear = %d, want %d", got.DayOfYear, tt.want.DayOfYear)
}
if got.IsWeekend != tt.want.IsWeekend {
t.Errorf("Info().IsWeekend = %v, want %v", got.IsWeekend, tt.want.IsWeekend)
}
if got.IsLeapYear != tt.want.IsLeapYear {
t.Errorf("Info().IsLeapYear = %v, want %v", got.IsLeapYear, tt.want.IsLeapYear)
}
if got.Unix != tt.want.Unix {
t.Errorf("Info().Unix = %d, want %d", got.Unix, tt.want.Unix)
}
})
}
}
// TestInfo_ConsistentWithIndividualMethods verifies that Info() returns
// the same values as calling each method individually
func TestInfo_ConsistentWithIndividualMethods(t *testing.T) {
dates := []time.Time{
time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC),
time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC),
time.Date(2026, 12, 31, 23, 59, 59, 0, time.UTC),
time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC),
time.Date(2020, 12, 31, 0, 0, 0, 0, time.UTC),
}
for _, date := range dates {
t.Run(date.Format("2006-01-02"), func(t *testing.T) {
d := From(date)
info := d.Info()
if info.WeekNumber != d.WeekNumber() {
t.Errorf("Info().WeekNumber inconsistent: got %d, individual method %d", info.WeekNumber, d.WeekNumber())
}
if info.Quarter != d.Quarter() {
t.Errorf("Info().Quarter inconsistent: got %d, individual method %d", info.Quarter, d.Quarter())
}
if info.DayOfYear != d.DayOfYear() {
t.Errorf("Info().DayOfYear inconsistent: got %d, individual method %d", info.DayOfYear, d.DayOfYear())
}
if info.IsWeekend != d.IsWeekend() {
t.Errorf("Info().IsWeekend inconsistent: got %v, individual method %v", info.IsWeekend, d.IsWeekend())
}
if info.IsLeapYear != d.IsLeapYear() {
t.Errorf("Info().IsLeapYear inconsistent: got %v, individual method %v", info.IsLeapYear, d.IsLeapYear())
}
if info.Unix != d.Unix() {
t.Errorf("Info().Unix inconsistent: got %d, individual method %d", info.Unix, d.Unix())
}
})
}
}
// Benchmarks
func BenchmarkWeekNumber(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.WeekNumber()
}
}
func BenchmarkQuarter(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.Quarter()
}
}
func BenchmarkDayOfYear(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.DayOfYear()
}
}
func BenchmarkIsWeekend(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.IsWeekend()
}
}
func BenchmarkIsLeapYear(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.IsLeapYear()
}
}
func BenchmarkInfo(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.Info()
}
}