66} from "../date_utils" ;
77
88describe ( "Timezone utility functions" , ( ) => {
9+ // Use a fixed UTC date for consistent testing
910 const testDate = new Date ( "2024-06-15T12:00:00Z" ) ;
1011
1112 describe ( "toZonedTime" , ( ) => {
@@ -19,20 +20,36 @@ describe("Timezone utility functions", () => {
1920 expect ( result ) . toBe ( testDate ) ;
2021 } ) ;
2122
22- it ( "should return the original date when date-fns-tz is not installed" , ( ) => {
23- // Suppress console.warn for this test
24- const warnSpy = jest . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } ) ;
25-
26- // Since date-fns-tz is not installed, it should return the original date
23+ it ( "should convert UTC date to specified timezone" , ( ) => {
24+ // 2024-06-15T12:00:00Z in America/New_York (EDT, UTC-4) should be 08:00
2725 const result = toZonedTime ( testDate , "America/New_York" ) ;
28- expect ( result ) . toBe ( testDate ) ;
26+ expect ( result ) . toBeInstanceOf ( Date ) ;
27+ // The result should represent 08:00 in New York time
28+ expect ( result . getHours ( ) ) . toBe ( 8 ) ;
29+ expect ( result . getMinutes ( ) ) . toBe ( 0 ) ;
30+ } ) ;
2931
30- // Should have warned about missing date-fns-tz
31- expect ( warnSpy ) . toHaveBeenCalledWith (
32- expect . stringContaining ( "date-fns-tz" ) ,
33- ) ;
32+ it ( "should handle UTC timezone" , ( ) => {
33+ const result = toZonedTime ( testDate , "UTC" ) ;
34+ expect ( result ) . toBeInstanceOf ( Date ) ;
35+ // toZonedTime returns a date that represents the time in the target timezone
36+ // When displayed locally, it should show 12:00 (the UTC time)
37+ expect ( result . getHours ( ) ) . toBe ( 12 ) ;
38+ expect ( result . getMinutes ( ) ) . toBe ( 0 ) ;
39+ } ) ;
3440
35- warnSpy . mockRestore ( ) ;
41+ it ( "should handle different timezones" , ( ) => {
42+ // Test with Europe/London (BST in June, UTC+1)
43+ const londonResult = toZonedTime ( testDate , "Europe/London" ) ;
44+ expect ( londonResult ) . toBeInstanceOf ( Date ) ;
45+ // 12:00 UTC should be 13:00 in London during BST
46+ expect ( londonResult . getHours ( ) ) . toBe ( 13 ) ;
47+
48+ // Test with Asia/Tokyo (JST, UTC+9)
49+ const tokyoResult = toZonedTime ( testDate , "Asia/Tokyo" ) ;
50+ expect ( tokyoResult ) . toBeInstanceOf ( Date ) ;
51+ // 12:00 UTC should be 21:00 in Tokyo
52+ expect ( tokyoResult . getHours ( ) ) . toBe ( 21 ) ;
3653 } ) ;
3754 } ) ;
3855
@@ -47,15 +64,28 @@ describe("Timezone utility functions", () => {
4764 expect ( result ) . toBe ( testDate ) ;
4865 } ) ;
4966
50- it ( "should return the original date when date-fns-tz is not installed" , ( ) => {
51- // Suppress console.warn for this test
52- const warnSpy = jest . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } ) ;
67+ it ( "should convert zoned time to UTC" , ( ) => {
68+ // Create a date representing 08:00 in New York (which is 12:00 UTC in June)
69+ const nyDate = new Date ( "2024-06-15T08:00:00" ) ;
70+ const result = fromZonedTime ( nyDate , "America/New_York" ) ;
71+ expect ( result ) . toBeInstanceOf ( Date ) ;
72+ // The result should be 12:00 UTC
73+ expect ( result . getUTCHours ( ) ) . toBe ( 12 ) ;
74+ } ) ;
5375
54- // Since date-fns-tz is not installed, it should return the original date
55- const result = fromZonedTime ( testDate , "America/New_York" ) ;
56- expect ( result ) . toBe ( testDate ) ;
76+ it ( "should handle UTC timezone" , ( ) => {
77+ const utcDate = new Date ( "2024-06-15T12:00:00" ) ;
78+ const result = fromZonedTime ( utcDate , "UTC" ) ;
79+ expect ( result ) . toBeInstanceOf ( Date ) ;
80+ expect ( result . getUTCHours ( ) ) . toBe ( 12 ) ;
81+ } ) ;
5782
58- warnSpy . mockRestore ( ) ;
83+ it ( "should be inverse of toZonedTime" , ( ) => {
84+ const timezone = "America/Los_Angeles" ;
85+ const zonedTime = toZonedTime ( testDate , timezone ) ;
86+ const backToUtc = fromZonedTime ( zonedTime , timezone ) ;
87+ // The round-trip should give us back the original UTC time
88+ expect ( backToUtc . getTime ( ) ) . toBe ( testDate . getTime ( ) ) ;
5989 } ) ;
6090 } ) ;
6191
@@ -66,19 +96,38 @@ describe("Timezone utility functions", () => {
6696 expect ( result ) . toBe ( "2024-06-15" ) ;
6797 } ) ;
6898
69- it ( "should use standard format when date-fns-tz is not installed" , ( ) => {
70- // Suppress console.warn for this test
71- const warnSpy = jest . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } ) ;
72-
73- // Since date-fns-tz is not installed, it should fall back to standard format
99+ it ( "should format date in specified timezone" , ( ) => {
100+ // 2024-06-15T12:00:00Z formatted in America/New_York should show 08:00
74101 const result = formatInTimeZone (
75102 testDate ,
76- "yyyy-MM-dd" ,
103+ "yyyy-MM-dd HH:mm " ,
77104 "America/New_York" ,
78105 ) ;
79- expect ( result ) . toBe ( "2024-06-15" ) ;
106+ expect ( result ) . toBe ( "2024-06-15 08:00" ) ;
107+ } ) ;
108+
109+ it ( "should format date in UTC timezone" , ( ) => {
110+ const result = formatInTimeZone ( testDate , "yyyy-MM-dd HH:mm" , "UTC" ) ;
111+ expect ( result ) . toBe ( "2024-06-15 12:00" ) ;
112+ } ) ;
113+
114+ it ( "should format date in different timezones" , ( ) => {
115+ // Europe/London (BST in June, UTC+1)
116+ const londonResult = formatInTimeZone ( testDate , "HH:mm" , "Europe/London" ) ;
117+ expect ( londonResult ) . toBe ( "13:00" ) ;
80118
81- warnSpy . mockRestore ( ) ;
119+ // Asia/Tokyo (JST, UTC+9)
120+ const tokyoResult = formatInTimeZone ( testDate , "HH:mm" , "Asia/Tokyo" ) ;
121+ expect ( tokyoResult ) . toBe ( "21:00" ) ;
122+ } ) ;
123+
124+ it ( "should handle complex format strings" , ( ) => {
125+ const result = formatInTimeZone (
126+ testDate ,
127+ "EEEE, MMMM d, yyyy 'at' h:mm a" ,
128+ "America/New_York" ,
129+ ) ;
130+ expect ( result ) . toBe ( "Saturday, June 15, 2024 at 8:00 AM" ) ;
82131 } ) ;
83132 } ) ;
84133
@@ -92,20 +141,17 @@ describe("Timezone utility functions", () => {
92141 expect ( result . getTime ( ) ) . toBeLessThanOrEqual ( after . getTime ( ) ) ;
93142 } ) ;
94143
95- it ( "should return current date when date-fns-tz is not installed" , ( ) => {
96- // Suppress console.warn for this test
97- const warnSpy = jest . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } ) ;
98-
99- const before = new Date ( ) ;
144+ it ( "should return current date in specified timezone" , ( ) => {
100145 const result = nowInTimeZone ( "America/New_York" ) ;
101- const after = new Date ( ) ;
102-
103- // The result should be a Date object within the expected range
104146 expect ( result ) . toBeInstanceOf ( Date ) ;
105- expect ( result . getTime ( ) ) . toBeGreaterThanOrEqual ( before . getTime ( ) ) ;
106- expect ( result . getTime ( ) ) . toBeLessThanOrEqual ( after . getTime ( ) ) ;
147+ // We can't test exact time, but we can verify it's a valid date
148+ expect ( result . getTime ( ) ) . not . toBeNaN ( ) ;
149+ } ) ;
107150
108- warnSpy . mockRestore ( ) ;
151+ it ( "should return current date in UTC" , ( ) => {
152+ const result = nowInTimeZone ( "UTC" ) ;
153+ expect ( result ) . toBeInstanceOf ( Date ) ;
154+ expect ( result . getTime ( ) ) . not . toBeNaN ( ) ;
109155 } ) ;
110156 } ) ;
111157} ) ;
@@ -130,4 +176,88 @@ describe("Timezone utility functions - edge cases", () => {
130176 expect ( result ) . toBe ( date ) ;
131177 } ) ;
132178 } ) ;
179+
180+ it ( "should handle DST transitions" , ( ) => {
181+ // Test a date during DST (summer)
182+ const summerDate = new Date ( "2024-07-15T12:00:00Z" ) ;
183+ const summerResult = formatInTimeZone (
184+ summerDate ,
185+ "HH:mm" ,
186+ "America/New_York" ,
187+ ) ;
188+ expect ( summerResult ) . toBe ( "08:00" ) ; // EDT (UTC-4)
189+
190+ // Test a date outside DST (winter)
191+ const winterDate = new Date ( "2024-01-15T12:00:00Z" ) ;
192+ const winterResult = formatInTimeZone (
193+ winterDate ,
194+ "HH:mm" ,
195+ "America/New_York" ,
196+ ) ;
197+ expect ( winterResult ) . toBe ( "07:00" ) ; // EST (UTC-5)
198+ } ) ;
199+
200+ it ( "should handle dates at midnight" , ( ) => {
201+ const midnightUtc = new Date ( "2024-06-15T00:00:00Z" ) ;
202+ const result = formatInTimeZone (
203+ midnightUtc ,
204+ "yyyy-MM-dd HH:mm" ,
205+ "America/New_York" ,
206+ ) ;
207+ // Midnight UTC is 8 PM previous day in New York (EDT)
208+ expect ( result ) . toBe ( "2024-06-14 20:00" ) ;
209+ } ) ;
210+
211+ it ( "should handle dates at end of day" , ( ) => {
212+ const endOfDayUtc = new Date ( "2024-06-15T23:59:59Z" ) ;
213+ const result = formatInTimeZone (
214+ endOfDayUtc ,
215+ "yyyy-MM-dd HH:mm" ,
216+ "Asia/Tokyo" ,
217+ ) ;
218+ // 23:59 UTC is 08:59 next day in Tokyo (JST, UTC+9)
219+ expect ( result ) . toBe ( "2024-06-16 08:59" ) ;
220+ } ) ;
221+ } ) ;
222+
223+ describe ( "Timezone utility functions - integration" , ( ) => {
224+ it ( "should correctly round-trip dates through timezone conversions" , ( ) => {
225+ const originalDate = new Date ( "2024-06-15T15:30:00Z" ) ;
226+ const timezones = [
227+ "America/New_York" ,
228+ "America/Los_Angeles" ,
229+ "Europe/London" ,
230+ "Europe/Paris" ,
231+ "Asia/Tokyo" ,
232+ "Australia/Sydney" ,
233+ "UTC" ,
234+ ] ;
235+
236+ timezones . forEach ( ( tz ) => {
237+ const zoned = toZonedTime ( originalDate , tz ) ;
238+ const backToUtc = fromZonedTime ( zoned , tz ) ;
239+ expect ( backToUtc . getTime ( ) ) . toBe ( originalDate . getTime ( ) ) ;
240+ } ) ;
241+ } ) ;
242+
243+ it ( "should format consistently across different timezones" , ( ) => {
244+ const utcDate = new Date ( "2024-06-15T00:00:00Z" ) ;
245+
246+ // All these should represent the same moment in time
247+ const utcFormatted = formatInTimeZone ( utcDate , "yyyy-MM-dd HH:mm" , "UTC" ) ;
248+ const nyFormatted = formatInTimeZone (
249+ utcDate ,
250+ "yyyy-MM-dd HH:mm" ,
251+ "America/New_York" ,
252+ ) ;
253+ const tokyoFormatted = formatInTimeZone (
254+ utcDate ,
255+ "yyyy-MM-dd HH:mm" ,
256+ "Asia/Tokyo" ,
257+ ) ;
258+
259+ expect ( utcFormatted ) . toBe ( "2024-06-15 00:00" ) ;
260+ expect ( nyFormatted ) . toBe ( "2024-06-14 20:00" ) ; // Previous day in NY
261+ expect ( tokyoFormatted ) . toBe ( "2024-06-15 09:00" ) ; // Same day, later in Tokyo
262+ } ) ;
133263} ) ;
0 commit comments