55import java .time .format .DateTimeParseException ;
66import java .util .regex .Pattern ;
77
8+ import static java .util .regex .Pattern .CASE_INSENSITIVE ;
9+
810public class Format {
911
1012 public static boolean fastFormat (String format , String value ) {
@@ -70,7 +72,7 @@ private static boolean testRelativeJsonPointer(String value) {
7072 }
7173
7274 private static final Pattern JSON_POINTER_URI_FRAGMENT = Pattern .compile ("^#(?:\\ /(?:[a-z0-9_\\ -.!$&'()*+,;" +
73- ":=@]|%[0-9a-f]{2}|~0|~1)*)*$" , Pattern . CASE_INSENSITIVE );
75+ ":=@]|%[0-9a-f]{2}|~0|~1)*)*$" , CASE_INSENSITIVE );
7476
7577 private static boolean testJsonPointerUriFragment (String value ) {
7678 return JSON_POINTER_URI_FRAGMENT .matcher (value ).find ();
@@ -82,7 +84,7 @@ private static boolean testJsonPointer(String value) {
8284 return JSON_POINTER .matcher (value ).find ();
8385 }
8486
85- private static final Pattern UUID = Pattern .compile ("^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$" , Pattern . CASE_INSENSITIVE );
87+ private static final Pattern UUID = Pattern .compile ("^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$" , CASE_INSENSITIVE );
8688
8789 private static boolean testUuid (String value ) {
8890 return UUID .matcher (value ).find ();
@@ -103,7 +105,7 @@ private static boolean testRegex(String value) {
103105 }
104106
105107 // optimized http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
106- private static final Pattern IPV6 = Pattern .compile ("^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:)))$" , Pattern .CASE_INSENSITIVE );
108+ private static final Pattern IPV6 = Pattern .compile ("^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)(\\ .(25[0-5]|2[0-4]\\ d|1\\ d\\ d|[1-9]?\\ d)){3}))|:)))$" , CASE_INSENSITIVE );
107109
108110 private static boolean testIpv6 (String value ) {
109111 return IPV6 .matcher (value ).find ();
@@ -116,15 +118,15 @@ private static boolean testIpv4(String value) {
116118 return IPV4 .matcher (value ).find ();
117119 }
118120
119- private static final Pattern HOSTNAME = Pattern .compile ("^(?=.{1,253}\\ .?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\ .[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\\ .?$" , Pattern . CASE_INSENSITIVE );
121+ private static final Pattern HOSTNAME = Pattern .compile ("^(?=.{1,253}\\ .?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\ .[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\\ .?$" , CASE_INSENSITIVE );
120122
121123 private static boolean testHostname (String value ) {
122124 return HOSTNAME .matcher (value ).find ();
123125 }
124126
125- private static final Pattern EMAIL_HOST = Pattern .compile ("^[a-z0-9.-]+$" , Pattern . CASE_INSENSITIVE );
126- private static final Pattern EMAIL_NAME = Pattern .compile ("^[a-z0-9.!#$%&'*+/=?^_`\\ {|\\ }~-]+$" , Pattern . CASE_INSENSITIVE );
127- private static final Pattern EMAIL_HOST_PART = Pattern .compile ("^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$" , Pattern . CASE_INSENSITIVE );
127+ private static final Pattern EMAIL_HOST = Pattern .compile ("^[a-z0-9.-]+$" , CASE_INSENSITIVE );
128+ private static final Pattern EMAIL_NAME = Pattern .compile ("^[a-z0-9.!#$%&'*+/=?^_`\\ {|\\ }~-]+$" , CASE_INSENSITIVE );
129+ private static final Pattern EMAIL_HOST_PART = Pattern .compile ("^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$" , CASE_INSENSITIVE );
128130
129131 // https://github.com/ExodusMovement/schemasafe/blob/master/src/formats.js
130132 private static boolean testEmail (String value ) {
@@ -164,27 +166,27 @@ private static boolean testEmail(String value, Pattern emailHostMatcher, Pattern
164166
165167 // For the source: https://gist.github.com/dperini/729294
166168 // For test cases: https://mathiasbynens.be/demo/url-regex
167- private static final Pattern URL_ = Pattern .compile ("^(?:(?:https?|ftp):\\ /\\ /)(?:\\ S+(?::\\ S*)?@)?(?:(?!10(?:\\ .\\ d{1,3}){3})(?!127(?:\\ .\\ d{1,3}){3})(?!169\\ .254(?:\\ .\\ d{1,3}){2})(?!192\\ .168(?:\\ .\\ d{1,3}){2})(?!172\\ .(?:1[6-9]|2\\ d|3[0-1])(?:\\ .\\ d{1,3}){2})(?:[1-9]\\ d?|1\\ d\\ d|2[01]\\ d|22[0-3])(?:\\ .(?:1?\\ d{1,2}|2[0-4]\\ d|25[0-5])){2}(?:\\ .(?:[1-9]\\ d?|1\\ d\\ d|2[0-4]\\ d|25[0-4]))|(?:(?:[a-z\\ u00a1-\\ uffff0-9]+-?)*[a-z\\ u00a1-\\ uffff0-9]+)(?:\\ .(?:[a-z\\ u00a1-\\ uffff0-9]+-?)*[a-z\\ u00a1-\\ uffff0-9]+)*(?:\\ .(?:[a-z\\ u00a1-\\ uffff]{2,})))(?::\\ d{2,5})?(?:\\ /[^\\ s]*)?$" , Pattern . CASE_INSENSITIVE | Pattern .UNICODE_CASE );
169+ private static final Pattern URL_ = Pattern .compile ("^(?:(?:https?|ftp):\\ /\\ /)(?:\\ S+(?::\\ S*)?@)?(?:(?!10(?:\\ .\\ d{1,3}){3})(?!127(?:\\ .\\ d{1,3}){3})(?!169\\ .254(?:\\ .\\ d{1,3}){2})(?!192\\ .168(?:\\ .\\ d{1,3}){2})(?!172\\ .(?:1[6-9]|2\\ d|3[0-1])(?:\\ .\\ d{1,3}){2})(?:[1-9]\\ d?|1\\ d\\ d|2[01]\\ d|22[0-3])(?:\\ .(?:1?\\ d{1,2}|2[0-4]\\ d|25[0-5])){2}(?:\\ .(?:[1-9]\\ d?|1\\ d\\ d|2[0-4]\\ d|25[0-4]))|(?:(?:[a-z\\ u00a1-\\ uffff0-9]+-?)*[a-z\\ u00a1-\\ uffff0-9]+)(?:\\ .(?:[a-z\\ u00a1-\\ uffff0-9]+-?)*[a-z\\ u00a1-\\ uffff0-9]+)*(?:\\ .(?:[a-z\\ u00a1-\\ uffff]{2,})))(?::\\ d{2,5})?(?:\\ /[^\\ s]*)?$" , CASE_INSENSITIVE | Pattern .UNICODE_CASE );
168170
169171 private static boolean testUrl (String value ) {
170172 return URL_ .matcher (value ).find ();
171173 }
172174
173175 // uri-template: https://tools.ietf.org/html/rfc6570
174- private static final Pattern URITEMPLATE = Pattern .compile ("^(?:(?:[^\\ x00-\\ x20\" '<>%\\ \\ ^`\\ {|\\ }]|%[0-9a-f]{2})|\\ {[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\\ *)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\\ *)?)*\\ })*$" , Pattern . CASE_INSENSITIVE );
176+ private static final Pattern URITEMPLATE = Pattern .compile ("^(?:(?:[^\\ x00-\\ x20\" '<>%\\ \\ ^`\\ {|\\ }]|%[0-9a-f]{2})|\\ {[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\\ *)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\\ *)?)*\\ })*$" , CASE_INSENSITIVE );
175177
176178 private static boolean testUriTemplate (String value ) {
177179 return URITEMPLATE .matcher (value ).find ();
178180 }
179181
180- private static final Pattern URIREF = Pattern .compile ("^(?:[a-z][a-z0-9+\\ -.]*:)?(?:\\ /?\\ /(?:(?:[a-z0-9\\ -._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\\ [(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)\\ .){3}(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\\ .[a-z0-9\\ -._~!$&'()*+,;=:]+)\\ ]|(?:(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)\\ .){3}(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)|(?:[a-z0-9\\ -._~!$&'\" ()*+,;=]|%[0-9a-f]{2})*)(?::\\ d*)?(?:\\ /(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})*)*|\\ /(?:(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})+(?:\\ /(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})+(?:\\ /(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\\ ?(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@/?]|%[0-9a-f]{2})*)?$" , Pattern .CASE_INSENSITIVE );
182+ private static final Pattern URIREF = Pattern .compile ("^(?:[a-z][a-z0-9+\\ -.]*:)?(?:\\ /?\\ /(?:(?:[a-z0-9\\ -._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\\ [(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)\\ .){3}(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\\ .[a-z0-9\\ -._~!$&'()*+,;=:]+)\\ ]|(?:(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)\\ .){3}(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)|(?:[a-z0-9\\ -._~!$&'\" ()*+,;=]|%[0-9a-f]{2})*)(?::\\ d*)?(?:\\ /(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})*)*|\\ /(?:(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})+(?:\\ /(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})+(?:\\ /(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\\ ?(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\\ -._~!$&'\" ()*+,;=:@/?]|%[0-9a-f]{2})*)?$" , CASE_INSENSITIVE );
181183
182184 private static boolean testUriReference (String value ) {
183185 return URIREF .matcher (value ).find ();
184186 }
185187
186188 private static final Pattern NOT_URI_FRAGMENT = Pattern .compile ("\\ /|:" );
187- private static final Pattern URI_PATTERN = Pattern .compile ("^(?:[a-z][a-z0-9+\\ -.]*:)(?:\\ /?\\ /(?:(?:[a-z0-9\\ -._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\\ [(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)\\ .){3}(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\\ .[a-z0-9\\ -._~!$&'()*+,;=:]+)\\ ]|(?:(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)\\ .){3}(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)|(?:[a-z0-9\\ -._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\\ d*)?(?:\\ /(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\\ /(?:(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\\ /(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\\ /(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\\ ?(?:[a-z0-9\\ -._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\\ -._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$" , Pattern .CASE_INSENSITIVE );
189+ private static final Pattern URI_PATTERN = Pattern .compile ("^(?:[a-z][a-z0-9+\\ -.]*:)(?:\\ /?\\ /(?:(?:[a-z0-9\\ -._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\\ [(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)\\ .){3}(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\\ .[a-z0-9\\ -._~!$&'()*+,;=:]+)\\ ]|(?:(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)\\ .){3}(?:25[0-5]|2[0-4]\\ d|[01]?\\ d\\ d?)|(?:[a-z0-9\\ -._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\\ d*)?(?:\\ /(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\\ /(?:(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\\ /(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\\ /(?:[a-z0-9\\ -._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\\ ?(?:[a-z0-9\\ -._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\\ -._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$" , CASE_INSENSITIVE );
188190
189191 private static boolean testUri (String value ) {
190192 // http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "."
@@ -203,17 +205,30 @@ private static boolean testDuration(String value) {
203205 DURATIION_C .matcher (value ).find ()));
204206 }
205207
206- private static final Pattern FASTDATETIME = Pattern .compile ("^\\ d\\ d\\ d\\ d-[0-1]\\ d-[0-3]\\ d[t\\ s](?:[0-2]\\ d:[0-5]\\ d:[0-5]\\ d|23:59:60)(?:\\ .\\ d+)?(?:z|[+-]\\ d\\ d(?::?\\ d\\ d)?)$" , Pattern . CASE_INSENSITIVE );
208+ private static final Pattern FASTDATETIME = Pattern .compile ("^\\ d\\ d\\ d\\ d-[0-1]\\ d-[0-3]\\ d[t\\ s](?:[0-2]\\ d:[0-5]\\ d:[0-5]\\ d|23:59:60)(?:\\ .\\ d+)?(?:z? |[+-]\\ d\\ d(?::?\\ d\\ d)?)$" , CASE_INSENSITIVE );
207209
208210 private static boolean testDateTime (String value ) {
209- return FASTDATETIME .matcher (value ).find ();
211+ if (!FASTDATETIME .matcher (value ).find ()) {
212+ return false ;
213+ }
214+ return validateISOTime (DateTimeFormatter .ISO_DATE_TIME , value );
210215 }
211216
212217 // date-time: http://tools.ietf.org/html/rfc3339#section-5.6
213- private static final Pattern FASTTIME = Pattern .compile ("^(?:[0-2]\\ d:[0-5]\\ d:[0-5]\\ d|23:59:60)(?:\\ .\\ d+)?(?:z|[+-]\\ d\\ d(?::?\\ d\\ d)?)?$" , Pattern . CASE_INSENSITIVE );
214-
218+ private static final Pattern FASTTIME = Pattern .compile ("^(?:[0-2]\\ d:[0-5]\\ d:[0-5]\\ d|23:59:60)(?:\\ .\\ d+)?(?:z|[+-]\\ d\\ d(?::?\\ d\\ d)?)?$" , CASE_INSENSITIVE );
219+ private static final Pattern VALID_LEAP_SECONDS = Pattern . compile ( "^23:59:60(?: \\ . \\ d+)?(?:z|[+-]00(?::?00)?)?$" , CASE_INSENSITIVE );
215220 private static boolean testTime (String value ) {
216- return FASTTIME .matcher (value ).find ();
221+ if (!FASTTIME .matcher (value ).find ()) {
222+ return false ;
223+ }
224+
225+ //The built-in ISO_TIME does not account for leap seconds.
226+ // So if it IS a leap second (or matches) just assume that it's OK
227+ if (VALID_LEAP_SECONDS .matcher (value ).find ()) {
228+ return true ;
229+ }
230+
231+ return validateISOTime (DateTimeFormatter .ISO_TIME , value );
217232 }
218233
219234 // date: http://tools.ietf.org/html/rfc3339#section-5.6
@@ -223,8 +238,13 @@ private static boolean testDate(String value) {
223238 if (!FASTDATE .matcher (value ).matches ()) {
224239 return false ;
225240 }
241+
242+ return validateISOTime (DateTimeFormatter .ISO_DATE , value );
243+ }
244+
245+ private static boolean validateISOTime (DateTimeFormatter formatter , String value ) {
226246 try {
227- DateTimeFormatter . ISO_DATE .parse (value );
247+ formatter .parse (value );
228248 return true ;
229249 } catch (DateTimeParseException e ) {
230250 return false ;
0 commit comments