Skip to content

Commit

Permalink
add UtcDateTime class
Browse files Browse the repository at this point in the history
  • Loading branch information
solodkiy committed Jul 8, 2021
1 parent b4e45bf commit 353b31a
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 3 deletions.
71 changes: 71 additions & 0 deletions src/UtcDateTime.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace Brick\DateTime;

use Brick\DateTime\Parser\DateTimeParser;
use Brick\DateTime\Parser\DateTimeParseResult;

final class UtcDateTime extends ZonedDateTime
{
public static function of(LocalDateTime $dateTime, TimeZone $timeZone = null): ZonedDateTime
{
if ($timeZone === null) {
$timeZone = TimeZone::utc();
}
if (!$timeZone->isEqualTo(TimeZone::utc())) {
throw new \InvalidArgumentException('Create UtcDateTime with not UTC timezone is not supported');
}
return parent::of($dateTime, $timeZone);
}

public static function ofInstant(Instant $instant, TimeZone $timeZone = null): ZonedDateTime
{
if ($timeZone === null) {
$timeZone = TimeZone::utc();
}
if (!$timeZone->isEqualTo(TimeZone::utc())) {
throw new \InvalidArgumentException('Create UtcDateTime with not UTC timezone is not supported');
}
return parent::ofInstant($instant, $timeZone);
}

public static function now(TimeZone $timeZone = null, ?Clock $clock = null): ZonedDateTime
{
if ($timeZone === null) {
$timeZone = TimeZone::utc();
}
if (!$timeZone->isEqualTo(TimeZone::utc())) {
throw new \InvalidArgumentException('Create UtcDateTime with not UTC timezone is not supported');
}
return parent::now($timeZone, $clock);
}

public static function from(DateTimeParseResult $result): ZonedDateTime
{
$result = parent::from($result);
if (!$result->getTimeZone()->isEqualTo(TimeZone::utc())) {
$result = $result->withTimeZoneSameInstant(TimeZone::utc());
}
return $result;
}

public static function parse(string $text, ?DateTimeParser $parser = null): ZonedDateTime
{
$result = parent::parse($text, $parser);
if (!$result->getTimeZone()->isEqualTo(TimeZone::utc())) {
$result = $result->withTimeZoneSameInstant(TimeZone::utc());
}
return $result;
}

public static function fromDateTime(\DateTimeInterface $dateTime): ZonedDateTime
{
$result = parent::fromDateTime($dateTime);
if (!$result->getTimeZone()->isEqualTo(TimeZone::utc())) {
$result = $result->withTimeZoneSameInstant(TimeZone::utc());
}
return $result;
}
}
24 changes: 21 additions & 3 deletions src/ZonedDateTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ public static function of(LocalDateTime $dateTime, TimeZone $timeZone) : ZonedDa
// DateTime does not support nanos of seconds, so we just copy the nanos back from the original date-time.
$dateTime = LocalDateTime::parse($dt->format('Y-m-d\TH:i:s'))->withNano($dateTime->getNano());

return new ZonedDateTime($dateTime, $timeZoneOffset, $timeZone, $instant);
if ($timeZone->isEqualTo(TimeZone::utc())) {
return new UtcDateTime($dateTime, $timeZoneOffset, $timeZone, $instant);
} else {
return new ZonedDateTime($dateTime, $timeZoneOffset, $timeZone, $instant);
}
}

/**
Expand All @@ -135,7 +139,11 @@ public static function ofInstant(Instant $instant, TimeZone $timeZone) : ZonedDa
$timeZoneOffset = TimeZoneOffset::ofTotalSeconds($dateTime->getOffset());
}

return new ZonedDateTime($localDateTime, $timeZoneOffset, $timeZone, $instant);
if ($timeZone->isEqualTo(TimeZone::utc())) {
return new UtcDateTime($localDateTime, $timeZoneOffset, $timeZone, $instant);
} else {
return new ZonedDateTime($localDateTime, $timeZoneOffset, $timeZone, $instant);
}
}

/**
Expand Down Expand Up @@ -222,7 +230,11 @@ public static function fromDateTime(\DateTimeInterface $dateTime) : ZonedDateTim

$instant = Instant::of($dateTime->getTimestamp(), $localDateTime->getNano());

return new ZonedDateTime($localDateTime, $timeZoneOffset, $timeZone, $instant);
if ($timeZone->isEqualTo(TimeZone::utc())) {
return new UtcDateTime($localDateTime, $timeZoneOffset, $timeZone, $instant);
} else {
return new ZonedDateTime($localDateTime, $timeZoneOffset, $timeZone, $instant);
}
}

/**
Expand Down Expand Up @@ -697,6 +709,12 @@ public function toDateTimeImmutable() : \DateTimeImmutable
return \DateTimeImmutable::createFromMutable($this->toDateTime());
}

public function toUtcDateTime() : UtcDateTime
{
/** @noinspection PhpIncompatibleReturnTypeInspection */
return UtcDateTime::ofInstant($this->instant);
}

