Skip to content

Commit 01d2d64

Browse files
committed
Use a stricter date-time attribute formatter(rfc3339)
1 parent c1fffae commit 01d2d64

File tree

3 files changed

+141
-8
lines changed

3 files changed

+141
-8
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.github.fge.jsonschema.format.common;
2+
3+
import java.time.format.DateTimeFormatter;
4+
import java.time.format.DateTimeFormatterBuilder;
5+
import java.time.format.DateTimeParseException;
6+
import java.time.temporal.ChronoField;
7+
import java.util.List;
8+
9+
import com.github.fge.jackson.NodeType;
10+
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
11+
import com.github.fge.jsonschema.core.report.ProcessingReport;
12+
import com.github.fge.jsonschema.format.AbstractFormatAttribute;
13+
import com.github.fge.jsonschema.format.FormatAttribute;
14+
import com.github.fge.jsonschema.processors.data.FullData;
15+
import com.github.fge.msgsimple.bundle.MessageBundle;
16+
import com.google.common.collect.ImmutableList;
17+
18+
/**
19+
* A {@link DateTimeFormatter} for date and time format defined in RFC3339.
20+
* @see <a href="https://tools.ietf.org/html/rfc3339#section-5.6">RFC 3339 - Section 5.6</a>
21+
*/
22+
public class RFC3339DateTimeAttribute extends AbstractFormatAttribute {
23+
24+
private static final List<String> RFC3339_FORMATS = ImmutableList.of(
25+
"yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)"
26+
);
27+
28+
private static final DateTimeFormatter RFC3339_FORMATTER;
29+
30+
static {
31+
final DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder()
32+
.appendPattern("yyyy-MM-dd")
33+
.appendLiteral('T')
34+
.appendPattern("HH:mm:ss")
35+
.optionalStart()
36+
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
37+
.optionalEnd()
38+
.appendOffset("+HH:mm", "Z");
39+
RFC3339_FORMATTER = builder.toFormatter();
40+
}
41+
42+
private static final FormatAttribute INSTANCE = new RFC3339DateTimeAttribute();
43+
44+
public static FormatAttribute getInstance()
45+
{
46+
return INSTANCE;
47+
}
48+
49+
private RFC3339DateTimeAttribute()
50+
{
51+
super("date-time", NodeType.STRING);
52+
}
53+
54+
@Override
55+
public void validate(final ProcessingReport report,
56+
final MessageBundle bundle, final FullData data)
57+
throws ProcessingException
58+
{
59+
final String value = data.getInstance().getNode().textValue();
60+
61+
try {
62+
RFC3339_FORMATTER.parse(value);
63+
} catch (DateTimeParseException ignored) {
64+
report.error(newMsg(data, bundle, "err.format.invalidDate")
65+
.putArgument("value", value).putArgument("expected", RFC3339_FORMATS));
66+
}
67+
}
68+
}

src/main/java/com/github/fge/jsonschema/library/format/CommonFormatAttributesDictionary.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
import com.github.fge.jsonschema.core.util.Dictionary;
2323
import com.github.fge.jsonschema.core.util.DictionaryBuilder;
2424
import com.github.fge.jsonschema.format.FormatAttribute;
25-
import com.github.fge.jsonschema.format.common.DateTimeAttribute;
2625
import com.github.fge.jsonschema.format.common.EmailAttribute;
2726
import com.github.fge.jsonschema.format.common.IPv6Attribute;
27+
import com.github.fge.jsonschema.format.common.RFC3339DateTimeAttribute;
2828
import com.github.fge.jsonschema.format.common.RegexAttribute;
2929
import com.github.fge.jsonschema.format.common.URIAttribute;
3030

@@ -49,7 +49,7 @@ private CommonFormatAttributesDictionary()
4949
FormatAttribute attribute;
5050

