timezone_test.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package timezone
  2. import (
  3. "testing"
  4. "time"
  5. )
  6. func TestInit(t *testing.T) {
  7. // Test with valid timezone
  8. err := Init("Asia/Shanghai")
  9. if err != nil {
  10. t.Fatalf("Init failed with valid timezone: %v", err)
  11. }
  12. // Verify time.Local was set
  13. if time.Local.String() != "Asia/Shanghai" {
  14. t.Errorf("time.Local not set correctly, got %s", time.Local.String())
  15. }
  16. // Verify our location variable
  17. if Location().String() != "Asia/Shanghai" {
  18. t.Errorf("Location() not set correctly, got %s", Location().String())
  19. }
  20. // Test Name()
  21. if Name() != "Asia/Shanghai" {
  22. t.Errorf("Name() not set correctly, got %s", Name())
  23. }
  24. }
  25. func TestInitInvalidTimezone(t *testing.T) {
  26. err := Init("Invalid/Timezone")
  27. if err == nil {
  28. t.Error("Init should fail with invalid timezone")
  29. }
  30. }
  31. func TestTimeNowAffected(t *testing.T) {
  32. // Reset to UTC first
  33. if err := Init("UTC"); err != nil {
  34. t.Fatalf("Init failed with UTC: %v", err)
  35. }
  36. utcNow := time.Now()
  37. // Switch to Shanghai (UTC+8)
  38. if err := Init("Asia/Shanghai"); err != nil {
  39. t.Fatalf("Init failed with Asia/Shanghai: %v", err)
  40. }
  41. shanghaiNow := time.Now()
  42. // The times should be the same instant, but different timezone representation
  43. // Shanghai should be 8 hours ahead in display
  44. _, utcOffset := utcNow.Zone()
  45. _, shanghaiOffset := shanghaiNow.Zone()
  46. expectedDiff := 8 * 3600 // 8 hours in seconds
  47. actualDiff := shanghaiOffset - utcOffset
  48. if actualDiff != expectedDiff {
  49. t.Errorf("Timezone offset difference incorrect: expected %d, got %d", expectedDiff, actualDiff)
  50. }
  51. }
  52. func TestToday(t *testing.T) {
  53. if err := Init("Asia/Shanghai"); err != nil {
  54. t.Fatalf("Init failed with Asia/Shanghai: %v", err)
  55. }
  56. today := Today()
  57. now := Now()
  58. // Today should be at 00:00:00
  59. if today.Hour() != 0 || today.Minute() != 0 || today.Second() != 0 {
  60. t.Errorf("Today() not at start of day: %v", today)
  61. }
  62. // Today should be same date as now
  63. if today.Year() != now.Year() || today.Month() != now.Month() || today.Day() != now.Day() {
  64. t.Errorf("Today() date mismatch: today=%v, now=%v", today, now)
  65. }
  66. }
  67. func TestStartOfDay(t *testing.T) {
  68. if err := Init("Asia/Shanghai"); err != nil {
  69. t.Fatalf("Init failed with Asia/Shanghai: %v", err)
  70. }
  71. // Create a time at 15:30:45
  72. testTime := time.Date(2024, 6, 15, 15, 30, 45, 123456789, Location())
  73. startOfDay := StartOfDay(testTime)
  74. expected := time.Date(2024, 6, 15, 0, 0, 0, 0, Location())
  75. if !startOfDay.Equal(expected) {
  76. t.Errorf("StartOfDay incorrect: expected %v, got %v", expected, startOfDay)
  77. }
  78. }
  79. func TestTruncateVsStartOfDay(t *testing.T) {
  80. // This test demonstrates why Truncate(24*time.Hour) can be problematic
  81. // and why StartOfDay is more reliable for timezone-aware code
  82. if err := Init("Asia/Shanghai"); err != nil {
  83. t.Fatalf("Init failed with Asia/Shanghai: %v", err)
  84. }
  85. now := Now()
  86. // Truncate operates on UTC, not local time
  87. truncated := now.Truncate(24 * time.Hour)
  88. // StartOfDay operates on local time
  89. startOfDay := StartOfDay(now)
  90. // These will likely be different for non-UTC timezones
  91. t.Logf("Now: %v", now)
  92. t.Logf("Truncate(24h): %v", truncated)
  93. t.Logf("StartOfDay: %v", startOfDay)
  94. // The truncated time may not be at local midnight
  95. // StartOfDay is always at local midnight
  96. if startOfDay.Hour() != 0 {
  97. t.Errorf("StartOfDay should be at hour 0, got %d", startOfDay.Hour())
  98. }
  99. }
  100. func TestDSTAwareness(t *testing.T) {
  101. // Test with a timezone that has DST (America/New_York)
  102. err := Init("America/New_York")
  103. if err != nil {
  104. t.Skipf("America/New_York timezone not available: %v", err)
  105. }
  106. // Just verify it doesn't crash
  107. _ = Today()
  108. _ = Now()
  109. _ = StartOfDay(Now())
  110. }
  111. func TestStartOfWeek_Boundaries(t *testing.T) {
  112. if err := Init("Asia/Shanghai"); err != nil {
  113. t.Fatalf("Init: %v", err)
  114. }
  115. t.Cleanup(func() { _ = Init("UTC") })
  116. loc := Location()
  117. wantMon := time.Date(2026, 5, 18, 0, 0, 0, 0, loc) // 2026-05-18 是周一
  118. cases := []struct {
  119. name string
  120. in time.Time
  121. }{
  122. {"friday", time.Date(2026, 5, 22, 14, 30, 0, 0, loc)},
  123. {"sunday", time.Date(2026, 5, 24, 10, 0, 0, 0, loc)},
  124. {"monday-self", time.Date(2026, 5, 18, 9, 15, 30, 0, loc)},
  125. }
  126. for _, c := range cases {
  127. t.Run(c.name, func(t *testing.T) {
  128. if got := StartOfWeek(c.in); !got.Equal(wantMon) {
  129. t.Errorf("StartOfWeek(%v) = %v, want %v", c.in, got, wantMon)
  130. }
  131. })
  132. }
  133. }