/**
* Serializes as a string using {@see ZonedDateTime::__toString()}.
*/
Expand Down
105 changes: 105 additions & 0 deletions tests/UtcDateTimeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

declare(strict_types=1);

namespace Brick\DateTime\Tests;

use Brick\DateTime\Instant;
use Brick\DateTime\LocalDateTime;
use Brick\DateTime\LocalDate;
use Brick\DateTime\LocalTime;
use Brick\DateTime\Parser\DateTimeParseException;
use Brick\DateTime\Period;
use Brick\DateTime\Duration;
use Brick\DateTime\TimeZone;
use Brick\DateTime\TimeZoneOffset;
use Brick\DateTime\UtcDateTime;
use Brick\DateTime\ZonedDateTime;
use Brick\DateTime\DayOfWeek;
use Brick\DateTime\Clock\FixedClock;

/**
* Unit tests for class ZonedDateTime.
*/
class UtcDateTimeTest extends AbstractTestCase
{
public function testOf(): void
{
$a = ZonedDateTime::of(LocalDateTime::of(2020, 1, 2), TimeZone::utc());
$this->assertInstanceOf(UtcDateTime::class, $a);
$b = UtcDateTime::of(LocalDateTime::of(2020, 1, 2), TimeZone::utc());
$this->assertInstanceOf(UtcDateTime::class, $b);
$c = UtcDateTime::of(LocalDateTime::of(2020, 1, 2));
$this->assertInstanceOf(UtcDateTime::class, $c);

$this->assertEquals($b, $a);
$this->assertEquals($c, $a);
$this->assertEquals($c, $b);
}

public function testOfError(): void
{
$this->expectException(\InvalidArgumentException::class);
UtcDateTime::of(LocalDateTime::of(2020, 1, 2), TimeZone::parse('Europe/Moscow'));
}

/**
* @dataProvider providerFromDateTime
* @param string $dateTimeString
* @param string $timeZone
* @param string $expected
* @throws \Exception
*/
public function testFromDateTime(string $dateTimeString, string $timeZone, string $expected): void
{
$dateTime = new \DateTime($dateTimeString, new \DateTimeZone($timeZone));
$this->assertIs(UtcDateTime::class, $expected, UtcDateTime::fromDateTime($dateTime));
}

public function providerFromDateTime() : array
{
return [
['2018-07-21 14:09:10.23456', 'America/Los_Angeles', '2018-07-21T21:09:10.23456Z'],
['2019-01-21 17:59', 'America/Los_Angeles', '2019-01-22T01:59Z'],
['2019-01-23 09:10:11.123', '+05:30', '2019-01-23T03:40:11.123Z'],
];
}

/**
* @dataProvider providerParse
*
* @param string $text The string to parse.
* @param string $date The expected date string.
* @param string $time The expected time string.
* @param string $offset The expected time-zone offset.
* @param string $zone The expected time-zone, should be the same as offset when no region is specified.
*/
public function testParse(string $text, string $date, string $time, string $offset, string $zone): void
{
$zonedDateTime = UtcDateTime::parse($text);

$this->assertInstanceOf(UtcDateTime::class, $zonedDateTime);

$this->assertSame($date, (string) $zonedDateTime->getDate());
$this->assertSame($time, (string) $zonedDateTime->getTime());
$this->assertSame($offset, (string) $zonedDateTime->getTimeZoneOffset());
$this->assertSame($zone, (string) $zonedDateTime->getTimeZone());
}

public function providerParse() : array
{
return [
['2001-02-03T01:02Z', '2001-02-03', '01:02', 'Z', 'Z'],
['2001-02-03T01:02:03Z', '2001-02-03', '01:02:03', 'Z', 'Z'],
['2001-02-03T01:02:03.456Z', '2001-02-03', '01:02:03.456', 'Z', 'Z'],
['2001-02-03T01:02-03:00', '2001-02-03', '04:02', 'Z', 'Z'],
['2001-02-03T01:02:03+04:00', '2001-02-02', '21:02:03', 'Z', 'Z'],

//['2001-02-03T01:02:03.456+12:34:56', '2001-02-03', '01:02:03.456', 'Z', 'Z'],
['2001-02-03T01:02Z[Europe/London]', '2001-02-03', '01:02', 'Z', 'Z'],
['2001-02-03T01:02+00:00[Europe/London]', '2001-02-03', '01:02', 'Z', 'Z'],
['2001-02-03T01:02:03-00:00[Europe/London]', '2001-02-03', '01:02:03', 'Z', 'Z'],
['2001-02-03T01:02:03.456+00:00[Europe/London]', '2001-02-03', '01:02:03.456', 'Z', 'Z']
];
}
}

0 comments on commit 353b31a

Please sign in to comment.