Skip to content

Commit

Permalink
Merge pull request java-json-tools#235 from tvdinh/rfc3339_datetime
Browse files Browse the repository at this point in the history
Use a stricter date-time attribute formatter(rfc3339)
  • Loading branch information
huggsboson authored Jul 10, 2017
2 parents c1fffae + d273e06 commit 728b3da
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.github.fge.jsonschema.format.common;

import java.util.List;

import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.DateTimeParser;

import com.github.fge.jackson.NodeType;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.format.AbstractFormatAttribute;
import com.github.fge.jsonschema.format.FormatAttribute;
import com.github.fge.jsonschema.processors.data.FullData;
import com.github.fge.msgsimple.bundle.MessageBundle;
import com.google.common.collect.ImmutableList;

/**
* A {@link DateTimeFormatter} for date and time format defined in RFC3339.
* @see <a href="https://tools.ietf.org/html/rfc3339#section-5.6">RFC 3339 - Section 5.6</a>
*/
public class RFC3339DateTimeAttribute extends AbstractFormatAttribute {

private static final List<String> RFC3339_FORMATS = ImmutableList.of(
"yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)"
);

private static final DateTimeFormatter FORMATTER;

static {
final DateTimeParser secFracsParser = new DateTimeFormatterBuilder()
.appendLiteral('.').appendFractionOfSecond(1,12)
.toParser();

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd'T'HH:mm:ss")
.appendOptional(secFracsParser)
.appendTimeZoneOffset("Z", true, 2, 2);

FORMATTER = builder.toFormatter();
}

private static final FormatAttribute INSTANCE = new RFC3339DateTimeAttribute();

public static FormatAttribute getInstance()
{
return INSTANCE;
}

private RFC3339DateTimeAttribute()
{
super("date-time", NodeType.STRING);
}

@Override
public void validate(final ProcessingReport report,
final MessageBundle bundle, final FullData data)
throws ProcessingException
{
final String value = data.getInstance().getNode().textValue();

try
{
FORMATTER.parseDateTime(value);

final String secFracsAndOffset = value.substring("yyyy-MM-ddTHH:mm:ss".length());
final String offset;
if (!secFracsAndOffset.startsWith(".")) {
offset = secFracsAndOffset;
} else{
if (secFracsAndOffset.contains("Z")) {
offset = secFracsAndOffset.substring(secFracsAndOffset.indexOf("Z"));
} else if (secFracsAndOffset.contains("+")) {
offset = secFracsAndOffset.substring(secFracsAndOffset.indexOf("+"));
} else {
offset = secFracsAndOffset.substring(secFracsAndOffset.indexOf("-"));
}
}
if (!isOffSetStrictRFC3339(offset)) {
throw new IllegalArgumentException();
}

} catch (IllegalArgumentException ignored) {
report.error(newMsg(data, bundle, "err.format.invalidDate")
.putArgument("value", value).putArgument("expected", RFC3339_FORMATS));
}

}

/**
* Return true if date-time offset stricly follows RFC3339:
* <code>time-hour = 2DIGIT ; 00-23</code>
* <code>time-minute = 2DIGIT ; 00-59</code>
* <code>time-numoffset = ("+" / "-") time-hour ":" time-minute</code>
* <code>time-offset = "Z" / time-numoffset</code>,
* and false otherwise
* @param offset
* @return
*/
private boolean isOffSetStrictRFC3339(final String offset)
{
if (offset.endsWith("Z")) return true;
if (offset.length() == 6 && offset.contains(":")) {
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
import com.github.fge.jsonschema.core.util.Dictionary;
import com.github.fge.jsonschema.core.util.DictionaryBuilder;
import com.github.fge.jsonschema.format.FormatAttribute;
import com.github.fge.jsonschema.format.common.DateTimeAttribute;
import com.github.fge.jsonschema.format.common.EmailAttribute;
import com.github.fge.jsonschema.format.common.IPv6Attribute;
import com.github.fge.jsonschema.format.common.RFC3339DateTimeAttribute;
import com.github.fge.jsonschema.format.common.RegexAttribute;
import com.github.fge.jsonschema.format.common.URIAttribute;

Expand All @@ -49,7 +49,7 @@ private CommonFormatAttributesDictionary()
FormatAttribute attribute;

name = "date-time";
attribute = DateTimeAttribute.getInstance();
attribute = RFC3339DateTimeAttribute.getInstance();
builder.addEntry(name, attribute);

name = "email";
Expand Down
94 changes: 83 additions & 11 deletions src/test/resources/format/common/date-time.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"data": "2012-12-02T13:05:00+0100",
"data": "2012-12-02T13:05:00+01:00",
"valid": true
},
{
Expand All @@ -20,22 +20,90 @@
"valid": true
},
{
"data": "2012-02-30T00:00:00+0000",
"data": "2012-08-07T20:42:32+10:00",
"valid": true
},
{
"data": "2012-08-07T20:42:32-05:30",
"valid": true
},
{
"data": "201202030",
"valid": false,
"message": "err.format.invalidDate",
"msgData": {
"value": "2012-02-30T00:00:00+0000",
"expected": [ "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}Z" ]
"value": "201202030",
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)" ]
},
"msgParams": [ "value", "expected" ]
},
{
"data": "201202030",
"data": "2012-12-02T13:05:00+0100",
"valid": false,
"message": "err.format.invalidDate",
"msgData": {
"value": "201202030",
"expected": [ "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}Z" ]
"value": "2012-12-02T13:05:00+0100",
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)" ]
},
"msgParams": [ "value", "expected" ]
},
{
"data": "2012-12-02T13:05:00+01:30:30",
"valid": false,
"message": "err.format.invalidDate",
"msgData": {
"value": "2012-12-02T13:05:00+01:30:30",
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)" ]
},
"msgParams": [ "value", "expected" ]
},
{
"data": "2012-12-02T13:05:00Z[Europe/Paris]",
"valid": false,
"message": "err.format.invalidDate",
"msgData": {
"value": "2012-12-02T13:05:00Z[Europe/Paris]",
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)" ]
},
"msgParams": [ "value", "expected" ]
},
{
"data": "2012-12-02T13:05:00+10:00Z",
"valid": false,
"message": "err.format.invalidDate",
"msgData": {
"value": "2012-12-02T13:05:00+10:00Z",
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)" ]
},
"msgParams": [ "value", "expected" ]
},
{
"data": "2012-12-02T13:05:00America/New_York",
"valid": false,
"message": "err.format.invalidDate",
"msgData": {
"value": "2012-12-02T13:05:00America/New_York",
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)" ]
},
"msgParams": [ "value", "expected" ]
},
{
"data": "2012-12-02T13:05:00[America/New_York]",
"valid": false,
"message": "err.format.invalidDate",
"msgData": {
"value": "2012-12-02T13:05:00[America/New_York]",
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)" ]
},
"msgParams": [ "value", "expected" ]
},
{
"data": "2012-12-02T13:05:00.123456",
"valid": false,
"message": "err.format.invalidDate",
"msgData": {
"value": "2012-12-02T13:05:00.123456",
"expected": [ "yyyy-MM-dd'T'HH:mm:ss((+|-)HH:mm|Z)", "yyyy-MM-dd'T'HH:mm:ss.[0-9]{1,12}((+|-)HH:mm|Z)" ]
},
"msgParams": [ "value", "expected" ]
},
Expand All @@ -44,19 +112,19 @@
"valid": true
},
{
"data": "2012-08-07T20:42:32.12345Z",
"data": "2012-08-07T20:42:32.1234+05:00",
"valid": true
},
{
"data": "2012-08-07T20:42:32.123456Z",
"data": "2012-08-07T20:42:32.12345Z",
"valid": true
},
{
"data": "2012-08-07T20:42:32.1234567Z",
"data": "2012-08-07T20:42:32.123456Z",
"valid": true
},
{
"data": "2012-08-07T20:42:32.12345678Z",
"data": "2012-08-07T20:42:32.1234567Z",
"valid": true
},
{
Expand All @@ -78,5 +146,9 @@
{
"data": "2012-08-07T20:42:32.123456789012Z",
"valid": true
},
{
"data": "2012-08-07T20:42:32.123456789012+05:00",
"valid": true
}
]

0 comments on commit 728b3da

Please sign in to comment.