Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept strings or util.TimeZone instances #347

Merged
merged 4 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 50 additions & 22 deletions src/main/php/util/Date.class.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php namespace util;

use DateTime;
use DateTime, DateTimeZone;
use lang\{IllegalArgumentException, IllegalStateException, Value};

/**
Expand Down Expand Up @@ -38,28 +38,36 @@ static function __static() {
* timezone is used.
*
* @param ?int|float|string|DateTime $in
* @param ?util.TimeZone $timezone default NULL string of timezone
* @param ?string|util.TimeZone $timezone
* @throws lang.IllegalArgumentException in case the date is unparseable
*/
public function __construct($in= null, ?TimeZone $timezone= null) {
public function __construct($in= null, $timezone= null) {
if (null === $timezone) {
$tz= null;
} else if ($timezone instanceof TimeZone) {
$tz= $timezone->getHandle();
} else {
$tz= new DateTimeZone($timezone);
}

if (null === $in) {
$this->handle= date_create('now', $timezone ? $timezone->getHandle() : null);
$this->handle= date_create('now', $tz);
} else if ($in instanceof DateTime) {
$this->handle= $in;
} else if (is_int($in) || (string)(int)$in === $in) {

// Specially mark timestamps for parsing (we assume here that strings
// containing only digits are timestamps)
$this->handle= date_create('@'.$in);
$timezone && date_timezone_set($this->handle, $timezone->getHandle());
$tz && date_timezone_set($this->handle, $tz);
} else if (is_float($in)) {

// Timestamps with microseconds are defined as `"@" "-"? [0-9]+ "." [0-9]{0,6}`,
// see https://www.php.net/manual/en/datetime.formats.php#datetime.formats.relative
$this->handle= date_create('@'.sprintf('%.6f', $in));
$timezone && date_timezone_set($this->handle, $timezone->getHandle());
$tz && date_timezone_set($this->handle, $tz);
} else {
if (false === ($this->handle= date_create($in ?? 'now', $timezone ? $timezone->getHandle() : null))) {
if (false === ($this->handle= date_create($in ?? 'now', $tz))) {
throw new IllegalArgumentException('Given argument is neither a timestamp nor a well-formed timestring: '.Objects::stringOf($in));
}
}
Expand Down Expand Up @@ -96,13 +104,16 @@ public function __unserialize($data) {
* @param int $hour
* @param int $minute
* @param int $second
* @param ?util.TimeZone $tz default NULL
* @param ?string|util.TimeZone $timezone
* @return self
*/
public static function create($year, $month, $day, $hour, $minute, $second, ?TimeZone $tz= null): self {
$date= date_create();
if ($tz) {
date_timezone_set($date, $tz->getHandle());
public static function create($year, $month, $day, $hour, $minute, $second, $timezone= null): self {
if (null === $timezone) {
$date= date_create();
} else if ($timezone instanceof TimeZone) {
$date= date_create('now', $timezone->getHandle());
} else {
$date= date_create('now', new DateTimeZone($timezone));
}

try {
Expand Down Expand Up @@ -133,9 +144,9 @@ public function equals($cmp): bool {
return $cmp instanceof self && $this->getTime() === $cmp->getTime();
}

/** Static method to get current date/time */
public static function now(?TimeZone $tz= null): self {
return new self(null, $tz);
/** @param ?string|util.TimeZone $timezone */
public static function now($timezone= null): self {
return new self(null, $timezone);
}

/** Compare this date to another date */
Expand Down Expand Up @@ -199,11 +210,20 @@ public function getTimeZone(): Timezone {
*
* @see php://date
* @param string $format default Date::DEFAULT_FORMAT format-string
* @param ?util.TimeZone $outtz default NULL
* @param ?string|util.TimeZone $timezone
* @return string the formatted date
*/
public function toString(string $format= self::DEFAULT_FORMAT, ?TimeZone $outtz= null): string {
return date_format(($outtz === null ? $this : $outtz->translate($this))->handle, $format);
public function toString(string $format= self::DEFAULT_FORMAT, $timezone= null): string {
if (null === $timezone) {
$handle= $this->handle;
} else if ($timezone instanceof TimeZone) {
$handle= clone $this->handle;
date_timezone_set($handle, $timezone->getHandle());
} else {
$handle= clone $this->handle;
date_timezone_set($handle, new DateTimeZone($timezone));
}
return date_format($handle, $format);
}

/**
Expand All @@ -214,11 +234,10 @@ public function toString(string $format= self::DEFAULT_FORMAT, ?TimeZone $outtz=
*
* @see php://strftime
* @param string $format
* @param ?util.TimeZone $outtz default NULL
* @param ?string|util.TimeZone $timezone
* @return string
* @throws lang.IllegalArgumentException if unsupported token has been given
*/
public function format(string $format, ?TimeZone $outtz= null): string {
public function format(string $format, $timezone= null): string {
static $replace= [
'%d' => 'd',
'%m' => 'm',
Expand Down Expand Up @@ -253,6 +272,15 @@ public function format(string $format, ?TimeZone $outtz= null): string {
'%%' => '%'
];

return date_format(($outtz === null ? $this : $outtz->translate($this))->handle, strtr($format, $replace));
if (null === $timezone) {
$handle= $this->handle;
} else if ($timezone instanceof TimeZone) {
$handle= clone $this->handle;
date_timezone_set($handle, $timezone->getHandle());
} else {
$handle= clone $this->handle;
date_timezone_set($handle, new DateTimeZone($timezone));
}
return date_format($handle, strtr($format, $replace));
}
}
48 changes: 32 additions & 16 deletions src/test/php/util/unittest/DateTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
class DateTest {
private $nowTime, $nowDate, $refDate, $tz;

/** @return iterable */
private function timezones() {
yield 'Europe/Berlin';
yield new TimeZone('Europe/Berlin');
}

#[Before]
public function setUp() {

Expand Down Expand Up @@ -51,11 +57,11 @@ public function constructorUnixtimestampWithoutTz() {
$this->assertDateEquals('2007-08-23T12:35:47+00:00', new Date(1187872547));
}

#[Test]
public function constructorUnixtimestampWithTz() {
$this->assertDateEquals('2007-08-23T14:35:47+02:00', new Date(1187872547, new TimeZone('Europe/Berlin')));
#[Test, Values(from: 'timezones')]
public function constructorUnixtimestampWithTz($tz) {
$this->assertDateEquals('2007-08-23T14:35:47+02:00', new Date(1187872547, $tz));
}

#[Test]
public function constructorParseTz() {
$date= new Date('2007-01-01 01:00:00 Europe/Berlin');
Expand Down Expand Up @@ -157,7 +163,12 @@ public function dateCreate() {
// Test with a date before 1971
Assert::equals(-44668800, Date::create(1968, 8, 2, 0, 0, 0)->getTime());
}


#[Test, Values(from: 'timezones')]
public function create_date_with_timezone($tz) {
Assert::equals(-44672400, Date::create(1968, 8, 2, 0, 0, 0, $tz)->getTime());
}

#[Test]
public function pre1970() {
$this->assertDateEquals('1969-02-01T00:00:00+00:00', new Date('01.02.1969'));
Expand Down Expand Up @@ -268,21 +279,29 @@ public function emptyTimeZoneNameIfUnknown() {
}

#[Test]
public function toStringOutput() {
$date= new Date('2007-11-10 20:15+0100');
Assert::equals('2007-11-10 20:15:00+0100', $date->toString());
Assert::equals('2007-11-10 19:15:00+0000', $date->toString(Date::DEFAULT_FORMAT, new TimeZone('GMT')));
public function string_representation() {
Assert::equals(
'2007-11-10 20:15:00+0100',
(new Date('2007-11-10 20:15+0100'))->toString(Date::DEFAULT_FORMAT)
);
}

#[Test, Values(from: 'timezones')]
public function string_representation_with_timezone($timezone) {
Assert::equals(
'2007-11-10 20:15:00+0100',
(new Date('2007-11-10 19:15+0000'))->toString(Date::DEFAULT_FORMAT, $timezone)
);
}

#[Test]
public function toStringOutputPreserved() {
public function timezone_preserved_during_serialization() {
$date= unserialize(serialize(new Date('2007-11-10 20:15+0100')));
Assert::equals('2007-11-10 20:15:00+0100', $date->toString());
Assert::equals('2007-11-10 19:15:00+0000', $date->toString(Date::DEFAULT_FORMAT, new TimeZone('GMT')));
}

#[Test, Expect(IllegalArgumentException::class)]
public function malformedInputString() {
public function malformed_input_string() {
new Date('@@not-a-date@@');
}

Expand All @@ -309,10 +328,7 @@ public function unknownTimeZoneOffsetInString() {
#[Test]
public function constructorBrokenAfterException() {
Date::now();
try {
new Date('bogus');
$this->fail('No exception raised', null, IllegalArgumentException::class);
} catch (\lang\IllegalArgumentException $expected) { }
Assert::throws(IllegalArgumentException::class, fn() => new Date('bogus'));
Date::now();
}

Expand Down
Loading