@@ -21,6 +21,7 @@ import {
2121 intervalToLocalDateRange ,
2222 localDateRangeToInterval ,
2323 localToUtcDate ,
24+ parseIsoDate ,
2425 utcToLocalDate ,
2526} from './date' ;
2627
@@ -59,4 +60,155 @@ describe('date', () => {
5960 expect ( localDateRangeToInterval ( intervalToLocalDateRange ( interval ) ) ) . toEqual ( interval ) ;
6061 } ) ;
6162 } ) ;
63+
64+ describe ( 'parseIsoDate' , ( ) => {
65+ it ( 'works with year only' , ( ) => {
66+ const result = parseIsoDate ( '2016' ) ;
67+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 0 , 1 , 0 , 0 , 0 , 0 ) ) ) ;
68+ } ) ;
69+
70+ it ( 'works with year-month' , ( ) => {
71+ const result = parseIsoDate ( '2016-06' ) ;
72+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 1 , 0 , 0 , 0 , 0 ) ) ) ;
73+ } ) ;
74+
75+ it ( 'works with date only' , ( ) => {
76+ const result = parseIsoDate ( '2016-06-20' ) ;
77+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 0 , 0 , 0 , 0 ) ) ) ;
78+ } ) ;
79+
80+ it ( 'works with date and hour using T separator' , ( ) => {
81+ const result = parseIsoDate ( '2016-06-20T21' ) ;
82+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 0 , 0 , 0 ) ) ) ;
83+ } ) ;
84+
85+ it ( 'works with date and hour using space separator' , ( ) => {
86+ const result = parseIsoDate ( '2016-06-20 21' ) ;
87+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 0 , 0 , 0 ) ) ) ;
88+ } ) ;
89+
90+ it ( 'works with date, hour, and minute using T separator' , ( ) => {
91+ const result = parseIsoDate ( '2016-06-20T21:31' ) ;
92+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 0 , 0 ) ) ) ;
93+ } ) ;
94+
95+ it ( 'works with date, hour, and minute using space separator' , ( ) => {
96+ const result = parseIsoDate ( '2016-06-20 21:31' ) ;
97+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 0 , 0 ) ) ) ;
98+ } ) ;
99+
100+ it ( 'works with datetime without milliseconds using T separator' , ( ) => {
101+ const result = parseIsoDate ( '2016-06-20T21:31:02' ) ;
102+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 0 ) ) ) ;
103+ } ) ;
104+
105+ it ( 'works with datetime without milliseconds using space separator' , ( ) => {
106+ const result = parseIsoDate ( '2016-06-20 21:31:02' ) ;
107+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 0 ) ) ) ;
108+ } ) ;
109+
110+ it ( 'works with full datetime with milliseconds using T separator' , ( ) => {
111+ const result = parseIsoDate ( '2016-06-20T21:31:02.123' ) ;
112+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 123 ) ) ) ;
113+ } ) ;
114+
115+ it ( 'works with full datetime with milliseconds using space separator' , ( ) => {
116+ const result = parseIsoDate ( '2016-06-20 21:31:02.123' ) ;
117+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 123 ) ) ) ;
118+ } ) ;
119+
120+ it ( 'works with single digit milliseconds' , ( ) => {
121+ const result = parseIsoDate ( '2016-06-20T21:31:02.1' ) ;
122+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 100 ) ) ) ;
123+ } ) ;
124+
125+ it ( 'works with two digit milliseconds' , ( ) => {
126+ const result = parseIsoDate ( '2016-06-20T21:31:02.12' ) ;
127+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 120 ) ) ) ;
128+ } ) ;
129+
130+ it ( 'works with whitespace trimming' , ( ) => {
131+ const result = parseIsoDate ( ' 2016-06-20T21:31:02 ' ) ;
132+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 0 ) ) ) ;
133+ } ) ;
134+
135+ it ( 'works with trailing Z' , ( ) => {
136+ const result = parseIsoDate ( '2016-06-20T21:31:02Z' ) ;
137+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 0 ) ) ) ;
138+ } ) ;
139+
140+ it ( 'works with trailing Z and milliseconds' , ( ) => {
141+ const result = parseIsoDate ( '2016-06-20T21:31:02.123Z' ) ;
142+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 21 , 31 , 2 , 123 ) ) ) ;
143+ } ) ;
144+
145+ it ( 'works with date only and trailing Z' , ( ) => {
146+ const result = parseIsoDate ( '2016-06-20Z' ) ;
147+ expect ( result ) . toEqual ( new Date ( Date . UTC ( 2016 , 5 , 20 , 0 , 0 , 0 , 0 ) ) ) ;
148+ } ) ;
149+
150+ it ( 'throws error for nonsense format with multiple T separators' , ( ) => {
151+ expect ( ( ) => parseIsoDate ( '2016T06T20T21T31T02T000' ) ) . toThrow (
152+ 'Invalid date format: expected ISO 8601 format' ,
153+ ) ;
154+ } ) ;
155+
156+ it ( 'throws error for invalid year below range' , ( ) => {
157+ expect ( ( ) => parseIsoDate ( '0999-06-20' ) ) . toThrow (
158+ 'Invalid year: must be between 1000 and 3999' ,
159+ ) ;
160+ } ) ;
161+
162+ it ( 'throws error for invalid year above range' , ( ) => {
163+ expect ( ( ) => parseIsoDate ( '4000-06-20' ) ) . toThrow (
164+ 'Invalid year: must be between 1000 and 3999' ,
165+ ) ;
166+ } ) ;
167+
168+ it ( 'throws error for invalid month below range' , ( ) => {
169+ expect ( ( ) => parseIsoDate ( '2016-00-20' ) ) . toThrow ( 'Invalid month: must be between 1 and 12' ) ;
170+ } ) ;
171+
172+ it ( 'throws error for invalid month above range' , ( ) => {
173+ expect ( ( ) => parseIsoDate ( '2016-13-20' ) ) . toThrow ( 'Invalid month: must be between 1 and 12' ) ;
174+ } ) ;
175+
176+ it ( 'throws error for invalid day below range' , ( ) => {
177+ expect ( ( ) => parseIsoDate ( '2016-06-00' ) ) . toThrow ( 'Invalid day: must be between 1 and 31' ) ;
178+ } ) ;
179+
180+ it ( 'throws error for invalid day above range' , ( ) => {
181+ expect ( ( ) => parseIsoDate ( '2016-06-32' ) ) . toThrow ( 'Invalid day: must be between 1 and 31' ) ;
182+ } ) ;
183+
184+ it ( 'throws error for invalid hour' , ( ) => {
185+ expect ( ( ) => parseIsoDate ( '2016-06-20 25:00:00' ) ) . toThrow (
186+ 'Invalid hour: must be between 0 and 23' ,
187+ ) ;
188+ } ) ;
189+
190+ it ( 'throws error for invalid minute' , ( ) => {
191+ expect ( ( ) => parseIsoDate ( '2016-06-20 21:60:00' ) ) . toThrow (
192+ 'Invalid minute: must be between 0 and 59' ,
193+ ) ;
194+ } ) ;
195+
196+ it ( 'throws error for invalid second' , ( ) => {
197+ expect ( ( ) => parseIsoDate ( '2016-06-20 21:31:60' ) ) . toThrow (
198+ 'Invalid second: must be between 0 and 59' ,
199+ ) ;
200+ } ) ;
201+
202+ it ( 'throws error for slash separators' , ( ) => {
203+ expect ( ( ) => parseIsoDate ( '2016/06/20' ) ) . toThrow ( 'Invalid date format' ) ;
204+ } ) ;
205+
206+ it ( 'throws error for completely invalid string' , ( ) => {
207+ expect ( ( ) => parseIsoDate ( 'not-a-date' ) ) . toThrow ( 'Invalid date format' ) ;
208+ } ) ;
209+
210+ it ( 'throws error for empty string' , ( ) => {
211+ expect ( ( ) => parseIsoDate ( '' ) ) . toThrow ( 'Invalid date format' ) ;
212+ } ) ;
213+ } ) ;
62214} ) ;
0 commit comments