Skip to content

Commit 7070e7d

Browse files
committed
added and tested all date/time combinations
1 parent fb9231f commit 7070e7d

File tree

1 file changed

+74
-10
lines changed

1 file changed

+74
-10
lines changed

src/Formatter/Specialised/JoltHttpOGMTranslator.php

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace Laudis\Neo4j\Formatter\Specialised;
1515

1616
use Closure;
17+
use const DATE_ATOM;
1718
use DateTimeImmutable;
1819
use function is_array;
1920
use Laudis\Neo4j\Contracts\ConnectionInterface;
@@ -25,6 +26,8 @@
2526
use Laudis\Neo4j\Types\CypherList;
2627
use Laudis\Neo4j\Types\CypherMap;
2728
use Laudis\Neo4j\Types\Date;
29+
use Laudis\Neo4j\Types\DateTime;
30+
use Laudis\Neo4j\Types\LocalDateTime;
2831
use Laudis\Neo4j\Types\LocalTime;
2932
use Laudis\Neo4j\Types\Node;
3033
use Laudis\Neo4j\Types\Path;
@@ -36,6 +39,7 @@
3639
use function preg_match;
3740
use Psr\Http\Message\RequestInterface;
3841
use Psr\Http\Message\ResponseInterface;
42+
use RuntimeException;
3943
use stdClass;
4044
use function str_pad;
4145
use const STR_PAD_RIGHT;
@@ -303,6 +307,10 @@ private function translateBinary(): Closure
303307
throw new UnexpectedValueException('Binary data has not been implemented');
304308
}
305309

