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