1: <?php declare(strict_types=1);
2:
3: namespace Salient\Core\Date;
4:
5: use Salient\Contract\Core\DateParserInterface;
6: use Salient\Utility\Date;
7: use Salient\Utility\Regex;
8: use Salient\Utility\Str;
9: use DateTimeImmutable;
10: use DateTimeZone;
11:
12: /**
13: * Parses Microsoft JSON and legacy JSON.NET timestamps
14: *
15: * @link https://www.newtonsoft.com/json/help/html/datesinjson.htm
16: *
17: * @api
18: */
19: final class DotNetDateParser implements DateParserInterface
20: {
21: private const REGEX = '/^\/Date\((?<seconds>[0-9]*?)(?<milliseconds>[0-9]{1,3})(?<offset>[-+][0-9]{4})?\)\/$/D';
22:
23: /**
24: * @inheritDoc
25: */
26: public function parse(string $value, ?DateTimeZone $timezone = null): ?DateTimeImmutable
27: {
28: if (!Regex::match(self::REGEX, $value, $matches, \PREG_UNMATCHED_AS_NULL)) {
29: return null;
30: }
31:
32: $date = new DateTimeImmutable(sprintf(
33: // PHP 7.4 requires 6 digits after the decimal point
34: '@%d.%03d000',
35: (int) Str::coalesce($matches['seconds'], '0'),
36: (int) $matches['milliseconds'],
37: ));
38: if ($matches['offset'] !== null) {
39: $date = $date->setTimezone(new DateTimeZone($matches['offset']));
40: }
41: return Date::maybeSetTimezone($date, $timezone);
42: }
43: }
44: