44
44
public class DateLiteral extends Literal {
45
45
public static final String JAVA_DATE_FORMAT = "yyyy-MM-dd" ;
46
46
47
+ public static final Set <Character > punctuations = ImmutableSet .of ('!' , '@' , '#' , '$' , '%' , '^' , '&' , '*' , '(' , ')' ,
48
+ '-' , '+' , '=' , '_' , '{' , '}' , '[' , ']' , '|' , '\\' , ':' , ';' , '"' , '\'' , '<' , '>' , ',' , '.' , '?' , '/' , '~' ,
49
+ '`' );
50
+
47
51
// for cast datetime type to date type.
48
52
private static final LocalDateTime START_OF_A_DAY = LocalDateTime .of (0 , 1 , 1 , 0 , 0 , 0 );
49
53
private static final LocalDateTime END_OF_A_DAY = LocalDateTime .of (9999 , 12 , 31 , 23 , 59 , 59 , 999999000 );
50
54
private static final DateLiteral MIN_DATE = new DateLiteral (0 , 1 , 1 );
51
55
private static final DateLiteral MAX_DATE = new DateLiteral (9999 , 12 , 31 );
52
56
private static final int [] DAYS_IN_MONTH = new int [] {0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 };
53
57
54
- private static final Set <Character > punctuations = ImmutableSet .of ('!' , '@' , '#' , '$' , '%' , '^' , '&' , '*' , '(' , ')' ,
55
- '-' , '+' , '=' , '_' , '{' , '}' , '[' , ']' , '|' , '\\' , ':' , ';' , '"' , '\'' , '<' , '>' , ',' , '.' , '?' , '/' , '~' ,
56
- '`' );
57
-
58
58
protected long year ;
59
59
protected long month ;
60
60
protected long day ;
@@ -145,7 +145,7 @@ private static boolean isPunctuation(char c) {
145
145
return punctuations .contains (c );
146
146
}
147
147
148
- static String normalize (String s ) {
148
+ static Result < String , AnalysisException > normalize (String s ) {
149
149
// merge consecutive space
150
150
if (s .contains (" " )) {
151
151
s = s .replaceAll (" +" , " " );
@@ -208,7 +208,10 @@ static String normalize(String s) {
208
208
sb .append ('0' ).append (c );
209
209
}
210
210
} else {
211
- throw new AnalysisException ("date/datetime literal [" + s + "] is invalid" );
211
+ final String currentString = s ;
212
+ return Result .err (
213
+ () -> new AnalysisException ("date/datetime literal [" + currentString + "] is invalid" )
214
+ );
212
215
}
213
216
i = j ;
214
217
partNumber += 1 ;
@@ -228,7 +231,10 @@ static String normalize(String s) {
228
231
} else if (partNumber > 3 && isPunctuation (c )) {
229
232
sb .append (':' );
230
233
} else {
231
- throw new AnalysisException ("date/datetime literal [" + s + "] is invalid" );
234
+ final String currentString = s ;
235
+ return Result .err (
236
+ () -> new AnalysisException ("date/datetime literal [" + currentString + "] is invalid" )
237
+ );
232
238
}
233
239
} else {
234
240
break ;
@@ -259,15 +265,33 @@ static String normalize(String s) {
259
265
// trim use to remove any blank before zone id or zone offset
260
266
sb .append (s .substring (i ).trim ());
261
267
262
- return sb .toString ();
268
+ return Result . ok ( sb .toString () );
263
269
}
264
270
265
- protected static TemporalAccessor parse (String s ) {
271
+ /** parseDateLiteral */
272
+ public static Result <DateLiteral , AnalysisException > parseDateLiteral (String s ) {
273
+ Result <TemporalAccessor , AnalysisException > parseResult = parseDateTime (s );
274
+ if (parseResult .isError ()) {
275
+ return parseResult .cast ();
276
+ }
277
+ TemporalAccessor dateTime = parseResult .get ();
278
+ int year = DateUtils .getOrDefault (dateTime , ChronoField .YEAR );
279
+ int month = DateUtils .getOrDefault (dateTime , ChronoField .MONTH_OF_YEAR );
280
+ int day = DateUtils .getOrDefault (dateTime , ChronoField .DAY_OF_MONTH );
281
+
282
+ if (checkDatetime (dateTime ) || checkRange (year , month , day ) || checkDate (year , month , day )) {
283
+ return Result .err (() -> new AnalysisException ("date/datetime literal [" + s + "] is out of range" ));
284
+ }
285
+ return Result .ok (new DateLiteral (year , month , day ));
286
+ }
287
+
288
+ /** parseDateTime */
289
+ public static Result <TemporalAccessor , AnalysisException > parseDateTime (String s ) {
266
290
// fast parse '2022-01-01'
267
291
if (s .length () == 10 && s .charAt (4 ) == '-' && s .charAt (7 ) == '-' ) {
268
292
TemporalAccessor date = fastParseDate (s );
269
293
if (date != null ) {
270
- return date ;
294
+ return Result . ok ( date ) ;
271
295
}
272
296
}
273
297
@@ -289,15 +313,20 @@ protected static TemporalAccessor parse(String s) {
289
313
if (!containsPunctuation ) {
290
314
s = normalizeBasic (s );
291
315
// mysql reject "20200219 010101" "200219 010101", can't use ' ' spilt basic date time.
316
+
292
317
if (!s .contains ("T" )) {
293
318
dateTime = DateTimeFormatterUtils .BASIC_FORMATTER_WITHOUT_T .parse (s );
294
319
} else {
295
320
dateTime = DateTimeFormatterUtils .BASIC_DATE_TIME_FORMATTER .parse (s );
296
321
}
297
- return dateTime ;
322
+ return Result . ok ( dateTime ) ;
298
323
}
299
324
300
- s = normalize (s );
325
+ Result <String , AnalysisException > normalizeResult = normalize (s );
326
+ if (normalizeResult .isError ()) {
327
+ return normalizeResult .cast ();
328
+ }
329
+ s = normalizeResult .get ();
301
330
302
331
if (!s .contains (" " )) {
303
332
dateTime = DateTimeFormatterUtils .ZONE_DATE_FORMATTER .parse (s );
@@ -307,32 +336,34 @@ protected static TemporalAccessor parse(String s) {
307
336
308
337
// if Year is not present, throw exception
309
338
if (!dateTime .isSupported (ChronoField .YEAR )) {
310
- throw new AnalysisException ("date/datetime literal [" + originalString + "] is invalid" );
339
+ return Result .err (
340
+ () -> new AnalysisException ("date/datetime literal [" + originalString + "] is invalid" )
341
+ );
311
342
}
312
343
313
- return dateTime ;
344
+ return Result . ok ( dateTime ) ;
314
345
} catch (Exception ex ) {
315
- throw new AnalysisException ("date/datetime literal [" + originalString + "] is invalid" );
346
+ return Result . err (() -> new AnalysisException ("date/datetime literal [" + originalString + "] is invalid" ) );
316
347
}
317
348
}
318
349
319
350
protected void init (String s ) throws AnalysisException {
320
- TemporalAccessor dateTime = parse ( s );
351
+ TemporalAccessor dateTime = parseDateTime ( s ). get ( );
321
352
year = DateUtils .getOrDefault (dateTime , ChronoField .YEAR );
322
353
month = DateUtils .getOrDefault (dateTime , ChronoField .MONTH_OF_YEAR );
323
354
day = DateUtils .getOrDefault (dateTime , ChronoField .DAY_OF_MONTH );
324
355
325
- if (checkDatetime (dateTime ) || checkRange () || checkDate ()) {
356
+ if (checkDatetime (dateTime ) || checkRange (year , month , day ) || checkDate (year , month , day )) {
326
357
throw new AnalysisException ("date/datetime literal [" + s + "] is out of range" );
327
358
}
328
359
}
329
360
330
- protected boolean checkRange () {
361
+ protected static boolean checkRange (long year , long month , long day ) {
331
362
return year > MAX_DATE .getYear () || month > MAX_DATE .getMonth () || day > MAX_DATE .getDay ();
332
363
}
333
364
334
- protected boolean checkDate () {
335
- if (month != 0 && day > DAYS_IN_MONTH [(( int ) month ) ]) {
365
+ protected static boolean checkDate (long year , long month , long day ) {
366
+ if (month != 0 && day > DAYS_IN_MONTH [(int ) month ]) {
336
367
if (month == 2 && day == 29 && (Year .isLeap (year ) && year > 0 )) {
337
368
return false ;
338
369
}
@@ -345,7 +376,7 @@ protected static boolean isDateOutOfRange(LocalDateTime dateTime) {
345
376
return dateTime == null || dateTime .isBefore (START_OF_A_DAY ) || dateTime .isAfter (END_OF_A_DAY );
346
377
}
347
378
348
- private boolean checkDatetime (TemporalAccessor dateTime ) {
379
+ private static boolean checkDatetime (TemporalAccessor dateTime ) {
349
380
return DateUtils .getOrDefault (dateTime , ChronoField .HOUR_OF_DAY ) != 0
350
381
|| DateUtils .getOrDefault (dateTime , ChronoField .MINUTE_OF_HOUR ) != 0
351
382
|| DateUtils .getOrDefault (dateTime , ChronoField .SECOND_OF_MINUTE ) != 0
0 commit comments