Skip to content

Commit 94d8d29

Browse files
committed
Merge pull request #238 from bburnichon/feature/rfc339-helper
add rfc3339 helper class
2 parents 5a49ebd + 6474265 commit 94d8d29

File tree

3 files changed

+91
-18
lines changed

3 files changed

+91
-18
lines changed

src/JsonSchema/Constraints/FormatConstraint.php

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
namespace JsonSchema\Constraints;
11+
use JsonSchema\Rfc3339;
1112

1213
/**
1314
* Validates against the "format" property
@@ -40,11 +41,7 @@ public function check($element, $schema = null, $path = null, $i = null)
4041
break;
4142

4243
case 'date-time':
43-
if (!$this->validateDateTime($element, 'Y-m-d\TH:i:s\Z') &&
44-
!$this->validateDateTime($element, 'Y-m-d\TH:i:s.u\Z') &&
45-
!$this->validateDateTime($element, 'Y-m-d\TH:i:sP') &&
46-
!$this->validateDateTime($element, 'Y-m-d\TH:i:sO')
47-
) {
44+
if (null === Rfc3339::createFromString($element)) {
4845
$this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)), 'format', array('format' => $schema->format,));
4946
}
5047
break;
@@ -130,19 +127,7 @@ protected function validateDateTime($datetime, $format)
130127
return false;
131128
}
132129

133-
if ($datetime === $dt->format($format)) {
134-
return true;
135-
}
136-
137-
// handles the case where a non-6 digit microsecond datetime is passed
138-
// which will fail the above string comparison because the passed
139-
// $datetime may be '2000-05-01T12:12:12.123Z' but format() will return
140-
// '2000-05-01T12:12:12.123000Z'
141-
if ((strpos('u', $format) !== -1) && (intval($dt->format('u')) > 0)) {
142-
return true;
143-
}
144-
145-
return false;
130+
return $datetime === $dt->format($format);
146131
}
147132

148133
protected function validateRegex($regex)

src/JsonSchema/Rfc3339.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace JsonSchema;
4+
5+
class Rfc3339
6+
{
7+
const REGEX = '/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d+)?(Z|([+-]\d{2}):?(\d{2}))/';
8+
9+
/**
10+
* Try creating a DateTime instance
11+
*
12+
* @param string $string
13+
* @return \DateTime|null
14+
*/
15+
public static function createFromString($string)
16+
{
17+
if (!preg_match(self::REGEX, strtoupper($string), $matches)) {
18+
return null;
19+
}
20+
21+
$dateAndTime = $matches[1];
22+
$microseconds = $matches[2] ?: '.000000';
23+
$timeZone = 'Z' !== $matches[3] ? $matches[4] . ':' . $matches[5] : '+00:00';
24+
25+
$dateTime = \DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $dateAndTime . $microseconds . $timeZone, new \DateTimeZone('UTC'));
26+
27+
return $dateTime ?: null;
28+
}
29+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace JsonSchema\Tests;
4+
5+
use JsonSchema\Rfc3339;
6+
7+
class Rfc3339Test extends \PHPUnit_Framework_TestCase
8+
{
9+
/**
10+
* @param string $string
11+
* @param \DateTime|null $expected
12+
* @dataProvider provideValidFormats
13+
*/
14+
public function testCreateFromValidString($string, \DateTime $expected)
15+
{
16+
$actual = Rfc3339::createFromString($string);
17+
18+
$this->assertInstanceOf('DateTime', $actual);
19+
$this->assertEquals($expected->format('U.u'), $actual->format('U.u'));
20+
}
21+
22+
/**
23+
* @param string $string
24+
* @dataProvider provideInvalidFormats
25+
*/
26+
public function testCreateFromInvalidString($string)
27+
{
28+
$this->assertNull(Rfc3339::createFromString($string), sprintf('String "%s" should not be converted to DateTime', $string));
29+
}
30+
31+
public function provideValidFormats()
32+
{
33+
return array(
34+
array(
35+
'2000-05-01T12:12:12Z',
36+
\DateTime::createFromFormat('Y-m-d\TH:i:s', '2000-05-01T12:12:12', new \DateTimeZone('UTC'))
37+
),
38+
array('2000-05-01T12:12:12+0100', \DateTime::createFromFormat('Y-m-d\TH:i:sP', '2000-05-01T12:12:12+01:00')),
39+
array('2000-05-01T12:12:12+01:00', \DateTime::createFromFormat('Y-m-d\TH:i:sP', '2000-05-01T12:12:12+01:00')),
40+
array(
41+
'2000-05-01T12:12:12.123456Z',
42+
\DateTime::createFromFormat('Y-m-d\TH:i:s.u', '2000-05-01T12:12:12.123456', new \DateTimeZone('UTC'))
43+
),
44+
array(
45+
'2000-05-01T12:12:12.123Z',
46+
\DateTime::createFromFormat('Y-m-d\TH:i:s.u', '2000-05-01T12:12:12.123000', new \DateTimeZone('UTC'))
47+
),
48+
);
49+
}
50+
51+
public function provideInvalidFormats()
52+
{
53+
return array(
54+
array('1999-1-11T00:00:00Z'),
55+
array('1999-01-11T00:00:00+100'),
56+
array('1999-01-11T00:00:00+1:00'),
57+
);
58+
}
59+
}

0 commit comments

Comments
 (0)