310+
private const TIME_REGEX = '(?<hours>\d{2}):(?<minutes>\d{2}):(?<seconds>\d{2})((\.)(?<nanoseconds>\d+))?';
311+
private const DATE_REGEX = '(?<date>[\-−]?\d+-\d{2}-\d{2})';
312+
private const ZONE_REGEX = '(?<zone>.+)';
313+
306314
/**
307315
* @return OGMTypes
308316
*
@@ -311,34 +319,56 @@ private function translateBinary(): Closure
311319
*/
312320
private function translateDateTime(string $datetime)
313321
{
314-
if (preg_match('/^[\-−]?\d+-\d{2}-\d{2}$/u', $datetime)) {
315-
$date = DateTimeImmutable::createFromFormat('Y-m-d', $datetime);
322+
if (preg_match('/^'.self::DATE_REGEX.'$/u', $datetime, $matches)) {
323+
$days = $this->daysFromMatches($matches);
316324

317-
return new Date((int) $date->diff(new DateTimeImmutable('@0'))->format('%a'));
325+
return new Date($days);
318326
}
319327

320-
if (preg_match('/^(\d{2}):(\d{2}):(\d{2})((\.)(\d+))?$/', $datetime, $matches)) {
328+
if (preg_match('/^'.self::TIME_REGEX.'$/u', $datetime, $matches)) {
321329
$nanoseconds = $this->nanosecondsFromMatches($matches);
322330

323331
return new LocalTime($nanoseconds);
324332
}
325333

326-
if (preg_match('/^(\d{2}):(\d{2}):(\d{2})((\.)(\d+))?(?<zone>.+)$/', $datetime, $matches)) {
334+
if (preg_match('/^'.self::TIME_REGEX.self::ZONE_REGEX.'$/u', $datetime, $matches)) {
327335
$nanoseconds = $this->nanosecondsFromMatches($matches);
328336

329337
$offset = $this->offsetFromMatches($matches);
330338

331339
return new Time($nanoseconds, $offset);
332340
}
333341

334-
throw new UnexpectedValueException('Date/time values have not been implemented yet');
342+
if (preg_match('/^'.self::DATE_REGEX.'T'.self::TIME_REGEX.'$/u', $datetime, $matches)) {
343+
$nanoseconds = $this->nanosecondsFromMatches($matches);
344+
$seconds = $this->secondsInDaysFromMatches($matches);
345+
346+
[$seconds, $nanoseconds] = $this->addNanoSecondsToSeconds($nanoseconds, $seconds);
347+
348+
return new LocalDateTime($seconds, $nanoseconds);
349+
}
350+
351+
if (preg_match('/^'.self::DATE_REGEX.'T'.self::TIME_REGEX.self::ZONE_REGEX.'$/u', $datetime, $matches)) {
352+
$nanoseconds = $this->nanosecondsFromMatches($matches);
353+
$seconds = $this->secondsInDaysFromMatches($matches);
354+
355+
[$seconds, $nanoseconds] = $this->addNanoSecondsToSeconds($nanoseconds, $seconds);
356+
357+
$offset = $this->offsetFromMatches($matches);
358+
359+
return new DateTime($seconds, $nanoseconds, $offset);
360+
}
361+
362+
throw new UnexpectedValueException(sprintf('Could not handle date/time "%s"', $datetime));
335363
}
336364

337365
private function nanosecondsFromMatches(array $matches): int
338366
{
339-
/** @var array{0: string, 1: string, 2: string, 3: string, 4?: array{0: string, 1: string}} $matches */
340-
$seconds = ((int) $matches[1]) * 60 * 60 + ((int) $matches[2]) * 60 + ((int) $matches[3]);
341-
$nanoseconds = $matches[4][1] ?? '0';
367+
/** @var array{0: string, hours: string, minutes: string, seconds: string, nanoseconds?: string} $matches */
368+
['hours' => $hours, 'minutes' => $minutes, 'seconds' => $seconds] = $matches;
369+
$seconds = (((int) $hours) * 60 * 60) + (((int) $minutes) * 60) + ((int) $seconds);
370+
371+
$nanoseconds = $matches['nanoseconds'] ?? '0';
342372
$nanoseconds = str_pad($nanoseconds, 9, '0', STR_PAD_RIGHT);
343373

344374
return $seconds * 1000 * 1000 * 1000 + (int) $nanoseconds;
@@ -351,9 +381,43 @@ private function offsetFromMatches(array $matches): int
351381

352382
if (preg_match('/(\d{2}):(\d{2})/', $zone, $matches)) {
353383
/** @var array{0: string, 1: string, 2: string} $matches */
354-
return ((int) $matches[1]) * 60 + (int) $matches[2];
384+
return ((int) $matches[1]) * 60 * 60 + (int) $matches[2] * 60;
355385
}
356386

357387
return 0;
358388
}
389+
390+
private function daysFromMatches(array $matches): int
391+
{
392+
/** @var array{date: string} $matches */
393+
$date = DateTimeImmutable::createFromFormat('Y-m-d', $matches['date']);
394+
if ($date === false) {
395+
throw new RuntimeException(sprintf('Cannot create DateTime from "%s" in format "Y-m-d"', $matches['date']));
396+
}
397+
398+
/** @psalm-suppress ImpureMethodCall */
399+
return (int) $date->diff(new DateTimeImmutable('@0'))->format('%a');
400+
}
401+
402+
private function secondsInDaysFromMatches(array $matches): int
403+
{
404+
/** @var array{date: string} $matches */
405+
$date = DateTimeImmutable::createFromFormat(DATE_ATOM, $matches['date'].'T00:00:00+00:00');
406+
if ($date === false) {
407+
throw new RuntimeException(sprintf('Cannot create DateTime from "%s" in format "Y-m-d"', $matches['date']));
408+
}
409+
410+
return $date->getTimestamp();
411+
}
412+
413+
/**
414+
* @return array{0: int, 1: int}
415+
*/
416+
private function addNanoSecondsToSeconds(int $nanoseconds, int $seconds): array
417+
{
418+
$seconds += (int) ($nanoseconds / 1000 / 1000 / 1000);
419+
$nanoseconds %= 1000000000;
420+
421+
return [$seconds, $nanoseconds];
422+
}
359423
}

0 commit comments

Comments
 (0)