5151
name = "date-time";
52-
attribute = DateTimeAttribute.getInstance();
52+
attribute = RFC3339DateTimeAttribute.getInstance();
5353
builder.addEntry(name, attribute);
5454

5555
name = "email";

src/test/resources/format/common/date-time.json

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
3-
"data": "2012-12-02T13:05:00+0100",
3+
"data": "2012-12-02T13:05:00+01:00",
44
"valid": true
55
},
66
{
@@ -19,13 +19,60 @@
1919
"data": "2012-08-07T20:42:32.13Z",
2020
"valid": true
2121
},
22+
{
23+
"data": "2012-08-07T20:42:32+10:00",
24+
"valid": true
25+
},
26+
{
27+
"data": "2012-08-07T20:42:32-05:30",
28+
"valid": true
29+
},
30+
{
31+
"data": "2012-12-02T13:05:00+0100",
32+
"valid": false,
33+
"message": "err.format.invalidDate",
34+
"msgData": {
35+
"value": "2012-12-02T13:05:00+0100",
36+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
37+
},
38+
"msgParams": [ "value", "expected" ]
39+
},
40+
{
41+
"data": "2012-12-02T13:05:00+0100",
42+
"valid": false,
43+
"message": "err.format.invalidDate",
44+
"msgData": {
45+
"value": "2012-12-02T13:05:00+0100",
46+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
47+
},
48+
"msgParams": [ "value", "expected" ]
49+
},
50+
{
51+
"data": "2012-12-02T13:05:00Z[Europe/Paris]",
52+
"valid": false,
53+
"message": "err.format.invalidDate",
54+
"msgData": {
55+
"value": "2012-12-02T13:05:00Z[Europe/Paris]",
56+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
57+
},
58+
"msgParams": [ "value", "expected" ]
59+
}, {
60+
"data": "2012-12-02T13:05:00+0100",
61+
"valid": false,
62+
"message": "err.format.invalidDate",
63+
"msgData": {
64+
"value": "2012-12-02T13:05:00+0100",
65+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
66+
},
67+
"msgParams": [ "value", "expected" ]
68+
},
2269
{
2370
"data": "2012-02-30T00:00:00+0000",
2471
"valid": false,
2572
"message": "err.format.invalidDate",
2673
"msgData": {
2774
"value": "2012-02-30T00:00:00+0000",
28-
"expected": [ "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}Z" ]
75+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
2976
},
3077
"msgParams": [ "value", "expected" ]
3178
},
@@ -35,7 +82,7 @@
3582
"message": "err.format.invalidDate",
3683
"msgData": {
3784
"value": "201202030",
38-
"expected": [ "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}Z" ]
85+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
3986
},
4087
"msgParams": [ "value", "expected" ]
4188
},
@@ -69,14 +116,32 @@
69116
},
70117
{
71118
"data": "2012-08-07T20:42:32.1234567890Z",
72-
"valid": true
119+
"valid": false,
120+
"message": "err.format.invalidDate",
121+
"msgData": {
122+
"value": "2012-08-07T20:42:32.1234567890Z",
123+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
124+
},
125+
"msgParams": [ "value", "expected" ]
73126
},
74127
{
75128
"data": "2012-08-07T20:42:32.12345678901Z",
76-
"valid": true
129+
"valid": false,
130+
"message": "err.format.invalidDate",
131+
"msgData": {
132+
"value": "2012-08-07T20:42:32.12345678901Z",
133+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
134+
},
135+
"msgParams": [ "value", "expected" ]
77136
},
78137
{
79138
"data": "2012-08-07T20:42:32.123456789012Z",
80-
"valid": true
139+
"valid": false,
140+
"message": "err.format.invalidDate",
141+
"msgData": {
142+
"value": "2012-08-07T20:42:32.123456789012Z",
143+
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,9}((+|-)HH:mm|Z)" ]
144+
},
145+
"msgParams": [ "value", "expected" ]
81146
}
82147
]

0 commit comments

Comments
 (0)