@@ -18,6 +18,28 @@ const enum FormatDesc {
18
18
TwoDigits = '2-digit'
19
19
}
20
20
21
+ export interface DateTimeValue {
22
+ state : DateState ;
23
+ value : Date ;
24
+ }
25
+
26
+ export enum DatePart {
27
+ Date = 'date' ,
28
+ Month = 'month' ,
29
+ Year = 'year' ,
30
+ Hours = 'hours' ,
31
+ Minutes = 'minutes' ,
32
+ Seconds = 'seconds' ,
33
+ AmPm = 'ampm'
34
+ }
35
+
36
+ export interface DatePartInfo {
37
+ type : DatePart ;
38
+ start : number ;
39
+ end : number ;
40
+ format : string ;
41
+ }
42
+
21
43
/**
22
44
*@hidden
23
45
*/
@@ -27,6 +49,9 @@ const enum DateChars {
27
49
DayChar = 'd'
28
50
}
29
51
52
+ const TimeCharsArr = [ 'h' , 'H' , 'm' , 's' , 'S' , 't' , 'T' ] ;
53
+ const DateCharsArr = [ 'd' , 'D' , 'M' , 'y' , 'Y' ] ;
54
+
30
55
/**
31
56
*@hidden
32
57
*/
@@ -37,7 +62,7 @@ const enum DateParts {
37
62
}
38
63
39
64
/**
40
- *@hidden
65
+ * @hidden
41
66
*/
42
67
export abstract class DatePickerUtil {
43
68
private static readonly SHORT_DATE_MASK = 'MM/dd/yy' ;
@@ -46,6 +71,244 @@ export abstract class DatePickerUtil {
46
71
private static readonly PROMPT_CHAR = '_' ;
47
72
private static readonly DEFAULT_LOCALE = 'en' ;
48
73
74
+ public static parseDateTimeArray ( dateTimeParts : DatePartInfo [ ] , inputData : string ) : DateTimeValue {
75
+ const parts : { [ key in DatePart ] : number } = { } as any ;
76
+ dateTimeParts . forEach ( dp => {
77
+ let value = parseInt ( this . getCleanVal ( inputData , dp ) , 10 ) ;
78
+ if ( ! value ) {
79
+ value = dp . type === DatePart . Date || dp . type === DatePart . Month ? 1 : 0 ;
80
+ }
81
+ parts [ dp . type ] = value ;
82
+ } ) ;
83
+
84
+ if ( parts [ DatePart . Month ] < 1 || 12 < parts [ DatePart . Month ] ) {
85
+ return { state : DateState . Invalid , value : new Date ( NaN ) } ;
86
+ }
87
+
88
+ // TODO: Century threshold
89
+ if ( parts [ DatePart . Year ] < 50 ) {
90
+ parts [ DatePart . Year ] += 2000 ;
91
+ }
92
+
93
+ if ( parts [ DatePart . Date ] > DatePickerUtil . daysInMonth ( parts [ DatePart . Year ] , parts [ DatePart . Month ] ) ) {
94
+ return { state : DateState . Invalid , value : new Date ( NaN ) } ;
95
+ }
96
+
97
+ if ( parts [ DatePart . Hours ] > 23 || parts [ DatePart . Minutes ] > 59 || parts [ DatePart . Seconds ] > 59 ) {
98
+ return { state : DateState . Invalid , value : new Date ( NaN ) } ;
99
+ }
100
+
101
+ return {
102
+ state : DateState . Valid ,
103
+ value : new Date (
104
+ parts [ DatePart . Year ] || 2000 ,
105
+ parts [ DatePart . Month ] - 1 || 0 ,
106
+ parts [ DatePart . Date ] || 1 ,
107
+ parts [ DatePart . Hours ] || 0 ,
108
+ parts [ DatePart . Minutes ] || 0 ,
109
+ parts [ DatePart . Seconds ] || 0
110
+ )
111
+ } ;
112
+ }
113
+
114
+ public static parseDateTimeFormat ( mask : string , locale : string = DatePickerUtil . DEFAULT_LOCALE ) : DatePartInfo [ ] {
115
+ let format = DatePickerUtil . setInputFormat ( mask ) ;
116
+ let dateTimeData : DatePartInfo [ ] = [ ] ;
117
+ if ( ! format && ! isIE ( ) ) {
118
+ dateTimeData = DatePickerUtil . getDefaultLocaleMask ( locale ) ;
119
+ } else {
120
+ format = ( format ) ? format : DatePickerUtil . SHORT_DATE_MASK ;
121
+ const formatArray = Array . from ( format ) ;
122
+ for ( let i = 0 ; i < formatArray . length ; i ++ ) {
123
+ const datePartRange = this . getDatePartInfoRange ( formatArray [ i ] , format , i ) ;
124
+ const dateTimeInfo = {
125
+ type : DatePickerUtil . determineDatePart ( formatArray [ i ] ) ,
126
+ start : datePartRange . start ,
127
+ end : datePartRange . end ,
128
+ format : mask . match ( new RegExp ( `${ format [ i ] } +` , 'g' ) ) [ 0 ] ,
129
+ } ;
130
+ while ( DatePickerUtil . isDateOrTimeChar ( formatArray [ i ] ) ) {
131
+ if ( dateTimeData . indexOf ( dateTimeInfo ) === - 1 ) {
132
+ dateTimeData . push ( dateTimeInfo ) ;
133
+ }
134
+ i ++ ;
135
+ }
136
+ }
137
+ }
138
+
139
+ return dateTimeData ;
140
+ }
141
+
142
+ public static setInputFormat ( format : string ) : string {
143
+ if ( ! format ) { return '' ; }
144
+ let chars = '' ;
145
+ let newFormat = '' ;
146
+ for ( let i = 0 ; ; i ++ ) {
147
+ while ( DatePickerUtil . isDateOrTimeChar ( format [ i ] ) ) {
148
+ chars += format [ i ] ;
149
+ i ++ ;
150
+ }
151
+ const datePartType = DatePickerUtil . determineDatePart ( chars [ 0 ] ) ;
152
+ if ( datePartType !== DatePart . Year ) {
153
+ newFormat += chars [ 0 ] . repeat ( 2 ) ;
154
+ } else {
155
+ newFormat += chars ;
156
+ }
157
+
158
+ if ( i >= format . length ) { break ; }
159
+
160
+ if ( ! DatePickerUtil . isDateOrTimeChar ( format [ i ] ) ) {
161
+ newFormat += format [ i ] ;
162
+ }
163
+ chars = '' ;
164
+ }
165
+
166
+ return newFormat ;
167
+ }
168
+
169
+ public static isDateOrTimeChar ( char : string ) : boolean {
170
+ return TimeCharsArr . indexOf ( char ) !== - 1 || DateCharsArr . indexOf ( char ) !== - 1 ;
171
+ }
172
+
173
+ public static calculateDateOnSpin ( delta : number , newDate : Date , currentDate : Date , isSpinLoop : boolean ) : Date {
174
+ newDate = new Date ( newDate . setDate ( newDate . getDate ( ) + delta ) ) ;
175
+ if ( isSpinLoop ) {
176
+ if ( currentDate . getMonth ( ) > newDate . getMonth ( ) ) {
177
+ return new Date ( currentDate . getFullYear ( ) , currentDate . getMonth ( ) + 1 , 0 ) ; // add delta instead of 1?
178
+ } else if ( currentDate . getMonth ( ) < newDate . getMonth ( ) ) {
179
+ return new Date ( currentDate . setDate ( 1 ) ) ;
180
+ }
181
+ }
182
+ if ( currentDate . getMonth ( ) === newDate . getMonth ( ) ) {
183
+ return newDate ;
184
+ }
185
+
186
+ return currentDate ;
187
+ }
188
+
189
+ public static calculateMonthOnSpin ( delta : number , newDate : Date , currentDate : Date , isSpinLoop : boolean ) : Date {
190
+ const maxDate = DatePickerUtil . daysInMonth ( currentDate . getFullYear ( ) , newDate . getMonth ( ) + 1 + delta ) ;
191
+ if ( newDate . getDate ( ) > maxDate ) {
192
+ newDate . setDate ( maxDate ) ;
193
+ }
194
+ newDate = new Date ( newDate . setMonth ( newDate . getMonth ( ) + delta ) ) ;
195
+ if ( isSpinLoop ) {
196
+ if ( currentDate . getFullYear ( ) < newDate . getFullYear ( ) ) {
197
+ return new Date ( currentDate . setMonth ( 0 ) ) ;
198
+ } else if ( currentDate . getFullYear ( ) > newDate . getFullYear ( ) ) {
199
+ return new Date ( currentDate . setMonth ( 11 ) ) ;
200
+ }
201
+ }
202
+ if ( currentDate . getFullYear ( ) === newDate . getFullYear ( ) ) {
203
+ return newDate ;
204
+ }
205
+
206
+ return currentDate ;
207
+ }
208
+
209
+ public static calculateHoursOnSpin ( delta : number , newDate : Date , currentDate : Date , isSpinLoop : boolean ) : Date {
210
+ newDate = new Date ( newDate . setHours ( newDate . getHours ( ) + delta ) ) ;
211
+ if ( isSpinLoop ) {
212
+ if ( newDate . getDate ( ) > currentDate . getDate ( ) ) {
213
+ return new Date ( currentDate . setHours ( 0 ) ) ;
214
+ } else if ( newDate . getDate ( ) < currentDate . getDate ( ) ) {
215
+ return new Date ( currentDate . setHours ( 23 ) ) ;
216
+ }
217
+ }
218
+ if ( currentDate . getDate ( ) === newDate . getDate ( ) ) {
219
+ return newDate ;
220
+ }
221
+
222
+ return currentDate ;
223
+ }
224
+
225
+ public static calculateMinutesOnSpin ( delta : number , newDate : Date , currentDate : Date , isSpinLoop : boolean ) : Date {
226
+ newDate = new Date ( newDate . setMinutes ( newDate . getMinutes ( ) + delta ) ) ;
227
+ if ( isSpinLoop ) {
228
+ if ( newDate . getHours ( ) > currentDate . getHours ( ) ) {
229
+ return new Date ( currentDate . setMinutes ( 0 ) ) ;
230
+ } else if ( newDate . getHours ( ) < currentDate . getHours ( ) ) {
231
+ return new Date ( currentDate . setMinutes ( 59 ) ) ;
232
+ }
233
+ }
234
+
235
+ if ( currentDate . getHours ( ) === newDate . getHours ( ) ) {
236
+ return newDate ;
237
+ }
238
+
239
+ return currentDate ;
240
+ }
241
+
242
+ public static calculateSecondsOnSpin ( delta : number , newDate : Date , currentDate : Date , isSpinLoop : boolean ) : Date {
243
+ newDate = new Date ( newDate . setSeconds ( newDate . getSeconds ( ) + delta ) ) ;
244
+ if ( isSpinLoop ) {
245
+ if ( newDate . getMinutes ( ) > currentDate . getMinutes ( ) ) {
246
+ return new Date ( currentDate . setSeconds ( 0 ) ) ;
247
+ } else if ( newDate . getMinutes ( ) < currentDate . getMinutes ( ) ) {
248
+ return new Date ( currentDate . setSeconds ( 59 ) ) ;
249
+ }
250
+ }
251
+ if ( currentDate . getMinutes ( ) === newDate . getMinutes ( ) ) {
252
+ return newDate ;
253
+ }
254
+
255
+ return currentDate ;
256
+ }
257
+
258
+ public static calculateAmPmOnSpin ( newDate : Date , currentDate : Date , amPmFromMask : string ) {
259
+ switch ( amPmFromMask ) {
260
+ case 'AM' :
261
+ newDate = new Date ( newDate . setHours ( newDate . getHours ( ) + 12 * 1 ) ) ;
262
+ break ;
263
+ case 'PM' :
264
+ newDate = new Date ( newDate . setHours ( newDate . getHours ( ) + 12 * - 1 ) ) ;
265
+ break ;
266
+ }
267
+ if ( newDate . getDate ( ) !== currentDate . getDate ( ) ) {
268
+ return currentDate ;
269
+ }
270
+
271
+ return newDate ;
272
+ }
273
+
274
+ private static getCleanVal ( inputData : string , datePart : DatePartInfo ) : string {
275
+ return DatePickerUtil . trimUnderlines ( inputData . substring ( datePart . start , datePart . end ) ) ;
276
+ }
277
+
278
+ private static getDatePartInfoRange ( datePartChars : string , mask : string , index : number ) : any {
279
+ const start = mask . indexOf ( datePartChars , index ) ;
280
+ let end = start ;
281
+ while ( this . isDateOrTimeChar ( mask [ end ] ) ) {
282
+ end ++ ;
283
+ }
284
+
285
+ return { start, end } ;
286
+ }
287
+
288
+ private static determineDatePart ( char : string ) : DatePart {
289
+ switch ( char ) {
290
+ case 'd' :
291
+ case 'D' :
292
+ return DatePart . Date ;
293
+ case 'M' :
294
+ return DatePart . Month ;
295
+ case 'y' :
296
+ case 'Y' :
297
+ return DatePart . Year ;
298
+ case 'h' :
299
+ case 'H' :
300
+ return DatePart . Hours ;
301
+ case 'm' :
302
+ return DatePart . Minutes ;
303
+ case 's' :
304
+ case 'S' :
305
+ return DatePart . Seconds ;
306
+ case 't' :
307
+ case 'T' :
308
+ return DatePart . AmPm ;
309
+ }
310
+ }
311
+
49
312
/**
50
313
* This method generates date parts structure based on editor mask and locale.
51
314
* @param maskValue: string
@@ -169,12 +432,12 @@ export abstract class DatePickerUtil {
169
432
return mask . join ( '' ) ;
170
433
}
171
434
/**
172
- * This method parses an input string base on date parts and returns a date and its validation state.
173
- * @param dateFormatParts
174
- * @param prevDateValue
175
- * @param inputValue
176
- * @returns object containing a date and its validation state
177
- */
435
+ * This method parses an input string base on date parts and returns a date and its validation state.
436
+ * @param dateFormatParts
437
+ * @param prevDateValue
438
+ * @param inputValue
439
+ * @returns object containing a date and its validation state
440
+ */
178
441
public static parseDateArray ( dateFormatParts : any [ ] , prevDateValue : Date , inputValue : string ) : any {
179
442
const dayStr = DatePickerUtil . getDayValueFromInput ( dateFormatParts , inputValue ) ;
180
443
const monthStr = DatePickerUtil . getMonthValueFromInput ( dateFormatParts , inputValue ) ;
@@ -339,6 +602,10 @@ export abstract class DatePickerUtil {
339
602
return '' ;
340
603
}
341
604
605
+ public static daysInMonth ( fullYear : number , month : number ) : number {
606
+ return new Date ( fullYear , month , 0 ) . getDate ( ) ;
607
+ }
608
+
342
609
private static getYearFormatType ( format : string ) : string {
343
610
switch ( format . match ( new RegExp ( DateChars . YearChar , 'g' ) ) . length ) {
344
611
case 1 : {
@@ -464,10 +731,6 @@ export abstract class DatePickerUtil {
464
731
return { min : minValue , max : maxValue } ;
465
732
}
466
733
467
- private static daysInMonth ( fullYear : number , month : number ) : number {
468
- return new Date ( fullYear , month , 0 ) . getDate ( ) ;
469
- }
470
-
471
734
private static getDateValueFromInput ( dateFormatParts : any [ ] , type : DateParts , inputValue : string , trim : boolean = true ) : string {
472
735
const partPosition = DatePickerUtil . getDateFormatPart ( dateFormatParts , type ) . position ;
473
736
const result = inputValue . substring ( partPosition [ 0 ] , partPosition [ 1 ] ) ;
0 commit comments