16
16
17
17
package com .networknt .schema ;
18
18
19
+ import com .ethlo .time .ITU ;
20
+ import com .ethlo .time .LeapSecondException ;
19
21
import com .fasterxml .jackson .databind .JsonNode ;
20
22
import org .slf4j .Logger ;
21
23
import org .slf4j .LoggerFactory ;
22
24
23
- import java .text .ParsePosition ;
24
- import java .text .SimpleDateFormat ;
25
+ import java .time .LocalDate ;
25
26
import java .util .Collections ;
26
27
import java .util .LinkedHashSet ;
27
28
import java .util .Set ;
28
- import java .util .TimeZone ;
29
- import java .util .regex .Matcher ;
30
- import java .util .regex .Pattern ;
31
29
32
30
public class DateTimeValidator extends BaseJsonValidator implements JsonValidator {
33
31
private static final Logger logger = LoggerFactory .getLogger (DateTimeValidator .class );
@@ -38,11 +36,6 @@ public class DateTimeValidator extends BaseJsonValidator implements JsonValidato
38
36
private final String DATE = "date" ;
39
37
private final String DATETIME = "date-time" ;
40
38
41
- private static final Pattern RFC3339_PATTERN = Pattern .compile (
42
- "^(\\ d{4})-(\\ d{2})-(\\ d{2})" // yyyy-MM-dd
43
- + "([Tt](\\ d{2}):(\\ d{2}):(\\ d{2})(\\ .\\ d+)?" // 'T'HH:mm:ss.milliseconds
44
- + "(([Zz])|([+-])(\\ d{2}):(\\ d{2})))?" );
45
-
46
39
public DateTimeValidator (String schemaPath , JsonNode schemaNode , JsonSchema parentSchema , ValidationContext validationContext , String formatName ) {
47
40
super (schemaPath , schemaNode , parentSchema , ValidatorTypeCode .DATETIME , validationContext );
48
41
this .formatName = formatName ;
@@ -66,74 +59,26 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
66
59
}
67
60
68
61
private boolean isLegalDateTime (String string ) {
69
- Matcher matcher = RFC3339_PATTERN .matcher (string );
70
- StringBuilder pattern = new StringBuilder ();
71
- StringBuilder dateTime = new StringBuilder ();
72
- // Validate the format
73
- if (!matcher .matches ()) {
74
- logger .error ("Failed to apply RFC3339 pattern on " + string );
75
- return false ;
62
+ if (formatName .equals (DATE )) {
63
+ return tryParse (() -> LocalDate .parse (string ));
64
+ } else if (formatName .equals (DATETIME )) {
65
+ return tryParse (() -> {
66
+ try {
67
+ ITU .parseDateTime (string );
68
+ } catch (LeapSecondException ignored ) {}
69
+ });
70
+ } else {
71
+ throw new IllegalStateException ("Unknown format: " + formatName );
76
72
}
77
- // Validate the date/time content
78
- String year = matcher .group (1 );
79
- String month = matcher .group (2 );
80
- String day = matcher .group (3 );
81
- dateTime .append (year ).append ('-' ).append (month ).append ('-' ).append (day );
82
- pattern .append ("yyyy-MM-dd" );
83
-
84
- boolean isTimeGiven = matcher .group (4 ) != null ;
85
- boolean isOffsetZuluTime = matcher .group (10 ) != null ;
86
- String hour = null ;
87
- String minute = null ;
88
- String second = null ;
89
- String milliseconds = null ;
90
- String timeShiftSign = null ;
91
- String timeShiftHour = null ;
92
- String timeShiftMinute = null ;
73
+ }
93
74
94
- if (!isTimeGiven && DATETIME .equals (formatName ) || (isTimeGiven && DATE .equals (formatName ))) {
95
- logger .error ("The supplied date/time format type does not match the specification, expected: " + formatName );
75
+ private boolean tryParse (Runnable parser ) {
76
+ try {
77
+ parser .run ();
78
+ return true ;
79
+ } catch (Exception ex ) {
80
+ logger .error ("Invalid " + formatName + ": " + ex .getMessage ());
96
81
return false ;
97
82
}
98
-
99
- if (isTimeGiven ) {
100
- hour = matcher .group (5 );
101
- minute = matcher .group (6 );
102
- second = matcher .group (7 );
103
- dateTime .append ('T' ).append (hour ).append (':' ).append (minute ).append (':' ).append (second );
104
- pattern .append ("'T'HH:mm:ss" );
105
- if (matcher .group (8 ) != null ) {
106
- // Normalize milliseconds to 3-length digit
107
- milliseconds = matcher .group (8 );
108
- if (milliseconds .length () > 4 ) {
109
- milliseconds = milliseconds .substring (0 , 4 );
110
- } else {
111
- while (milliseconds .length () < 4 ) {
112
- milliseconds += "0" ;
113
- }
114
- }
115
- dateTime .append (milliseconds );
116
- pattern .append (".SSS" );
117
- }
118
-
119
- if (isOffsetZuluTime ) {
120
- dateTime .append ('Z' );
121
- pattern .append ("'Z'" );
122
- } else {
123
- timeShiftSign = matcher .group (11 );
124
- timeShiftHour = matcher .group (12 );
125
- timeShiftMinute = matcher .group (13 );
126
- dateTime .append (timeShiftSign ).append (timeShiftHour ).append (':' ).append (timeShiftMinute );
127
- pattern .append ("XXX" );
128
- }
129
- }
130
- return validateDateTime (dateTime .toString (), pattern .toString ());
131
- }
132
-
133
- private boolean validateDateTime (String dateTime , String pattern ) {
134
- SimpleDateFormat sdf = new SimpleDateFormat (pattern );
135
- sdf .setLenient (false );
136
- sdf .setTimeZone (TimeZone .getTimeZone ("UTC" ));
137
- return sdf .parse (dateTime , new ParsePosition (0 )) != null ;
138
83
}
139
84
}
0 commit comments