1145 lines
66 KiB
PHP
1145 lines
66 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Brick\DateTime\Tests;
|
|
|
|
use Brick\DateTime\Clock\FixedClock;
|
|
use Brick\DateTime\DayOfWeek;
|
|
use Brick\DateTime\Duration;
|
|
use Brick\DateTime\Instant;
|
|
use Brick\DateTime\LocalDate;
|
|
use Brick\DateTime\LocalDateTime;
|
|
use Brick\DateTime\LocalTime;
|
|
use Brick\DateTime\Month;
|
|
use Brick\DateTime\Parser\DateTimeParseException;
|
|
use Brick\DateTime\Period;
|
|
use Brick\DateTime\TimeZone;
|
|
use Brick\DateTime\TimeZoneOffset;
|
|
use Brick\DateTime\ZonedDateTime;
|
|
use DateTime;
|
|
use DateTimeImmutable;
|
|
use DateTimeZone;
|
|
use Generator;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
|
|
use function json_encode;
|
|
|
|
use const JSON_THROW_ON_ERROR;
|
|
use const PHP_VERSION_ID;
|
|
|
|
/**
|
|
* Unit tests for class ZonedDateTime.
|
|
*/
|
|
class ZonedDateTimeTest extends AbstractTestCase
|
|
{
|
|
/**
|
|
* @param string $localDateTime The local date-time as a string.
|
|
* @param string $timeZone The time-zone as a string.
|
|
* @param string $offset The expected time-zone offset of the result zoned date-time.
|
|
* @param int $shift The expected shift applied to the date-time (when in a gap), in seconds.
|
|
* @param int $epochSecond The expected epoch-second the result zoned date-time resolves to.
|
|
* @param int $nanoOfSecond The expected nano-of-second of the result zoned date-time.
|
|
*/
|
|
#[DataProvider('providerOf')]
|
|
public function testOf(string $localDateTime, string $timeZone, string $offset, int $shift, int $epochSecond, int $nanoOfSecond): void
|
|
{
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$timeZone = TimeZone::parse($timeZone);
|
|
$offset = TimeZoneOffset::parse($offset);
|
|
|
|
$expectedDateTime = $localDateTime->plusSeconds($shift);
|
|
|
|
$zonedDateTime = ZonedDateTime::of($localDateTime, $timeZone);
|
|
|
|
self::assertInstanceOf(ZonedDateTime::class, $zonedDateTime);
|
|
|
|
self::assertLocalDateTimeEquals($expectedDateTime, $zonedDateTime->getDateTime());
|
|
self::assertTimeZoneEquals($timeZone, $zonedDateTime->getTimeZone());
|
|
self::assertTimeZoneEquals($offset, $zonedDateTime->getTimeZoneOffset());
|
|
|
|
self::assertSame($epochSecond, $zonedDateTime->getEpochSecond());
|
|
self::assertSame($nanoOfSecond, $zonedDateTime->getNano());
|
|
}
|
|
|
|
public static function providerOf(): array
|
|
{
|
|
return [
|
|
// WITH OFFSET FROM UTC
|
|
// The date-time is resolved to an instant according to the offset without ambiguity.
|
|
|
|
['2000-01-01T12:34:56.123456789', '-18:00', '-18:00', 0, 946794896, 123456789],
|
|
['2001-02-02T12:34:56.123456789', '-17:00', '-17:00', 0, 981178496, 123456789],
|
|
['2002-03-03T12:34:56.123456789', '-16:00', '-16:00', 0, 1015216496, 123456789],
|
|
['2003-04-04T12:34:56.123456789', '-15:00', '-15:00', 0, 1049513696, 123456789],
|
|
['2004-05-05T12:34:56.123456789', '-14:00', '-14:00', 0, 1083810896, 123456789],
|
|
['2005-06-06T12:34:56.123456789', '-06:00', '-06:00', 0, 1118082896, 123456789],
|
|
['2006-07-07T12:34:56.123456789', '-05:00', '-05:00', 0, 1152293696, 123456789],
|
|
['2007-08-08T12:34:56.123456789', '-04:00', '-04:00', 0, 1186590896, 123456789],
|
|
['2008-09-09T12:34:56.123456789', '-03:00', '-03:00', 0, 1220974496, 123456789],
|
|
['2009-10-10T12:34:56.123456789', '-02:00', '-02:00', 0, 1255185296, 123456789],
|
|
['2010-11-11T12:34:56.123456789', '-01:30', '-01:30', 0, 1289484296, 123456789],
|
|
['2011-12-12T12:34:56.123456789', '-01:00', '-01:00', 0, 1323696896, 123456789],
|
|
['2012-01-13T12:34:56.123456789', '-00:45', '-00:45', 0, 1326460796, 123456789],
|
|
['2013-02-14T12:34:56.123456789', '-00:30', '-00:30', 0, 1360847096, 123456789],
|
|
['2014-03-15T12:34:56.123456789', '-00:15', '-00:15', 0, 1394887796, 123456789],
|
|
['2015-04-16T12:34:56.123456789', '+00:00', '+00:00', 0, 1429187696, 123456789],
|
|
['2016-05-17T12:34:56.123456789', '+00:15', '+00:15', 0, 1463487596, 123456789],
|
|
['2017-06-18T12:34:56.123456789', '+00:30', '+00:30', 0, 1497787496, 123456789],
|
|
['2018-07-19T12:34:56.123456789', '+00:45', '+00:45', 0, 1532000996, 123456789],
|
|
['2019-08-20T12:34:56.123456789', '+01:00', '+01:00', 0, 1566300896, 123456789],
|
|
['2011-09-21T12:34:56.123456789', '+01:30', '+01:30', 0, 1316603096, 123456789],
|
|
['2011-10-22T12:34:56.123456789', '+02:00', '+02:00', 0, 1319279696, 123456789],
|
|
['2011-11-23T12:34:56.123456789', '+03:00', '+03:00', 0, 1322040896, 123456789],
|
|
['2011-12-24T12:34:56.123456789', '+04:00', '+04:00', 0, 1324715696, 123456789],
|
|
['2011-01-25T12:34:56.123456789', '+05:00', '+05:00', 0, 1295940896, 123456789],
|
|
['2011-02-26T12:34:56.123456789', '+06:00', '+06:00', 0, 1298702096, 123456789],
|
|
['2011-03-27T12:34:56.123456789', '+14:00', '+14:00', 0, 1301178896, 123456789],
|
|
['2011-04-28T12:34:56.123456789', '+15:00', '+15:00', 0, 1303940096, 123456789],
|
|
['2011-05-29T12:34:56.123456789', '+16:00', '+16:00', 0, 1306614896, 123456789],
|
|
['2011-06-30T12:34:56.123456789', '+17:00', '+17:00', 0, 1309376096, 123456789],
|
|
['2011-07-31T12:34:56.123456789', '+18:00', '+18:00', 0, 1312050896, 123456789],
|
|
|
|
// WITH REGION, NORMAL: NOT WITHIN A DST TRANSITION
|
|
// The region is resolved to an offset without ambiguity.
|
|
// The date-time is resolved to an instant without ambiguity.
|
|
|
|
['2001-06-01T12:34:56.123456789', 'Pacific/Niue', '-11:00', 0, 991438496, 123456789],
|
|
['2002-06-02T12:34:56.123456789', 'Pacific/Honolulu', '-10:00', 0, 1023057296, 123456789],
|
|
['2003-06-03T12:34:56.123456789', 'Pacific/Marquesas', '-09:30', 0, 1054677896, 123456789],
|
|
['2004-12-04T12:34:56.123456789', 'America/Anchorage', '-09:00', 0, 1102196096, 123456789],
|
|
['2005-06-05T12:34:56.123456789', 'America/Anchorage', '-08:00', 0, 1118003696, 123456789],
|
|
['2006-06-06T12:34:56.123456789', 'America/Los_Angeles', '-07:00', 0, 1149622496, 123456789],
|
|
['2007-12-07T12:34:56.123456789', 'America/Mexico_City', '-06:00', 0, 1197052496, 123456789],
|
|
['2008-12-08T12:34:56.123456789', 'America/Nassau', '-05:00', 0, 1228757696, 123456789],
|
|
['2009-01-09T12:34:56.123456789', 'America/Caracas', '-04:30', 0, 1231520696, 123456789],
|
|
['2010-06-10T12:34:56.123456789', 'America/Nassau', '-04:00', 0, 1276187696, 123456789],
|
|
['2011-12-11T12:34:56.123456789', 'America/St_Johns', '-03:30', 0, 1323619496, 123456789],
|
|
['2012-06-12T12:34:56.123456789', 'America/Sao_Paulo', '-03:00', 0, 1339515296, 123456789],
|
|
['2013-06-13T12:34:56.123456789', 'America/St_Johns', '-02:30', 0, 1371135896, 123456789],
|
|
['2014-06-14T12:34:56.123456789', 'America/Noronha', '-02:00', 0, 1402756496, 123456789],
|
|
['2015-06-15T12:34:56.123456789', 'Atlantic/Cape_Verde', '-01:00', 0, 1434375296, 123456789],
|
|
['2016-12-16T12:34:56.123456789', 'Africa/Casablanca', '+00:00', 0, 1481891696, 123456789],
|
|
['2017-06-17T12:34:56.123456789', 'Europe/London', '+01:00', 0, 1497699296, 123456789],
|
|
['2019-06-19T12:34:56.123456789', 'Europe/Paris', '+02:00', 0, 1560940496, 123456789],
|
|
['2020-06-20T12:34:56.123456789', 'Africa/Nairobi', '+03:00', 0, 1592645696, 123456789],
|
|
['2001-12-21T12:34:56.123456789', 'Asia/Tehran', '+03:30', 0, 1008925496, 123456789],
|
|
['2002-12-22T12:34:56.123456789', 'Asia/Yerevan', '+04:00', 0, 1040546096, 123456789],
|
|
['2003-06-23T12:34:56.123456789', 'Asia/Tehran', '+04:30', 0, 1056355496, 123456789],
|
|
['2004-06-24T12:34:56.123456789', 'Asia/Dushanbe', '+05:00', 0, 1088062496, 123456789],
|
|
['2011-06-25T12:34:56.123456789', 'Asia/Colombo', '+05:30', 0, 1308985496, 123456789],
|
|
['2006-06-26T12:34:56.123456789', 'Asia/Kathmandu', '+05:45', 0, 1151304596, 123456789],
|
|
['2007-06-27T12:34:56.123456789', 'Asia/Dhaka', '+06:00', 0, 1182926096, 123456789],
|
|
['2008-06-28T12:34:56.123456789', 'Asia/Rangoon', '+06:30', 0, 1214633096, 123456789],
|
|
['2009-06-29T12:34:56.123456789', 'Asia/Bangkok', '+07:00', 0, 1246253696, 123456789],
|
|
['2010-06-30T12:34:56.123456789', 'Asia/Hong_Kong', '+08:00', 0, 1277872496, 123456789],
|
|
['2011-07-31T12:34:56.123456789', 'Australia/Eucla', '+08:45', 0, 1312084196, 123456789],
|
|
['2012-07-01T12:34:56.123456789', 'Asia/Seoul', '+09:00', 0, 1341113696, 123456789],
|
|
['2013-06-02T12:34:56.123456789', 'Australia/Adelaide', '+09:30', 0, 1370142296, 123456789],
|
|
['2014-06-03T12:34:56.123456789', 'Pacific/Port_Moresby', '+10:00', 0, 1401762896, 123456789],
|
|
['2015-06-04T12:34:56.123456789', 'Australia/Lord_Howe', '+10:30', 0, 1433383496, 123456789],
|
|
['2016-06-05T12:34:56.123456789', 'Pacific/Noumea', '+11:00', 0, 1465090496, 123456789],
|
|
['2017-06-06T12:34:56.123456789', 'Pacific/Norfolk', '+11:00', 0, 1496712896, 123456789],
|
|
['2018-06-07T12:34:56.123456789', 'Pacific/Auckland', '+12:00', 0, 1528331696, 123456789],
|
|
['2019-06-08T12:34:56.123456789', 'Pacific/Chatham', '+12:45', 0, 1559951396, 123456789],
|
|
['2020-06-09T12:34:56.123456789', 'Pacific/Apia', '+13:00', 0, 1591659296, 123456789],
|
|
['2001-06-10T12:34:56.123456789', 'Pacific/Kiritimati', '+14:00', 0, 992126096, 123456789],
|
|
|
|
// WITH REGION, OVERLAP: SUMMER TO WINTER DST TRANSITION
|
|
// If the date-time falls within the overlap (inclusive of the start time and exclusive of the end time),
|
|
// the region can be resolved to two different offsets; the offset closest to UTC is chosen.
|
|
// The date-time is resolved to an instant according to the resolved offset.
|
|
|
|
// America/Juneau: at 2:00 AM (-08:00) clocks are set backward 1 hour, to 1:00 AM (-09:00)
|
|
// Date-times falling between 1:00 AM and 2:00 AM on the transition day are resolved to -08:00
|
|
|
|
['2014-11-02T00:59:59.999999999', 'America/Juneau', '-08:00', 0, 1414918799, 999999999],
|
|
['2014-11-02T01:00:00.000000000', 'America/Juneau', '-08:00', 0, 1414918800, 0],
|
|
['2014-11-02T01:59:59.999999999', 'America/Juneau', '-08:00', 0, 1414922399, 999999999],
|
|
['2014-11-02T02:00:00.000000000', 'America/Juneau', '-09:00', 0, 1414926000, 0],
|
|
['2014-11-02T02:59:59.999999999', 'America/Juneau', '-09:00', 0, 1414929599, 999999999],
|
|
|
|
// America/Chicago: at 2:00 AM (-05:00) clocks are set backward 1 hour, to 1:00 AM (-06:00)
|
|
// Date-times falling between 1:00 AM and 2:00 AM on the transition day are resolved to -05:00
|
|
|
|
['2013-11-03T00:59:59.999999999', 'America/Chicago', '-05:00', 0, 1383458399, 999999999],
|
|
['2013-11-03T01:00:00.000000000', 'America/Chicago', '-05:00', 0, 1383458400, 0],
|
|
['2013-11-03T01:59:59.999999999', 'America/Chicago', '-05:00', 0, 1383461999, 999999999],
|
|
['2013-11-03T02:00:00.000000000', 'America/Chicago', '-06:00', 0, 1383465600, 0],
|
|
['2013-11-03T02:59:59.999999999', 'America/Chicago', '-06:00', 0, 1383469199, 999999999],
|
|
|
|
['2014-11-02T00:59:59.999999999', 'America/Chicago', '-05:00', 0, 1414907999, 999999999],
|
|
['2014-11-02T01:00:00.000000000', 'America/Chicago', '-05:00', 0, 1414908000, 0],
|
|
['2014-11-02T01:59:59.999999999', 'America/Chicago', '-05:00', 0, 1414911599, 999999999],
|
|
['2014-11-02T02:00:00.000000000', 'America/Chicago', '-06:00', 0, 1414915200, 0],
|
|
['2014-11-02T02:59:59.999999999', 'America/Chicago', '-06:00', 0, 1414918799, 999999999],
|
|
|
|
// America/Miquelon: at 2:00 AM (-02:00) clocks are set backward 1 hour, to 1:00 AM (-03:00)
|
|
// Date-times falling between 1:00 AM and 2:00 AM on the transition day are resolved to -02:00
|
|
|
|
['2013-11-03T00:59:59.999999999', 'America/Miquelon', '-02:00', 0, 1383447599, 999999999],
|
|
['2013-11-03T01:00:00.000000000', 'America/Miquelon', '-02:00', 0, 1383447600, 0],
|
|
['2013-11-03T01:59:59.999999999', 'America/Miquelon', '-02:00', 0, 1383451199, 999999999],
|
|
['2013-11-03T02:00:00.000000000', 'America/Miquelon', '-03:00', 0, 1383454800, 0],
|
|
['2013-11-03T02:59:59.999999999', 'America/Miquelon', '-03:00', 0, 1383458399, 999999999],
|
|
|
|
['2014-11-02T00:59:59.999999999', 'America/Miquelon', '-02:00', 0, 1414897199, 999999999],
|
|
['2014-11-02T01:00:00.000000000', 'America/Miquelon', '-02:00', 0, 1414897200, 0],
|
|
['2014-11-02T01:59:59.999999999', 'America/Miquelon', '-02:00', 0, 1414900799, 999999999],
|
|
['2014-11-02T02:00:00.000000000', 'America/Miquelon', '-03:00', 0, 1414904400, 0],
|
|
['2014-11-02T02:59:59.999999999', 'America/Miquelon', '-03:00', 0, 1414907999, 999999999],
|
|
|
|
// Europe/Paris: at 3:00 AM (+02:00) clocks are set backward 1 hour, to 2:00 AM (+01:00)
|
|
// Date-times falling between 2:00 AM and 3:00 AM on the transition day are resolved to +01:00
|
|
|
|
['2013-10-27T01:00:00.000000000', 'Europe/Paris', '+02:00', 0, 1382828400, 0],
|
|
['2013-10-27T01:59:59.999999999', 'Europe/Paris', '+02:00', 0, 1382831999, 999999999],
|
|
['2013-10-27T02:00:00.000000000', 'Europe/Paris', '+01:00', 0, 1382835600, 0],
|
|
['2013-10-27T02:59:59.999999999', 'Europe/Paris', '+01:00', 0, 1382839199, 999999999],
|
|
['2013-10-27T03:00:00.000000000', 'Europe/Paris', '+01:00', 0, 1382839200, 0],
|
|
|
|
['2014-10-26T01:00:00.000000000', 'Europe/Paris', '+02:00', 0, 1414278000, 0],
|
|
['2014-10-26T01:59:59.999999999', 'Europe/Paris', '+02:00', 0, 1414281599, 999999999],
|
|
['2014-10-26T02:00:00.000000000', 'Europe/Paris', '+01:00', 0, 1414285200, 0],
|
|
['2014-10-26T02:59:59.999999999', 'Europe/Paris', '+01:00', 0, 1414288799, 999999999],
|
|
['2014-10-26T03:00:00.000000000', 'Europe/Paris', '+01:00', 0, 1414288800, 0],
|
|
|
|
// Europe/Athens: at 4:00 AM (+03:00) clocks are set backward 1 hour, to 3:00 AM (+02:00)
|
|
// Date-times falling between 3:00 AM nd 4:00 AM on the transition day are resolved to +02:00
|
|
|
|
['2013-10-27T02:00:00.000000000', 'Europe/Athens', '+03:00', 0, 1382828400, 0],
|
|
['2013-10-27T02:59:59.999999999', 'Europe/Athens', '+03:00', 0, 1382831999, 999999999],
|
|
['2013-10-27T03:00:00.000000000', 'Europe/Athens', '+02:00', 0, 1382835600, 0],
|
|
['2013-10-27T03:59:59.999999999', 'Europe/Athens', '+02:00', 0, 1382839199, 999999999],
|
|
['2013-10-27T04:00:00.000000000', 'Europe/Athens', '+02:00', 0, 1382839200, 0],
|
|
|
|
['2014-10-26T02:00:00.000000000', 'Europe/Athens', '+03:00', 0, 1414278000, 0],
|
|
['2014-10-26T02:59:59.999999999', 'Europe/Athens', '+03:00', 0, 1414281599, 999999999],
|
|
['2014-10-26T03:00:00.000000000', 'Europe/Athens', '+02:00', 0, 1414285200, 0],
|
|
['2014-10-26T03:59:59.999999999', 'Europe/Athens', '+02:00', 0, 1414288799, 999999999],
|
|
['2014-10-26T04:00:00.000000000', 'Europe/Athens', '+02:00', 0, 1414288800, 0],
|
|
|
|
// Australia/Sydney: at 3:00 AM (+11:00) clocks are set backward 1 hour, to 2:00 AM (+10:00)
|
|
// Date-times falling between 2:00 AM and 3:00 AM on the transition day are resolved to +10:00
|
|
|
|
['2013-04-07T01:00:00.000000000', 'Australia/Sydney', '+11:00', 0, 1365256800, 0],
|
|
['2013-04-07T01:59:59.999999999', 'Australia/Sydney', '+11:00', 0, 1365260399, 999999999],
|
|
['2013-04-07T02:00:00.000000000', 'Australia/Sydney', '+10:00', 0, 1365264000, 0],
|
|
['2013-04-07T02:59:59.999999999', 'Australia/Sydney', '+10:00', 0, 1365267599, 999999999],
|
|
['2013-04-07T03:00:00.000000000', 'Australia/Sydney', '+10:00', 0, 1365267600, 0],
|
|
['2013-04-07T03:59:59.999999999', 'Australia/Sydney', '+10:00', 0, 1365271199, 999999999],
|
|
['2013-04-07T04:00:00.000000000', 'Australia/Sydney', '+10:00', 0, 1365271200, 0],
|
|
|
|
['2014-04-06T01:00:00.000000000', 'Australia/Sydney', '+11:00', 0, 1396706400, 0],
|
|
['2014-04-06T01:59:59.999999999', 'Australia/Sydney', '+11:00', 0, 1396709999, 999999999],
|
|
['2014-04-06T02:00:00.000000000', 'Australia/Sydney', '+10:00', 0, 1396713600, 0],
|
|
['2014-04-06T02:59:59.999999999', 'Australia/Sydney', '+10:00', 0, 1396717199, 999999999],
|
|
['2014-04-06T03:00:00.000000000', 'Australia/Sydney', '+10:00', 0, 1396717200, 0],
|
|
['2014-04-06T03:59:59.999999999', 'Australia/Sydney', '+10:00', 0, 1396720799, 999999999],
|
|
['2014-04-06T04:00:00.000000000', 'Australia/Sydney', '+10:00', 0, 1396720800, 0],
|
|
|
|
// WITH REGION, GAP: WINTER TO SUMMER DST TRANSITION
|
|
// If the date-time falls within the gap (inclusive of the start time and exclusive of the end time),
|
|
// the date-time is shifted forward by the length of the gap, and the region is resolved to
|
|
// the later offset (summer time).
|
|
|
|
// America/Juneau: at 2:00 AM (-09:00) clocks are set forward 1 hour, to 3:00 AM (-08:00)
|
|
// Date-times falling between 2:00 AM and 3:00 AM on the transition day are shifted forward 1 hour
|
|
|
|
['2014-03-09T01:59:59.999999999', 'America/Juneau', '-09:00', 0, 1394362799, 999999999],
|
|
['2014-03-09T02:00:00.000000000', 'America/Juneau', '-08:00', 3600, 1394362800, 0],
|
|
['2014-03-09T02:59:59.999999999', 'America/Juneau', '-08:00', 3600, 1394366399, 999999999],
|
|
['2014-03-09T03:00:00.000000000', 'America/Juneau', '-08:00', 0, 1394362800, 0],
|
|
['2014-03-09T03:59:59.999999999', 'America/Juneau', '-08:00', 0, 1394366399, 999999999],
|
|
|
|
// America/Chicago: at 2:00 AM (-06:00) clocks are set forward 1 hour, to 3:00 AM (-05:00)
|
|
// Date-times falling between 2:00 AM and 3:00 AM on the transition day are shifted forward 1 hour
|
|
|
|
['2013-03-10T01:59:59.999999999', 'America/Chicago', '-06:00', 0, 1362902399, 999999999],
|
|
['2013-03-10T02:00:00.000000000', 'America/Chicago', '-05:00', 3600, 1362902400, 0],
|
|
['2013-03-10T02:59:59.999999999', 'America/Chicago', '-05:00', 3600, 1362905999, 999999999],
|
|
['2013-03-10T03:00:00.000000000', 'America/Chicago', '-05:00', 0, 1362902400, 0],
|
|
['2013-03-10T03:59:59.999999999', 'America/Chicago', '-05:00', 0, 1362905999, 999999999],
|
|
|
|
['2014-03-09T01:59:59.999999999', 'America/Chicago', '-06:00', 0, 1394351999, 999999999],
|
|
['2014-03-09T02:00:00.000000000', 'America/Chicago', '-05:00', 3600, 1394352000, 0],
|
|
['2014-03-09T02:59:59.999999999', 'America/Chicago', '-05:00', 3600, 1394355599, 999999999],
|
|
['2014-03-09T03:00:00.000000000', 'America/Chicago', '-05:00', 0, 1394352000, 0],
|
|
['2014-03-09T03:59:59.999999999', 'America/Chicago', '-05:00', 0, 1394355599, 999999999],
|
|
|
|
// America/Miquelon: at 2:00 AM (-03:00) clocks are set forward 1 hour, to 3:00 AM (-02:00)
|
|
// Date-times falling between 2:00 AM and 3:00 AM on the transition day are shifted forward 1 hour
|
|
|
|
['2013-03-10T01:59:59.999999999', 'America/Miquelon', '-03:00', 0, 1362891599, 999999999],
|
|
['2013-03-10T02:00:00.000000000', 'America/Miquelon', '-02:00', 3600, 1362891600, 0],
|
|
['2013-03-10T02:59:59.999999999', 'America/Miquelon', '-02:00', 3600, 1362895199, 999999999],
|
|
['2013-03-10T03:00:00.000000000', 'America/Miquelon', '-02:00', 0, 1362891600, 0],
|
|
['2013-03-10T03:59:59.999999999', 'America/Miquelon', '-02:00', 0, 1362895199, 999999999],
|
|
|
|
['2014-03-09T01:59:59.999999999', 'America/Miquelon', '-03:00', 0, 1394341199, 999999999],
|
|
['2014-03-09T02:00:00.000000000', 'America/Miquelon', '-02:00', 3600, 1394341200, 0],
|
|
['2014-03-09T02:59:59.999999999', 'America/Miquelon', '-02:00', 3600, 1394344799, 999999999],
|
|
['2014-03-09T03:00:00.000000000', 'America/Miquelon', '-02:00', 0, 1394341200, 0],
|
|
['2014-03-09T03:59:59.999999999', 'America/Miquelon', '-02:00', 0, 1394344799, 999999999],
|
|
|
|
// Europe/Paris: at 2:00 AM (+01:00) clocks are set forward 1 hour, to 3:00 AM (+02:00)
|
|
// Date-times falling between 2:00 AM and 3:00 AM on the transition day are shifted forward 1 hour
|
|
|
|
['2013-03-31T01:59:59.999999999', 'Europe/Paris', '+01:00', 0, 1364691599, 999999999],
|
|
['2013-03-31T02:00:00.000000000', 'Europe/Paris', '+02:00', 3600, 1364691600, 0],
|
|
['2013-03-31T02:59:59.999999999', 'Europe/Paris', '+02:00', 3600, 1364695199, 999999999],
|
|
['2013-03-31T03:00:00.000000000', 'Europe/Paris', '+02:00', 0, 1364691600, 0],
|
|
['2013-03-31T03:59:59.999999999', 'Europe/Paris', '+02:00', 0, 1364695199, 999999999],
|
|
|
|
['2014-03-30T01:59:59.999999999', 'Europe/Paris', '+01:00', 0, 1396141199, 999999999],
|
|
['2014-03-30T02:00:00.000000000', 'Europe/Paris', '+02:00', 3600, 1396141200, 0],
|
|
['2014-03-30T02:59:59.999999999', 'Europe/Paris', '+02:00', 3600, 1396144799, 999999999],
|
|
['2014-03-30T03:00:00.000000000', 'Europe/Paris', '+02:00', 0, 1396141200, 0],
|
|
['2014-03-30T03:59:59.999999999', 'Europe/Paris', '+02:00', 0, 1396144799, 999999999],
|
|
|
|
// Europe/Athens: at 3:00 AM (+02:00) clocks are set forward 1 hour, to 4:00 AM (+03:00)
|
|
// Date-times falling between 3:00 AM and 4:00 AM on the transition day are shifted forward 1 hour
|
|
|
|
['2013-03-31T02:59:59.999999999', 'Europe/Athens', '+02:00', 0, 1364691599, 999999999],
|
|
['2013-03-31T03:00:00.000000000', 'Europe/Athens', '+03:00', 3600, 1364691600, 0],
|
|
['2013-03-31T03:59:59.999999999', 'Europe/Athens', '+03:00', 3600, 1364695199, 999999999],
|
|
['2013-03-31T04:00:00.000000000', 'Europe/Athens', '+03:00', 0, 1364691600, 0],
|
|
['2013-03-31T04:59:59.999999999', 'Europe/Athens', '+03:00', 0, 1364695199, 999999999],
|
|
|
|
['2014-03-30T02:59:59.999999999', 'Europe/Athens', '+02:00', 0, 1396141199, 999999999],
|
|
['2014-03-30T03:00:00.000000000', 'Europe/Athens', '+03:00', 3600, 1396141200, 0],
|
|
['2014-03-30T03:59:59.999999999', 'Europe/Athens', '+03:00', 3600, 1396144799, 999999999],
|
|
['2014-03-30T04:00:00.000000000', 'Europe/Athens', '+03:00', 0, 1396141200, 0],
|
|
['2014-03-30T04:59:59.999999999', 'Europe/Athens', '+03:00', 0, 1396144799, 999999999],
|
|
|
|
// Australia/Sydney: at 2:00 AM (+10:00) clocks are set forward 1 hour, to 3:00 AM (+11:00)
|
|
// Date-times falling between 2:00 AM and 3:00 AM on the transition day are shifted forward 1 hour
|
|
|
|
['2013-10-06T01:59:59.999999999', 'Australia/Sydney', '+10:00', 0, 1380988799, 999999999],
|
|
['2013-10-06T02:00:00.000000000', 'Australia/Sydney', '+11:00', 3600, 1380988800, 0],
|
|
['2013-10-06T02:59:59.999999999', 'Australia/Sydney', '+11:00', 3600, 1380992399, 999999999],
|
|
['2013-10-06T03:00:00.000000000', 'Australia/Sydney', '+11:00', 0, 1380988800, 0],
|
|
['2013-10-06T03:59:59.999999999', 'Australia/Sydney', '+11:00', 0, 1380992399, 999999999],
|
|
|
|
['2014-10-05T01:59:59.999999999', 'Australia/Sydney', '+10:00', 0, 1412438399, 999999999],
|
|
['2014-10-05T02:00:00.000000000', 'Australia/Sydney', '+11:00', 3600, 1412438400, 0],
|
|
['2014-10-05T02:59:59.999999999', 'Australia/Sydney', '+11:00', 3600, 1412441999, 999999999],
|
|
['2014-10-05T03:00:00.000000000', 'Australia/Sydney', '+11:00', 0, 1412438400, 0],
|
|
['2014-10-05T03:59:59.999999999', 'Australia/Sydney', '+11:00', 0, 1412441999, 999999999],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerOfInstant')]
|
|
public function testOfInstant(string $formattedDatetime, string $timeZone): void
|
|
{
|
|
$instant = Instant::of(1000000000);
|
|
$zonedDateTime = ZonedDateTime::ofInstant($instant, TimeZone::parse($timeZone));
|
|
|
|
self::assertSame(1000000000, $zonedDateTime->getInstant()->getEpochSecond());
|
|
self::assertSame($formattedDatetime, (string) $zonedDateTime->getDateTime());
|
|
}
|
|
|
|
public static function providerOfInstant(): array
|
|
{
|
|
return [
|
|
['2001-09-09T01:46:40', 'UTC'],
|
|
['2001-09-08T18:46:40', 'America/Los_Angeles'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @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.
|
|
*/
|
|
#[DataProvider('providerParse')]
|
|
public function testParse(string $text, string $date, string $time, string $offset, string $zone): void
|
|
{
|
|
$zonedDateTime = ZonedDateTime::parse($text);
|
|
|
|
self::assertSame($date, (string) $zonedDateTime->getDate());
|
|
self::assertSame($time, (string) $zonedDateTime->getTime());
|
|
self::assertSame($offset, (string) $zonedDateTime->getTimeZoneOffset());
|
|
self::assertSame($zone, (string) $zonedDateTime->getTimeZone());
|
|
}
|
|
|
|
public static function providerParse(): iterable
|
|
{
|
|
yield from [
|
|
['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', '01:02', '-03:00', '-03:00'],
|
|
['2001-02-03T01:02:03+04:00', '2001-02-03', '01:02:03', '+04:00', '+04:00'],
|
|
['2001-02-03T01:02:03.456+12:34', '2001-02-03', '01:02:03.456', '+12:34', '+12:34'],
|
|
['2001-02-03T01:02Z[Europe/London]', '2001-02-03', '01:02', 'Z', 'Europe/London'],
|
|
['2001-02-03T01:02+00:00[Europe/London]', '2001-02-03', '01:02', 'Z', 'Europe/London'],
|
|
['2001-02-03T01:02:03-00:00[Europe/London]', '2001-02-03', '01:02:03', 'Z', 'Europe/London'],
|
|
['2001-02-03T01:02:03.456+00:00[Europe/London]', '2001-02-03', '01:02:03.456', 'Z', 'Europe/London'],
|
|
];
|
|
|
|
if (PHP_VERSION_ID >= 80107) {
|
|
yield ['2001-02-03T01:02:03.456+12:34:56', '2001-02-03', '01:02:03.456', '+12:34:56', '+12:34:56'];
|
|
}
|
|
}
|
|
|
|
#[DataProvider('providerParseInvalidStringThrowsException')]
|
|
public function testParseInvalidStringThrowsException(string $text): void
|
|
{
|
|
$this->expectException(DateTimeParseException::class);
|
|
ZonedDateTime::parse($text);
|
|
}
|
|
|
|
public static function providerParseInvalidStringThrowsException(): array
|
|
{
|
|
return [
|
|
[''],
|
|
['2001'],
|
|
['2001-'],
|
|
['2001-02'],
|
|
['2001-02-'],
|
|
['2001-02-03'],
|
|
['2001-02-03T'],
|
|
['2001-02-03T04'],
|
|
['2001-02-03T04:'],
|
|
['2001-02-03T04:05'],
|
|
['2001-02-03T04:05:06'],
|
|
['2001-02-03T04:05:06.789'],
|
|
['2001-02-03T04:05Z[]'],
|
|
['2001-02-03T04:05[Europe/London]'],
|
|
['2001-02-03T04:05:06[Europe/London]'],
|
|
['2001-02-03T04:05.789Z[Europe/London]'],
|
|
['2001-02-03T04:05:06Z[Europe/London'],
|
|
|
|
[' 2001-02-03T01:02:03Z'],
|
|
['2001-02-03T01:02:03Z '],
|
|
];
|
|
}
|
|
|
|
public static function providerParseSecondsOffsetThrowsException(): array
|
|
{
|
|
return [
|
|
['2001-02-03T01:02:03.456+12:34:56'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerFromNativeDateTime')]
|
|
public function testFromNativeDateTime(string $dateTimeString, string $timeZone, string $expected): void
|
|
{
|
|
$dateTime = new DateTime($dateTimeString, new DateTimeZone($timeZone));
|
|
self::assertIs(ZonedDateTime::class, $expected, ZonedDateTime::fromNativeDateTime($dateTime));
|
|
}
|
|
|
|
public static function providerFromNativeDateTime(): array
|
|
{
|
|
return [
|
|
['2018-07-21 14:09:10.23456', 'America/Los_Angeles', '2018-07-21T14:09:10.23456-07:00[America/Los_Angeles]'],
|
|
['2019-01-21 17:59', 'America/Los_Angeles', '2019-01-21T17:59:00-08:00[America/Los_Angeles]'],
|
|
['2019-01-23 09:10:11.123', '+05:30', '2019-01-23T09:10:11.123+05:30'],
|
|
];
|
|
}
|
|
|
|
public function testChangeTimeZone(): void
|
|
{
|
|
$timezone1 = TimeZone::parse('UTC');
|
|
$timezone2 = TimeZone::parse('America/Los_Angeles');
|
|
|
|
$datetime1 = ZonedDateTime::ofInstant(Instant::of(1000000000), $timezone1);
|
|
$datetime2 = $datetime1->withTimeZoneSameInstant($timezone2);
|
|
|
|
self::assertSame($timezone1, $datetime1->getTimeZone());
|
|
self::assertSame($timezone2, $datetime2->getTimeZone());
|
|
self::assertSame('2001-09-08T18:46:40', (string) $datetime2->getDateTime());
|
|
|
|
$datetime2 = $datetime1->withTimeZoneSameLocal($timezone2);
|
|
|
|
self::assertSame($timezone1, $datetime1->getTimeZone());
|
|
self::assertSame($timezone2, $datetime2->getTimeZone());
|
|
self::assertSame('2001-09-09T01:46:40', (string) $datetime2->getDateTime());
|
|
}
|
|
|
|
/**
|
|
* @param string $z1 The first zoned date-time.
|
|
* @param string $z2 The second zoned date-time.
|
|
* @param int $cmp The comparison value.
|
|
*/
|
|
#[DataProvider('providerCompareTo')]
|
|
public function testCompareTo(string $z1, string $z2, int $cmp): void
|
|
{
|
|
$z1 = ZonedDateTime::parse($z1);
|
|
$z2 = ZonedDateTime::parse($z2);
|
|
|
|
self::assertSame($cmp, $z1->compareTo($z2));
|
|
self::assertSame($cmp === 0, $z1->isEqualTo($z2));
|
|
self::assertSame($cmp === -1, $z1->isBefore($z2));
|
|
self::assertSame($cmp === 1, $z1->isAfter($z2));
|
|
self::assertSame($cmp <= 0, $z1->isBeforeOrEqualTo($z2));
|
|
self::assertSame($cmp >= 0, $z1->isAfterOrEqualTo($z2));
|
|
|
|
self::assertSame(-$cmp, $z2->compareTo($z1));
|
|
self::assertSame($cmp === 0, $z2->isEqualTo($z1));
|
|
self::assertSame($cmp === 1, $z2->isBefore($z1));
|
|
self::assertSame($cmp === -1, $z2->isAfter($z1));
|
|
self::assertSame($cmp >= 0, $z2->isBeforeOrEqualTo($z1));
|
|
self::assertSame($cmp <= 0, $z2->isAfterOrEqualTo($z1));
|
|
}
|
|
|
|
public static function providerCompareTo(): array
|
|
{
|
|
return [
|
|
['2020-06-06T14:30:30Z', '2014-12-31T23:59:59.999Z', 1],
|
|
['2020-06-06T14:30:30Z', '2020-06-06T14:30:30+00:00', 0],
|
|
['2020-06-06T14:30:30Z', '2020-06-06T14:29:29.999999999+00:00', 1],
|
|
['2020-06-06T14:30:30Z', '2020-06-06T14:30:30.000000000+00:00', 0],
|
|
['2020-06-06T14:30:30Z', '2020-06-06T14:30:30.000000001+00:00', -1],
|
|
['2020-06-06T14:30:30Z', '2020-06-06T15:30:30+01:00', 0],
|
|
['2020-06-06T14:30:30Z', '2020-06-06T16:00:30+01:30', 0],
|
|
['2020-06-06T14:30:30Z', '2020-06-06T15:30:30+02:00', 1],
|
|
['2020-06-06T14:30:30Z', '2020-06-06T13:30:30-02:00', -1],
|
|
];
|
|
}
|
|
|
|
public function testGetYear(): void
|
|
{
|
|
self::assertSame(2000, $this->getTestZonedDateTime()->getYear());
|
|
}
|
|
|
|
#[DataProvider('providerGetMonth')]
|
|
public function testGetMonth(int $monthValue, Month $month): void
|
|
{
|
|
$zonedDateTime = ZonedDateTime::of(
|
|
LocalDateTime::of(2000, $monthValue, 1),
|
|
TimeZone::utc(),
|
|
);
|
|
|
|
self::assertSame($month, $zonedDateTime->getMonth());
|
|
}
|
|
|
|
public static function providerGetMonth(): array
|
|
{
|
|
return [
|
|
[1, Month::JANUARY],
|
|
[2, Month::FEBRUARY],
|
|
[3, Month::MARCH],
|
|
[4, Month::APRIL],
|
|
[5, Month::MAY],
|
|
[6, Month::JUNE],
|
|
[7, Month::JULY],
|
|
[8, Month::AUGUST],
|
|
[9, Month::SEPTEMBER],
|
|
[10, Month::OCTOBER],
|
|
[11, Month::NOVEMBER],
|
|
[12, Month::DECEMBER],
|
|
];
|
|
}
|
|
|
|
public function testGetMonthValue(): void
|
|
{
|
|
self::assertSame(1, $this->getTestZonedDateTime()->getMonthValue());
|
|
}
|
|
|
|
public function testGetDayOfMonth(): void
|
|
{
|
|
self::assertSame(20, $this->getTestZonedDateTime()->getDayOfMonth());
|
|
}
|
|
|
|
public function testGetDayOfWeek(): void
|
|
{
|
|
self::assertSame(DayOfWeek::THURSDAY, $this->getTestZonedDateTime()->getDayOfWeek());
|
|
}
|
|
|
|
public function testGetDayOfYear(): void
|
|
{
|
|
self::assertSame(20, $this->getTestZonedDateTime()->getDayOfYear());
|
|
}
|
|
|
|
public function testGetHour(): void
|
|
{
|
|
self::assertSame(12, $this->getTestZonedDateTime()->getHour());
|
|
}
|
|
|
|
public function testGetMinute(): void
|
|
{
|
|
self::assertSame(34, $this->getTestZonedDateTime()->getMinute());
|
|
}
|
|
|
|
public function testGetSecond(): void
|
|
{
|
|
self::assertSame(56, $this->getTestZonedDateTime()->getSecond());
|
|
}
|
|
|
|
public function testWithDate(): void
|
|
{
|
|
$newDate = LocalDate::of(2000, 1, 22);
|
|
|
|
self::assertIs(ZonedDateTime::class, '2000-01-22T12:34:56.123456789-08:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withDate($newDate));
|
|
}
|
|
|
|
public function testWithTime(): void
|
|
{
|
|
$time = LocalTime::of(1, 2, 3, 987654321);
|
|
|
|
self::assertIs(ZonedDateTime::class, '2000-01-20T01:02:03.987654321-08:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withTime($time));
|
|
}
|
|
|
|
public function testWithYear(): void
|
|
{
|
|
self::assertIs(ZonedDateTime::class, '2020-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withYear(2020));
|
|
}
|
|
|
|
public function testWithMonth(): void
|
|
{
|
|
self::assertIs(ZonedDateTime::class, '2000-07-20T12:34:56.123456789-07:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withMonth(7));
|
|
self::assertIs(ZonedDateTime::class, '2000-07-20T12:34:56.123456789-07:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withMonth(Month::JULY));
|
|
}
|
|
|
|
public function testWithDay(): void
|
|
{
|
|
self::assertIs(ZonedDateTime::class, '2000-01-31T12:34:56.123456789-08:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withDay(31));
|
|
}
|
|
|
|
public function testWithHour(): void
|
|
{
|
|
self::assertIs(ZonedDateTime::class, '2000-01-20T23:34:56.123456789-08:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withHour(23));
|
|
}
|
|
|
|
public function testWithMinute(): void
|
|
{
|
|
self::assertIs(ZonedDateTime::class, '2000-01-20T12:00:56.123456789-08:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withMinute(0));
|
|
}
|
|
|
|
public function testWithSecond(): void
|
|
{
|
|
self::assertIs(ZonedDateTime::class, '2000-01-20T12:34:06.123456789-08:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withSecond(6));
|
|
}
|
|
|
|
public function testWithNano(): void
|
|
{
|
|
self::assertIs(ZonedDateTime::class, '2000-01-20T12:34:56.000000123-08:00[America/Los_Angeles]', $this->getTestZonedDateTime()->withNano(123));
|
|
}
|
|
|
|
public function testWithFixedOffsetTimeZone(): void
|
|
{
|
|
self::assertIs(ZonedDateTime::class, '2000-01-20T12:34:56.123456789-08:00', $this->getTestZonedDateTime()->withFixedOffsetTimeZone());
|
|
}
|
|
|
|
#[DataProvider('providerPlusPeriod')]
|
|
public function testPlusPeriod(string $zonedDateTime, Period $plusPeriod, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusPeriod($plusPeriod);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusPeriod(): Generator
|
|
{
|
|
yield from [
|
|
['2000-03-29T12:34:56.123456789-08:00[America/Los_Angeles]', Period::ofDays(3), '2000-04-01T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-03-29T12:34:56.123456789-08:00[America/Los_Angeles]', Period::ofDays(4), '2000-04-02T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-04-03T12:34:56.123456789-07:00[America/Los_Angeles]', Period::ofDays(-1), '2000-04-02T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-04-03T12:34:56.123456789-07:00[America/Los_Angeles]', Period::ofDays(-2), '2000-04-01T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Period::ofWeeks(10), '2000-03-30T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Period::ofWeeks(11), '2000-04-06T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-04-06T12:34:56.123456789-07:00[America/Los_Angeles]', Period::ofWeeks(-2), '2000-03-23T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-02-16T12:34:56.123456789-08:00[America/Los_Angeles]', Period::ofMonths(1), '2000-03-16T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-02-16T12:34:56.123456789-08:00[America/Los_Angeles]', Period::ofMonths(2), '2000-04-16T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-05-16T12:34:56.123456789-07:00[America/Los_Angeles]', Period::ofMonths(-1), '2000-04-16T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-05-16T12:34:56.123456789-07:00[America/Los_Angeles]', Period::ofMonths(-2), '2000-03-16T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-02-16T12:34:56.123456789-07:00[America/Los_Angeles]', Period::of(1, -2, 3), '2000-12-19T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
];
|
|
|
|
foreach (self::providerPlusDays() as [$zonedDateTime, $plusDays, $expected]) {
|
|
yield [$zonedDateTime, Period::ofDays($plusDays), $expected];
|
|
}
|
|
|
|
foreach (self::providerPlusWeeks() as [$zonedDateTime, $plusWeeks, $expected]) {
|
|
yield [$zonedDateTime, Period::ofWeeks($plusWeeks), $expected];
|
|
}
|
|
|
|
foreach (self::providerPlusMonths() as [$zonedDateTime, $plusMonths, $expected]) {
|
|
yield [$zonedDateTime, Period::ofMonths($plusMonths), $expected];
|
|
}
|
|
|
|
foreach (self::providerPlusYears() as [$zonedDateTime, $plusYears, $expected]) {
|
|
yield [$zonedDateTime, Period::ofYears($plusYears), $expected];
|
|
}
|
|
}
|
|
|
|
#[DataProvider('providerPlusDuration')]
|
|
public function testPlusDuration(string $zonedDateTime, Duration $plusDuration, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusDuration($plusDuration);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusDuration(): Generator
|
|
{
|
|
yield ['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Duration::ofSeconds(-5), '2000-01-20T12:34:51.123456789-08:00[America/Los_Angeles]'];
|
|
yield ['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Duration::ofSeconds(5), '2000-01-20T12:35:01.123456789-08:00[America/Los_Angeles]'];
|
|
yield ['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Duration::ofMinutes(-10), '2000-01-20T12:24:56.123456789-08:00[America/Los_Angeles]'];
|
|
yield ['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Duration::ofMinutes(10), '2000-01-20T12:44:56.123456789-08:00[America/Los_Angeles]'];
|
|
yield ['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Duration::ofHours(-24), '2000-01-19T12:34:56.123456789-08:00[America/Los_Angeles]'];
|
|
yield ['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Duration::ofHours(24), '2000-01-21T12:34:56.123456789-08:00[America/Los_Angeles]'];
|
|
yield ['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', Duration::ofSeconds(1, 5000), '2000-01-20T12:34:57.123461789-08:00[America/Los_Angeles]'];
|
|
|
|
foreach (self::providerPlusSeconds() as [$zonedDateTime, $plusSeconds, $expected]) {
|
|
yield [$zonedDateTime, Duration::ofSeconds($plusSeconds), $expected];
|
|
}
|
|
|
|
foreach (self::providerPlusMinutes() as [$zonedDateTime, $plusMinutes, $expected]) {
|
|
yield [$zonedDateTime, Duration::ofMinutes($plusMinutes), $expected];
|
|
}
|
|
|
|
foreach (self::providerPlusHours() as [$zonedDateTime, $plusHours, $expected]) {
|
|
yield [$zonedDateTime, Duration::ofHours($plusHours), $expected];
|
|
}
|
|
}
|
|
|
|
#[DataProvider('providerPlusYears')]
|
|
public function testPlusYears(string $zonedDateTime, int $plusYears, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusYears($plusYears);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusYears(): array
|
|
{
|
|
return [
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', -2, '1998-01-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', -1, '1999-01-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', 0, '2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', 1, '2001-01-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', 2, '2002-01-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerPlusMonths')]
|
|
public function testPlusMonths(string $zonedDateTime, int $plusMonths, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusMonths($plusMonths);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusMonths(): array
|
|
{
|
|
return [
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', -4, '1999-09-20T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', -3, '1999-10-20T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', -2, '1999-11-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', -1, '1999-12-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', 0, '2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', 1, '2000-02-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', 2, '2000-03-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', 3, '2000-04-20T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]', 4, '2000-05-20T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerPlusWeeks')]
|
|
public function testPlusWeeks(string $zonedDateTime, int $plusWeeks, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusWeeks($plusWeeks);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusWeeks(): array
|
|
{
|
|
return [
|
|
['2000-01-01T12:34:56.123456789-08:00[America/Los_Angeles]', -25, '1999-07-10T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-01-01T12:34:56.123456789-08:00[America/Los_Angeles]', -1, '1999-12-25T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-01T12:34:56.123456789-08:00[America/Los_Angeles]', 0, '2000-01-01T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-01T12:34:56.123456789-08:00[America/Los_Angeles]', 1, '2000-01-08T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-01T12:34:56.123456789-08:00[America/Los_Angeles]', 25, '2000-06-24T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
|
|
// https://github.com/brick/date-time/issues/115
|
|
['2025-03-23T01:00+01:00[Europe/Prague]', 1, '2025-03-30T01:00:00+01:00[Europe/Prague]'],
|
|
['2025-03-23T01:30+01:00[Europe/Prague]', 1, '2025-03-30T01:30:00+01:00[Europe/Prague]'],
|
|
['2025-03-23T02:00+01:00[Europe/Prague]', 1, '2025-03-30T03:00:00+02:00[Europe/Prague]'],
|
|
['2025-03-23T02:30+01:00[Europe/Prague]', 1, '2025-03-30T03:30:00+02:00[Europe/Prague]'],
|
|
['2025-03-23T03:00+01:00[Europe/Prague]', 1, '2025-03-30T03:00:00+02:00[Europe/Prague]'],
|
|
['2025-03-23T03:30+01:00[Europe/Prague]', 1, '2025-03-30T03:30:00+02:00[Europe/Prague]'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerPlusDays')]
|
|
public function testPlusDays(string $zonedDateTime, int $plusDays, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusDays($plusDays);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusDays(): array
|
|
{
|
|
return [
|
|
['2000-03-31T12:34:56.123456789-08:00[America/Los_Angeles]', -1, '2000-03-30T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-03-31T12:34:56.123456789-08:00[America/Los_Angeles]', 0, '2000-03-31T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-03-31T12:34:56.123456789-08:00[America/Los_Angeles]', 1, '2000-04-01T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-03-31T12:34:56.123456789-08:00[America/Los_Angeles]', 2, '2000-04-02T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-03-31T12:34:56.123456789-08:00[America/Los_Angeles]', 3, '2000-04-03T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
|
|
['2000-04-03T12:34:56.123456789-07:00[America/Los_Angeles]', -1, '2000-04-02T12:34:56.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-04-03T12:34:56.123456789-07:00[America/Los_Angeles]', -2, '2000-04-01T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
|
|
// https://github.com/brick/date-time/issues/115
|
|
['2025-03-29T01:00+01:00[Europe/Prague]', 1, '2025-03-30T01:00:00+01:00[Europe/Prague]'],
|
|
['2025-03-29T01:30+01:00[Europe/Prague]', 1, '2025-03-30T01:30:00+01:00[Europe/Prague]'],
|
|
['2025-03-29T02:00+01:00[Europe/Prague]', 1, '2025-03-30T03:00:00+02:00[Europe/Prague]'],
|
|
['2025-03-29T02:30+01:00[Europe/Prague]', 1, '2025-03-30T03:30:00+02:00[Europe/Prague]'],
|
|
['2025-03-29T03:00+01:00[Europe/Prague]', 1, '2025-03-30T03:00:00+02:00[Europe/Prague]'],
|
|
['2025-03-29T03:30+01:00[Europe/Prague]', 1, '2025-03-30T03:30:00+02:00[Europe/Prague]'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerPlusHours')]
|
|
public function testPlusHours(string $zonedDateTime, int $plusHours, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusHours($plusHours);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusHours(): array
|
|
{
|
|
return [
|
|
['2000-04-01T23:12:34.123456789-08:00[America/Los_Angeles]', -1, '2000-04-01T22:12:34.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-04-01T23:12:34.123456789-08:00[America/Los_Angeles]', 0, '2000-04-01T23:12:34.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-04-01T23:12:34.123456789-08:00[America/Los_Angeles]', 1, '2000-04-02T00:12:34.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-04-01T23:12:34.123456789-08:00[America/Los_Angeles]', 2, '2000-04-02T01:12:34.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-04-01T23:12:34.123456789-08:00[America/Los_Angeles]', 3, '2000-04-02T03:12:34.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-04-01T23:12:34.123456789-08:00[America/Los_Angeles]', 4, '2000-04-02T04:12:34.123456789-07:00[America/Los_Angeles]'],
|
|
|
|
['2000-04-02T04:12:34.123456789-07:00[America/Los_Angeles]', -3, '2000-04-02T00:12:34.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T04:12:34.123456789-07:00[America/Los_Angeles]', -2, '2000-04-02T01:12:34.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T04:12:34.123456789-07:00[America/Los_Angeles]', -1, '2000-04-02T03:12:34.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T04:12:34.123456789-07:00[America/Los_Angeles]', 0, '2000-04-02T04:12:34.123456789-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T04:12:34.123456789-07:00[America/Los_Angeles]', 1, '2000-04-02T05:12:34.123456789-07:00[America/Los_Angeles]'],
|
|
|
|
// https://github.com/brick/date-time/issues/115
|
|
['2025-03-29T01:00+01:00[Europe/Prague]', 24, '2025-03-30T01:00:00+01:00[Europe/Prague]'],
|
|
['2025-03-29T01:30+01:00[Europe/Prague]', 24, '2025-03-30T01:30:00+01:00[Europe/Prague]'],
|
|
['2025-03-29T02:00+01:00[Europe/Prague]', 24, '2025-03-30T03:00:00+02:00[Europe/Prague]'],
|
|
['2025-03-29T02:30+01:00[Europe/Prague]', 24, '2025-03-30T03:30:00+02:00[Europe/Prague]'],
|
|
['2025-03-29T03:00+01:00[Europe/Prague]', 24, '2025-03-30T04:00:00+02:00[Europe/Prague]'],
|
|
['2025-03-29T03:30+01:00[Europe/Prague]', 24, '2025-03-30T04:30:00+02:00[Europe/Prague]'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerPlusMinutes')]
|
|
public static function testPlusMinutes(string $zonedDateTime, int $plusMinutes, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusMinutes($plusMinutes);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusMinutes(): array
|
|
{
|
|
return [
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', -2, '2000-04-02T01:57:59.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', -1, '2000-04-02T01:58:59.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', 0, '2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', 1, '2000-04-02T03:00:59.654321-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', 2, '2000-04-02T03:01:59.654321-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', 3, '2000-04-02T03:02:59.654321-07:00[America/Los_Angeles]'],
|
|
|
|
['2000-04-02T03:01:01.654321-07:00[America/Los_Angeles]', -3, '2000-04-02T01:58:01.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T03:01:01.654321-07:00[America/Los_Angeles]', -2, '2000-04-02T01:59:01.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T03:01:01.654321-07:00[America/Los_Angeles]', -1, '2000-04-02T03:00:01.654321-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T03:01:01.654321-07:00[America/Los_Angeles]', 0, '2000-04-02T03:01:01.654321-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T03:01:01.654321-07:00[America/Los_Angeles]', 1, '2000-04-02T03:02:01.654321-07:00[America/Los_Angeles]'],
|
|
|
|
// https://github.com/brick/date-time/issues/115
|
|
['2025-03-30T01:30:00+01[Europe/Prague]', 50, '2025-03-30T03:20:00+02:00[Europe/Prague]'],
|
|
['2025-03-30T01:30:00+01[Europe/Prague]', 100, '2025-03-30T04:10:00+02:00[Europe/Prague]'],
|
|
['2025-03-30T03:20:00+02[Europe/Prague]', -50, '2025-03-30T01:30:00+01:00[Europe/Prague]'],
|
|
['2025-03-30T04:10:00+02[Europe/Prague]', -100, '2025-03-30T01:30:00+01:00[Europe/Prague]'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerPlusSeconds')]
|
|
public function testPlusSeconds(string $zonedDateTime, int $plusSeconds, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($zonedDateTime)->plusSeconds($plusSeconds);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public static function providerPlusSeconds(): array
|
|
{
|
|
return [
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', -2, '2000-04-02T01:59:57.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', -1, '2000-04-02T01:59:58.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', 0, '2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', 1, '2000-04-02T03:00:00.654321-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]', 2, '2000-04-02T03:00:01.654321-07:00[America/Los_Angeles]'],
|
|
|
|
['2000-04-02T03:00:01.654321-07:00[America/Los_Angeles]', -3, '2000-04-02T01:59:58.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T03:00:01.654321-07:00[America/Los_Angeles]', -2, '2000-04-02T01:59:59.654321-08:00[America/Los_Angeles]'],
|
|
['2000-04-02T03:00:01.654321-07:00[America/Los_Angeles]', -1, '2000-04-02T03:00:00.654321-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T03:00:01.654321-07:00[America/Los_Angeles]', 0, '2000-04-02T03:00:01.654321-07:00[America/Los_Angeles]'],
|
|
['2000-04-02T03:00:01.654321-07:00[America/Los_Angeles]', 1, '2000-04-02T03:00:02.654321-07:00[America/Los_Angeles]'],
|
|
|
|
// https://github.com/brick/date-time/issues/115
|
|
['2025-03-30T01:30:00+01[Europe/Prague]', 3000, '2025-03-30T03:20:00+02:00[Europe/Prague]'],
|
|
['2025-03-30T01:30:00+01[Europe/Prague]', 6000, '2025-03-30T04:10:00+02:00[Europe/Prague]'],
|
|
['2025-03-30T03:20:00+02[Europe/Prague]', -3000, '2025-03-30T01:30:00+01:00[Europe/Prague]'],
|
|
['2025-03-30T04:10:00+02[Europe/Prague]', -6000, '2025-03-30T01:30:00+01:00[Europe/Prague]'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerPlusPeriod')]
|
|
public function testMinusPeriod(string $dateTime, Period $plusPeriod, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusPeriod($plusPeriod->negated());
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
#[DataProvider('providerPlusDuration')]
|
|
public function testMinusDuration(string $dateTime, Duration $plusDuration, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusDuration($plusDuration->negated());
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
#[DataProvider('providerPlusYears')]
|
|
public function testMinusYears(string $dateTime, int $plusYears, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusYears(-$plusYears);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
#[DataProvider('providerPlusMonths')]
|
|
public function testMinusMonths(string $dateTime, int $plusMonths, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusMonths(-$plusMonths);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
#[DataProvider('providerPlusWeeks')]
|
|
public function testMinusWeeks(string $dateTime, int $plusWeeks, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusWeeks(-$plusWeeks);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
#[DataProvider('providerPlusDays')]
|
|
public function testMinusDays(string $dateTime, int $plusDays, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusDays(-$plusDays);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
#[DataProvider('providerPlusHours')]
|
|
public function testMinusHours(string $dateTime, int $plusHours, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusHours(-$plusHours);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
#[DataProvider('providerPlusMinutes')]
|
|
public function testMinusMinutes(string $dateTime, int $plusMinutes, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusMinutes(-$plusMinutes);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
#[DataProvider('providerPlusSeconds')]
|
|
public function testMinusSeconds(string $dateTime, int $plusSeconds, string $expected): void
|
|
{
|
|
$actual = ZonedDateTime::parse($dateTime)->minusSeconds(-$plusSeconds);
|
|
self::assertIs(ZonedDateTime::class, $expected, $actual);
|
|
}
|
|
|
|
public function testIsBetweenInclusive(): void
|
|
{
|
|
$timeZone = TimeZone::parse('America/Los_Angeles');
|
|
$localDateTime = '2000-01-20T12:34:56.123456789';
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$fromZonedDateTime = ZonedDateTime::of($localDateTime, $timeZone);
|
|
|
|
$localDateTime = '2015-01-20T12:34:56.123456789';
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$toZonedDateTime = ZonedDateTime::of($localDateTime, $timeZone);
|
|
|
|
$localDateTime = '2018-01-20T12:34:56.123456789';
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$notIncluZonedDateTime = ZonedDateTime::of($localDateTime, $timeZone);
|
|
|
|
self::assertTrue($fromZonedDateTime->isBetweenInclusive($fromZonedDateTime, $toZonedDateTime));
|
|
self::assertFalse($fromZonedDateTime->isBetweenInclusive($toZonedDateTime, $notIncluZonedDateTime));
|
|
}
|
|
|
|
public function testIsBetweenExclusive(): void
|
|
{
|
|
$timeZone = TimeZone::parse('America/Los_Angeles');
|
|
$localDateTime = '2000-01-20T12:34:56.123456789';
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$fromZonedDateTime = ZonedDateTime::of($localDateTime, $timeZone);
|
|
|
|
$localDateTime = '2015-01-20T12:34:56.123456789';
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$toZonedDateTime = ZonedDateTime::of($localDateTime, $timeZone);
|
|
|
|
$localDateTime = '2014-01-20T12:34:56.123456789';
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$incluZonedDateTime = ZonedDateTime::of($localDateTime, $timeZone);
|
|
|
|
self::assertTrue($incluZonedDateTime->isBetweenExclusive($fromZonedDateTime, $toZonedDateTime));
|
|
self::assertFalse($fromZonedDateTime->isBetweenExclusive($fromZonedDateTime, $toZonedDateTime));
|
|
}
|
|
|
|
#[DataProvider('providerForPastFuture')]
|
|
public function testIsFuture(int $clockTimestamp, string $zonedDateTime, bool $isFuture): void
|
|
{
|
|
$clock = new FixedClock(Instant::of($clockTimestamp));
|
|
$zonedDateTime = ZonedDateTime::parse($zonedDateTime);
|
|
self::assertSame($isFuture, $zonedDateTime->isFuture($clock));
|
|
}
|
|
|
|
#[DataProvider('providerForPastFuture')]
|
|
public function testIsPast(int $clockTimestamp, string $zonedDateTime, bool $isFuture): void
|
|
{
|
|
$clock = new FixedClock(Instant::of($clockTimestamp));
|
|
$zonedDateTime = ZonedDateTime::parse($zonedDateTime);
|
|
self::assertSame(! $isFuture, $zonedDateTime->isPast($clock));
|
|
}
|
|
|
|
public static function providerForPastFuture(): array
|
|
{
|
|
return [
|
|
[1234567890, '2009-02-14T00:31:29+01:00', false],
|
|
[1234567890, '2009-02-14T00:31:31+01:00', true],
|
|
[2345678901, '2044-04-30T17:28:20-08:00', false],
|
|
[2345678901, '2044-04-30T17:28:22-08:00', true],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param string $dateTime The date-time string that will be parse()d by ZonedDateTime.
|
|
* @param string $expected The expected output from the native DateTime object.
|
|
*/
|
|
#[DataProvider('providerToNativeDateTime')]
|
|
public function testToNativeDateTime(string $dateTime, string $expected): void
|
|
{
|
|
$zonedDateTime = ZonedDateTime::parse($dateTime);
|
|
$dateTime = $zonedDateTime->toNativeDateTime();
|
|
|
|
self::assertInstanceOf(DateTime::class, $dateTime);
|
|
self::assertSame($expected, $dateTime->format('Y-m-d\TH:i:s.uO'));
|
|
}
|
|
|
|
/**
|
|
* @param string $dateTime The date-time string that will be parse()d by ZonedDateTime.
|
|
* @param string $expected The expected output from the native DateTime object.
|
|
*/
|
|
#[DataProvider('providerToNativeDateTime')]
|
|
public function testToNativeDateTimeImmutable(string $dateTime, string $expected): void
|
|
{
|
|
$zonedDateTime = ZonedDateTime::parse($dateTime);
|
|
$dateTime = $zonedDateTime->toNativeDateTimeImmutable();
|
|
|
|
self::assertInstanceOf(DateTimeImmutable::class, $dateTime);
|
|
self::assertSame($expected, $dateTime->format('Y-m-d\TH:i:s.uO'));
|
|
}
|
|
|
|
public static function providerToNativeDateTime(): array
|
|
{
|
|
return [
|
|
['2018-10-18T12:34Z', '2018-10-18T12:34:00.000000+0000'],
|
|
['2018-10-18T12:34:56Z', '2018-10-18T12:34:56.000000+0000'],
|
|
['2018-10-18T12:34:00.001Z', '2018-10-18T12:34:00.001000+0000'],
|
|
['2018-10-18T12:34:56.123002Z', '2018-10-18T12:34:56.123002+0000'],
|
|
['2011-07-31T23:59:59+01:00', '2011-07-31T23:59:59.000000+0100'],
|
|
['2011-07-31T23:59:59.02-05:30', '2011-07-31T23:59:59.020000-0530'],
|
|
['2011-07-31T23:59:59+01:00[Europe/London]', '2011-07-31T23:59:59.000000+0100'],
|
|
['2011-07-31T23:59:59.000123456-07:00', '2011-07-31T23:59:59.000123-0700'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerToString')]
|
|
public function testJsonSerialize(string $localDateTime, string $timeZone, string $expectedString): void
|
|
{
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$zonedDateTime = ZonedDateTime::of($localDateTime, TimeZone::parse($timeZone));
|
|
|
|
self::assertSame(json_encode($expectedString, JSON_THROW_ON_ERROR), json_encode($zonedDateTime, JSON_THROW_ON_ERROR));
|
|
}
|
|
|
|
#[DataProvider('providerToString')]
|
|
public function testToISOString(string $localDateTime, string $timeZone, string $expectedString): void
|
|
{
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$zonedDateTime = ZonedDateTime::of($localDateTime, TimeZone::parse($timeZone));
|
|
|
|
self::assertSame($expectedString, $zonedDateTime->toISOString());
|
|
}
|
|
|
|
#[DataProvider('providerToString')]
|
|
public function testToString(string $localDateTime, string $timeZone, string $expectedString): void
|
|
{
|
|
$localDateTime = LocalDateTime::parse($localDateTime);
|
|
$zonedDateTime = ZonedDateTime::of($localDateTime, TimeZone::parse($timeZone));
|
|
|
|
self::assertSame($expectedString, (string) $zonedDateTime);
|
|
}
|
|
|
|
public static function providerToString(): array
|
|
{
|
|
return [
|
|
['2000-01-20T12:34:56.123456789', 'America/Los_Angeles', '2000-01-20T12:34:56.123456789-08:00[America/Los_Angeles]'],
|
|
['2000-01-20T12:34:56.123456789', '-07:00', '2000-01-20T12:34:56.123456789-07:00'],
|
|
// whole minute and whole hour must keep the full ":00" seconds (RFC 3339 section 5.6)
|
|
['2000-01-20T12:34', '-07:00', '2000-01-20T12:34:00-07:00'],
|
|
['2000-01-20T12:00', 'America/Los_Angeles', '2000-01-20T12:00:00-08:00[America/Los_Angeles]'],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerGetDurationTo')]
|
|
public function testGetDurationTo(string $firstDate, string $secondDate, int $expectedSeconds, int $expectedNanos): void
|
|
{
|
|
$actualResult = ZonedDateTime::parse($firstDate)->getDurationTo(ZonedDateTime::parse($secondDate));
|
|
|
|
self::assertDurationIs($expectedSeconds, $expectedNanos, $actualResult);
|
|
}
|
|
|
|
public static function providerGetDurationTo(): array
|
|
{
|
|
return [
|
|
['2023-01-01T10:00:00Z', '2023-01-01T10:00:00Z', 0, 0],
|
|
['2023-01-01T10:00:00Z', '2023-01-01T10:00:10Z', 10, 0],
|
|
['2023-01-01T10:00:00.001Z', '2023-01-01T10:00:10.002Z', 10, 1000000],
|
|
['2023-01-01T10:00:00.001Z', '2023-01-01T13:00:10.002+03:00', 10, 1000000],
|
|
['2023-01-01T10:00:00.000000001Z', '2023-01-01T10:00:00.000000009Z', 0, 8],
|
|
['2023-01-01T10:00:00Z', '2023-01-02T10:00:00Z', 24 * 60 * 60, 0],
|
|
['2023-01-02T10:00:00Z', '2023-01-01T10:00:00Z', -24 * 60 * 60, 0],
|
|
];
|
|
}
|
|
|
|
#[DataProvider('providerGetIntervalTo')]
|
|
public function testGetIntervalTo(string $firstDate, string $secondDate, string $expectedInterval): void
|
|
{
|
|
$actualResult = ZonedDateTime::parse($firstDate)->getIntervalTo(ZonedDateTime::parse($secondDate));
|
|
|
|
self::assertSame($expectedInterval, (string) $actualResult);
|
|
}
|
|
|
|
public static function providerGetIntervalTo(): array
|
|
{
|
|
return [
|
|
['2023-01-01T10:00:00Z', '2023-01-01T10:00:00Z', '2023-01-01T10:00:00Z/2023-01-01T10:00:00Z'],
|
|
['2023-01-01T10:00:00Z', '2023-01-01T10:00:10Z', '2023-01-01T10:00:00Z/2023-01-01T10:00:10Z'],
|
|
['2023-01-01T10:00:00.001Z', '2023-01-01T10:00:10.002Z', '2023-01-01T10:00:00.001Z/2023-01-01T10:00:10.002Z'],
|
|
['2023-01-01T10:00:00.001Z', '2023-01-01T13:00:10.002+03:00', '2023-01-01T10:00:00.001Z/2023-01-01T10:00:10.002Z'],
|
|
['2023-01-01T10:00:00.001+03:00', '2023-01-01T13:00:10.002+03:00', '2023-01-01T07:00:00.001Z/2023-01-01T10:00:10.002Z'],
|
|
['2023-01-01T10:00:00.000000001Z', '2023-01-01T10:00:00.000000009Z', '2023-01-01T10:00:00.000000001Z/2023-01-01T10:00:00.000000009Z'],
|
|
['2023-01-01T10:00:00Z', '2023-01-02T10:00:00Z', '2023-01-01T10:00:00Z/2023-01-02T10:00:00Z'],
|
|
];
|
|
}
|
|
|
|
private function getTestZonedDateTime(): ZonedDateTime
|
|
{
|
|
$timeZone = TimeZone::parse('America/Los_Angeles');
|
|
$localDateTime = LocalDateTime::parse('2000-01-20T12:34:56.123456789');
|
|
|
|
return ZonedDateTime::of($localDateTime, $timeZone);
|
|
}
|
|
}
|