TestTimeZone.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibTest/TestCase.h>
  7. #include <AK/StringView.h>
  8. #include <AK/Time.h>
  9. #include <LibTimeZone/TimeZone.h>
  10. using enum TimeZone::InDST;
  11. static void test_offset(StringView time_zone, i64 time, i64 expected_offset, TimeZone::InDST expected_in_dst)
  12. {
  13. auto actual_offset = TimeZone::get_time_zone_offset(time_zone, AK::Time::from_seconds(time));
  14. VERIFY(actual_offset.has_value());
  15. EXPECT_EQ(actual_offset->seconds, expected_offset);
  16. EXPECT_EQ(actual_offset->in_dst, expected_in_dst);
  17. }
  18. #if ENABLE_TIME_ZONE_DATA
  19. # include <LibTimeZone/TimeZoneData.h>
  20. class TimeZoneGuard {
  21. public:
  22. explicit TimeZoneGuard(char const* tz)
  23. : m_tz(getenv("TZ"))
  24. {
  25. setenv("TZ", tz, 1);
  26. }
  27. ~TimeZoneGuard()
  28. {
  29. if (m_tz)
  30. setenv("TZ", m_tz, 1);
  31. else
  32. unsetenv("TZ");
  33. }
  34. private:
  35. char const* m_tz { nullptr };
  36. };
  37. TEST_CASE(time_zone_from_string)
  38. {
  39. EXPECT_EQ(TimeZone::time_zone_from_string("America/New_York"sv), TimeZone::TimeZone::America_New_York);
  40. EXPECT_EQ(TimeZone::time_zone_from_string("Europe/Paris"sv), TimeZone::TimeZone::Europe_Paris);
  41. EXPECT_EQ(TimeZone::time_zone_from_string("Etc/GMT+2"sv), TimeZone::TimeZone::Etc_GMT_Ahead_2);
  42. EXPECT_EQ(TimeZone::time_zone_from_string("Etc/GMT-5"sv), TimeZone::TimeZone::Etc_GMT_Behind_5);
  43. EXPECT(!TimeZone::time_zone_from_string("I don't exist"sv).has_value());
  44. }
  45. TEST_CASE(time_zone_from_string_link)
  46. {
  47. auto test_link = [](auto tz1, auto tz2) {
  48. auto result1 = TimeZone::time_zone_from_string(tz1);
  49. EXPECT(result1.has_value());
  50. auto result2 = TimeZone::time_zone_from_string(tz2);
  51. EXPECT(result2.has_value());
  52. EXPECT_EQ(*result1, *result2);
  53. };
  54. test_link("America/New_York"sv, "US/Eastern"sv);
  55. test_link("Etc/GMT"sv, "GMT"sv);
  56. test_link("Etc/GMT+0"sv, "GMT"sv);
  57. test_link("Etc/GMT-0"sv, "GMT"sv);
  58. test_link("Etc/UTC"sv, "UTC"sv);
  59. test_link("Etc/Universal"sv, "UTC"sv);
  60. test_link("Universal"sv, "UTC"sv);
  61. }
  62. TEST_CASE(case_insensitive_time_zone_from_string)
  63. {
  64. EXPECT_EQ(TimeZone::time_zone_from_string("UTC"sv), TimeZone::TimeZone::UTC);
  65. EXPECT_EQ(TimeZone::time_zone_from_string("utc"sv), TimeZone::TimeZone::UTC);
  66. EXPECT_EQ(TimeZone::time_zone_from_string("uTc"sv), TimeZone::TimeZone::UTC);
  67. }
  68. TEST_CASE(time_zone_to_string)
  69. {
  70. EXPECT_EQ(TimeZone::time_zone_to_string(TimeZone::TimeZone::America_New_York), "America/New_York"sv);
  71. EXPECT_EQ(TimeZone::time_zone_to_string(TimeZone::TimeZone::Europe_Paris), "Europe/Paris"sv);
  72. EXPECT_EQ(TimeZone::time_zone_to_string(TimeZone::TimeZone::Etc_GMT_Ahead_2), "Etc/GMT+2"sv);
  73. EXPECT_EQ(TimeZone::time_zone_to_string(TimeZone::TimeZone::Etc_GMT_Behind_5), "Etc/GMT-5"sv);
  74. }
  75. TEST_CASE(time_zone_to_string_link)
  76. {
  77. EXPECT_EQ(TimeZone::time_zone_to_string(TimeZone::TimeZone::Etc_UTC), "Etc/UTC"sv);
  78. EXPECT_EQ(TimeZone::time_zone_to_string(TimeZone::TimeZone::UTC), "Etc/UTC"sv);
  79. EXPECT_EQ(TimeZone::time_zone_to_string(TimeZone::TimeZone::Universal), "Etc/UTC"sv);
  80. EXPECT_EQ(TimeZone::time_zone_to_string(TimeZone::TimeZone::Etc_Universal), "Etc/UTC"sv);
  81. }
  82. TEST_CASE(canonicalize_time_zone)
  83. {
  84. EXPECT_EQ(TimeZone::canonicalize_time_zone("America/New_York"sv), "America/New_York"sv);
  85. EXPECT_EQ(TimeZone::canonicalize_time_zone("AmErIcA/NeW_YoRk"sv), "America/New_York"sv);
  86. EXPECT_EQ(TimeZone::canonicalize_time_zone("UTC"sv), "UTC"sv);
  87. EXPECT_EQ(TimeZone::canonicalize_time_zone("GMT"sv), "UTC"sv);
  88. EXPECT_EQ(TimeZone::canonicalize_time_zone("GMT+0"sv), "UTC"sv);
  89. EXPECT_EQ(TimeZone::canonicalize_time_zone("GMT-0"sv), "UTC"sv);
  90. EXPECT_EQ(TimeZone::canonicalize_time_zone("Etc/UTC"sv), "UTC"sv);
  91. EXPECT_EQ(TimeZone::canonicalize_time_zone("Etc/GMT"sv), "UTC"sv);
  92. EXPECT(!TimeZone::canonicalize_time_zone("I don't exist"sv).has_value());
  93. }
  94. TEST_CASE(invalid_time_zone)
  95. {
  96. TimeZoneGuard guard { "ladybird" };
  97. EXPECT_EQ(TimeZone::current_time_zone(), "UTC"sv);
  98. }
  99. static i64 offset(i64 sign, i64 hours, i64 minutes, i64 seconds)
  100. {
  101. return sign * ((hours * 3600) + (minutes * 60) + seconds);
  102. }
  103. TEST_CASE(get_time_zone_offset)
  104. {
  105. test_offset("America/Chicago"sv, -2717647201, offset(-1, 5, 50, 36), No); // Sunday, November 18, 1883 5:59:59 PM
  106. test_offset("America/Chicago"sv, -2717647200, offset(-1, 6, 00, 00), No); // Sunday, November 18, 1883 6:00:00 PM
  107. test_offset("America/Chicago"sv, -1067810460, offset(-1, 6, 00, 00), No); // Sunday, March 1, 1936 1:59:00 AM
  108. test_offset("America/Chicago"sv, -1067810400, offset(-1, 5, 00, 00), No); // Sunday, March 1, 1936 2:00:00 AM
  109. test_offset("America/Chicago"sv, -1045432860, offset(-1, 5, 00, 00), No); // Sunday, November 15, 1936 1:59:00 AM
  110. test_offset("America/Chicago"sv, -1045432800, offset(-1, 6, 00, 00), No); // Sunday, November 15, 1936 2:00:00 AM
  111. test_offset("Europe/London"sv, -3852662401, offset(-1, 0, 01, 15), No); // Tuesday, November 30, 1847 11:59:59 PM
  112. test_offset("Europe/London"sv, -3852662400, offset(+1, 0, 00, 00), No); // Wednesday, December 1, 1847 12:00:00 AM
  113. test_offset("Europe/London"sv, -37238401, offset(+1, 0, 00, 00), No); // Saturday, October 26, 1968 11:59:59 PM
  114. test_offset("Europe/London"sv, -37238400, offset(+1, 1, 00, 00), No); // Sunday, October 27, 1968 12:00:00 AM
  115. test_offset("Europe/London"sv, 57722399, offset(+1, 1, 00, 00), No); // Sunday, October 31, 1971 1:59:59 AM
  116. test_offset("Europe/London"sv, 57722400, offset(+1, 0, 00, 00), No); // Sunday, October 31, 1971 2:00:00 AM
  117. test_offset("UTC"sv, -1641846268, offset(+1, 0, 00, 00), No);
  118. test_offset("UTC"sv, 0, offset(+1, 0, 00, 00), No);
  119. test_offset("UTC"sv, 1641846268, offset(+1, 0, 00, 00), No);
  120. test_offset("Etc/GMT+4"sv, -1641846268, offset(-1, 4, 00, 00), No);
  121. test_offset("Etc/GMT+5"sv, 0, offset(-1, 5, 00, 00), No);
  122. test_offset("Etc/GMT+6"sv, 1641846268, offset(-1, 6, 00, 00), No);
  123. test_offset("Etc/GMT-12"sv, -1641846268, offset(+1, 12, 00, 00), No);
  124. test_offset("Etc/GMT-13"sv, 0, offset(+1, 13, 00, 00), No);
  125. test_offset("Etc/GMT-14"sv, 1641846268, offset(+1, 14, 00, 00), No);
  126. EXPECT(!TimeZone::get_time_zone_offset("I don't exist"sv, {}).has_value());
  127. }
  128. TEST_CASE(get_time_zone_offset_with_dst)
  129. {
  130. test_offset("America/New_York"sv, 1642558528, offset(-1, 5, 00, 00), No); // Wednesday, January 19, 2022 2:15:28 AM
  131. test_offset("America/New_York"sv, 1663553728, offset(-1, 4, 00, 00), Yes); // Monday, September 19, 2022 2:15:28 AM
  132. test_offset("America/New_York"sv, 1671453238, offset(-1, 5, 00, 00), No); // Monday, December 19, 2022 12:33:58 PM
  133. // Phoenix does not observe DST.
  134. test_offset("America/Phoenix"sv, 1642558528, offset(-1, 7, 00, 00), No); // Wednesday, January 19, 2022 2:15:28 AM
  135. test_offset("America/Phoenix"sv, 1663553728, offset(-1, 7, 00, 00), No); // Monday, September 19, 2022 2:15:28 AM
  136. test_offset("America/Phoenix"sv, 1671453238, offset(-1, 7, 00, 00), No); // Monday, December 19, 2022 12:33:58 PM
  137. // Moscow's observed DST changed several times in 1919.
  138. test_offset("Europe/Moscow"sv, -1609459200, offset(+1, 2, 31, 19), No); // Wednesday, January 1, 1919 12:00:00 AM
  139. test_offset("Europe/Moscow"sv, -1596412800, offset(+1, 4, 31, 19), Yes); // Sunday, June 1, 1919 12:00:00 AM
  140. test_offset("Europe/Moscow"sv, -1592611200, offset(+1, 4, 00, 00), Yes); // Tuesday, July 15, 1919 12:00:00 AM
  141. test_offset("Europe/Moscow"sv, -1589068800, offset(+1, 3, 00, 00), No); // Monday, August 25, 1919 12:00:00 AM
  142. // Paraguay begins the year in DST.
  143. test_offset("America/Asuncion"sv, 1642558528, offset(-1, 3, 00, 00), Yes); // Wednesday, January 19, 2022 2:15:28 AM
  144. test_offset("America/Asuncion"sv, 1663553728, offset(-1, 4, 00, 00), No); // Monday, September 19, 2022 2:15:28 AM
  145. test_offset("America/Asuncion"sv, 1671453238, offset(-1, 3, 00, 00), Yes); // Monday, December 19, 2022 12:33:58 PM
  146. }
  147. TEST_CASE(get_named_time_zone_offsets)
  148. {
  149. auto test_named_offsets = [](auto time_zone, i64 time, i64 expected_standard_offset, i64 expected_daylight_offset, auto expected_standard_name, auto expected_daylight_name) {
  150. auto actual_offsets = TimeZone::get_named_time_zone_offsets(time_zone, AK::Time::from_seconds(time));
  151. VERIFY(actual_offsets.has_value());
  152. EXPECT_EQ(actual_offsets->at(0).seconds, expected_standard_offset);
  153. EXPECT_EQ(actual_offsets->at(1).seconds, expected_daylight_offset);
  154. EXPECT_EQ(actual_offsets->at(0).name, expected_standard_name);
  155. EXPECT_EQ(actual_offsets->at(1).name, expected_daylight_name);
  156. };
  157. test_named_offsets("America/New_York"sv, 1642558528, offset(-1, 5, 00, 00), offset(-1, 4, 00, 00), "EST"sv, "EDT"sv); // Wednesday, January 19, 2022 2:15:28 AM
  158. test_named_offsets("UTC"sv, 1642558528, offset(+1, 0, 00, 00), offset(+1, 0, 00, 00), "UTC"sv, "UTC"sv); // Wednesday, January 19, 2022 2:15:28 AM
  159. test_named_offsets("GMT"sv, 1642558528, offset(+1, 0, 00, 00), offset(+1, 0, 00, 00), "GMT"sv, "GMT"sv); // Wednesday, January 19, 2022 2:15:28 AM
  160. // Phoenix does not observe DST.
  161. test_named_offsets("America/Phoenix"sv, 1642558528, offset(-1, 7, 00, 00), offset(-1, 7, 00, 00), "MST"sv, "MST"sv); // Wednesday, January 19, 2022 2:15:28 AM
  162. // Moscow's observed DST changed several times in 1919.
  163. test_named_offsets("Europe/Moscow"sv, -1609459200, offset(+1, 2, 31, 19), offset(+1, 3, 31, 19), "MSK"sv, "MSD"sv); // Wednesday, January 1, 1919 12:00:00 AM
  164. test_named_offsets("Europe/Moscow"sv, -1596412800, offset(+1, 2, 31, 19), offset(+1, 4, 31, 19), "MSK"sv, "MDST"sv); // Sunday, June 1, 1919 12:00:00 AM
  165. test_named_offsets("Europe/Moscow"sv, -1589068800, offset(+1, 3, 00, 00), offset(+1, 4, 00, 00), "MSK"sv, "MSD"sv); // Monday, August 25, 1919 12:00:00 AM
  166. // Shanghai's DST rules end in 1991.
  167. test_named_offsets("Asia/Shanghai"sv, 694223999, offset(+1, 8, 00, 00), offset(+1, 9, 00, 00), "CST"sv, "CDT"sv); // Tuesday, December 31, 1991 11:59:59 PM
  168. test_named_offsets("Asia/Shanghai"sv, 694224000, offset(+1, 8, 00, 00), offset(+1, 8, 00, 00), "CST"sv, "CST"sv); // Wednesday, January 1, 1992 12:00:00 AM
  169. }
  170. #else
  171. TEST_CASE(time_zone_from_string)
  172. {
  173. EXPECT(TimeZone::time_zone_from_string("UTC"sv).has_value());
  174. EXPECT(!TimeZone::time_zone_from_string("Europe/Paris"sv).has_value());
  175. EXPECT(!TimeZone::time_zone_from_string("Etc/UTC"sv).has_value());
  176. EXPECT(!TimeZone::time_zone_from_string("I don't exist"sv).has_value());
  177. }
  178. TEST_CASE(get_time_zone_offset)
  179. {
  180. test_offset("UTC"sv, 123456, 0, No);
  181. EXPECT(!TimeZone::get_time_zone_offset("Europe/Paris"sv, {}).has_value());
  182. EXPECT(!TimeZone::get_time_zone_offset("Etc/UTC"sv, {}).has_value());
  183. EXPECT(!TimeZone::get_time_zone_offset("I don't exist"sv, {}).has_value());
  184. }
  185. #endif