11// eslint-disable-next-line -- TODO: move to date-fns
22import moment from 'moment' ;
3- import { buildFieldValue } from './date' ;
3+ import { buildFieldValue , computeLocalZoneOffset , userInputFromDatetime } from './date' ;
4+ import { defaultZoneOffset } from './zoneOffsets' ;
45
56describe ( 'date utils' , ( ) => {
67 describe ( 'buildFieldValue' , ( ) => {
@@ -15,7 +16,7 @@ describe('date utils', () => {
1516 } ,
1617 usesTimezone : true ,
1718 usesTime : true ,
18- } )
19+ } ) ,
1920 ) . toEqual ( {
2021 invalid : false ,
2122 valid : '2018-02-02T17:00+03:00' ,
@@ -31,7 +32,7 @@ describe('date utils', () => {
3132 } ,
3233 usesTimezone : true ,
3334 usesTime : true ,
34- } )
35+ } ) ,
3536 ) . toEqual ( {
3637 invalid : false ,
3738 valid : '2015-01-14T05:00-05:00' ,
@@ -47,11 +48,73 @@ describe('date utils', () => {
4748 } ,
4849 usesTimezone : true ,
4950 usesTime : true ,
50- } )
51+ } ) ,
5152 ) . toEqual ( {
5253 invalid : false ,
5354 valid : '2015-01-14T17:00-05:00' ,
5455 } ) ;
5556 } ) ;
57+
58+ it ( 'uses the DST-aware local offset when utcOffset is "Local time"' , ( ) => {
59+ const date = moment ( '2025-07-04' ) ;
60+ const expectedOffset = computeLocalZoneOffset ( date ) ;
61+ const result = buildFieldValue ( {
62+ data : {
63+ date,
64+ time : '20:00' ,
65+ ampm : 'PM' ,
66+ utcOffset : defaultZoneOffset ,
67+ } ,
68+ usesTimezone : true ,
69+ usesTime : true ,
70+ } ) ;
71+ expect ( result ) . toEqual ( {
72+ invalid : false ,
73+ valid : `2025-07-04T20:00${ expectedOffset } ` ,
74+ } ) ;
75+ } ) ;
76+ } ) ;
77+
78+ describe ( 'computeLocalZoneOffset' , ( ) => {
79+ it ( 'returns a valid UTC offset string' , ( ) => {
80+ const offset = computeLocalZoneOffset ( moment ( '2025-07-04' ) ) ;
81+ expect ( offset ) . toMatch ( / ^ [ + - ] \d { 2 } : \d { 2 } $ / ) ;
82+ } ) ;
83+
84+ it ( 'is not affected by a fixed UTC offset on the input moment' , ( ) => {
85+ // Same calendar date/time stored with two different explicit offsets
86+ const withPositiveOffset = moment . parseZone ( '2025-07-04T12:00+05:30' ) ;
87+ const withNegativeOffset = moment . parseZone ( '2025-07-04T12:00-08:00' ) ;
88+ // Both should produce the same local offset for 2025-07-04 at 12:00
89+ expect ( computeLocalZoneOffset ( withPositiveOffset ) ) . toBe (
90+ computeLocalZoneOffset ( withNegativeOffset ) ,
91+ ) ;
92+ } ) ;
93+ } ) ;
94+
95+ describe ( 'userInputFromDatetime' , ( ) => {
96+ it ( 'detects a value stored in the local timezone and returns "Local time"' , ( ) => {
97+ const localOffset = computeLocalZoneOffset ( moment ( '2025-07-04' ) ) ;
98+ const result = userInputFromDatetime ( {
99+ value : `2025-07-04T12:00${ localOffset } ` ,
100+ uses12hClock : false ,
101+ } ) ;
102+ expect ( result . utcOffset ) . toBe ( defaultZoneOffset ) ;
103+ } ) ;
104+
105+ it ( 'preserves an explicit offset that differs from the local timezone' , ( ) => {
106+ const localOffset = computeLocalZoneOffset ( moment ( '2025-07-04' ) ) ;
107+ const explicitOffset = localOffset === '+05:30' ? '+03:00' : '+05:30' ;
108+ const result = userInputFromDatetime ( {
109+ value : `2025-07-04T12:00${ explicitOffset } ` ,
110+ uses12hClock : false ,
111+ } ) ;
112+ expect ( result . utcOffset ) . toBe ( explicitOffset ) ;
113+ } ) ;
114+
115+ it ( 'returns "Local time" as the default when no value is provided' , ( ) => {
116+ const result = userInputFromDatetime ( { value : null , uses12hClock : false } ) ;
117+ expect ( result . utcOffset ) . toBe ( defaultZoneOffset ) ;
118+ } ) ;
56119 } ) ;
57120} ) ;
0 commit comments