Skip to content

Commit 9f1c30c

Browse files
committed
implemented safe versions of the classes DateTime and DateTimeImmutable
1 parent 277af72 commit 9f1c30c

File tree

5 files changed

+334
-0
lines changed

5 files changed

+334
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
4+
namespace Safe;
5+
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Safe\Exceptions\DatetimeException;
9+
10+
class DateTimeImmutableTest extends TestCase
11+
{
12+
protected function setUp()
13+
{
14+
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
15+
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
16+
require_once __DIR__ . '/../../generated/Exceptions/DatetimeException.php';
17+
require_once __DIR__ . '/../../lib/DateTimeImmutable.php';
18+
}
19+
20+
public function testSafeDatetimeImmutableCrashOnError(): void
21+
{
22+
$this->expectException(DatetimeException::class);
23+
$datetime = DateTimeImmutable::createFromFormat('lol', 'super');
24+
}
25+
26+
public function testSafeDatetimeImmutablePreserveTimeAndTimezone(): void
27+
{
28+
$timezone = new \DateTimeZone('Pacific/Chatham');
29+
$datetime = DateTimeImmutable::createFromFormat('d-m-Y', '20-03-2006', $timezone);
30+
$this->assertInstanceOf(DateTimeImmutable::class, $datetime);
31+
$this->assertEquals('20-03-2006', $datetime->format('d-m-Y'));
32+
$this->assertEquals($timezone, $datetime->getTimezone());
33+
}
34+
35+
public function testSafeDatetimeImmutableSetDate(): void
36+
{
37+
$datetime = new DateTimeImmutable();
38+
$datetime = $datetime->setDate(2017, 4, 6);
39+
$this->assertInstanceOf(DateTimeImmutable::class, $datetime);
40+
$this->assertEquals(2017, $datetime->format('Y'));
41+
$this->assertEquals(4, $datetime->format('n'));
42+
$this->assertEquals(6, $datetime->format('j'));
43+
44+
//todo: test an error case
45+
}
46+
47+
public function testSafeDatetimeImmutableModify(): void
48+
{
49+
$datetime = new DateTimeImmutable();
50+
$datetime = $datetime->setDate(2017, 4, 6);
51+
$datetime = $datetime->modify('+1 day');
52+
$this->assertInstanceOf(DateTimeImmutable::class, $datetime);
53+
$this->assertEquals('7-4-2017', $datetime->format('j-n-Y'));
54+
}
55+
}

generator/tests/DateTimeTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
4+
namespace Safe;
5+
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Safe\Exceptions\DatetimeException;
9+
10+
class DateTimeTest extends TestCase
11+
{
12+
protected function setUp()
13+
{
14+
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
15+
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
16+
require_once __DIR__ . '/../../generated/Exceptions/DatetimeException.php';
17+
require_once __DIR__ . '/../../lib/DateTime.php';
18+
}
19+
20+
public function testSafeDatetimeCrashOnError(): void
21+
{
22+
$this->expectException(DatetimeException::class);
23+
$datetime = DateTime::createFromFormat('lol', 'super');
24+
}
25+
26+
public function testSafeDatetimePreserveTimeAndTimezone(): void
27+
{
28+
$timezone = new \DateTimeZone('Pacific/Chatham');
29+
$datetime = DateTime::createFromFormat('d-m-Y', '20-03-2006', $timezone);
30+
$this->assertInstanceOf(DateTime::class, $datetime);
31+
$this->assertEquals('20-03-2006', $datetime->format('d-m-Y'));
32+
$this->assertEquals($timezone, $datetime->getTimezone());
33+
}
34+
35+
public function testSafeDatetimeSetDate(): void
36+
{
37+
$datetime = new DateTime();
38+
$datetime = $datetime->setDate(2017, 4, 6);
39+
$this->assertInstanceOf(DateTime::class, $datetime);
40+
$this->assertEquals(2017, $datetime->format('Y'));
41+
$this->assertEquals(4, $datetime->format('n'));
42+
$this->assertEquals(6, $datetime->format('j'));
43+
44+
//todo: test an error case
45+
}
46+
47+
public function testSafeDatetimeModify(): void
48+
{
49+
$datetime = new DateTime();
50+
$datetime = $datetime->setDate(2017, 4, 6);
51+
$datetime = $datetime->modify('+1 day');
52+
$this->assertInstanceOf(DateTime::class, $datetime);
53+
$this->assertEquals('7-4-2017', $datetime->format('j-n-Y'));
54+
}
55+
}

generator/tests/SpecialCasesTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
class SpecialCasesTest extends TestCase
99
{
10+
1011
public function testPregReplace()
1112
{
1213
require_once __DIR__.'/../../lib/special_cases.php';

lib/DateTime.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace Safe;
4+
5+
use DateInterval;
6+
use DateTimeInterface;
7+
use DateTimeZone;
8+
use Safe\Exceptions\DatetimeException;
9+
10+
//this class is used to implement a safe version of the Datetime
11+
class DateTime extends \DateTime
12+
{
13+
//switch from regular datetime to safe version
14+
private static function createFromRegular(\DateTime $datetime): self
15+
{
16+
return new self($datetime->format('Y-m-d H:i:s'), $datetime->getTimezone());
17+
}
18+
19+
public static function createFromFormat($format, $time, DateTimeZone $timezone = null): self
20+
{
21+
$datetime = parent::createFromFormat($format, $time, $timezone);
22+
if ($datetime === false) {
23+
throw DatetimeException::createFromPhpError();
24+
}
25+
return self::createFromRegular($datetime);
26+
}
27+
28+
/**
29+
* @param DateTimeInterface $datetime2 The date to compare to.
30+
* @param boolean $absolute [optional] Whether to return absolute difference.
31+
* @return DateInterval The DateInterval object representing the difference between the two dates.
32+
*/
33+
public function diff($datetime2, $absolute = false): DateInterval
34+
{
35+
/** @var \DateInterval|false $result */
36+
$result = parent::diff($datetime2, $absolute);
37+
if ($result === false) {
38+
throw DatetimeException::createFromPhpError();
39+
}
40+
return $result;
41+
}
42+
43+
/**
44+
* @param string $modify A date/time string. Valid formats are explained in <a href="https://secure.php.net/manual/en/datetime.formats.php">Date and Time Formats</a>.
45+
* @return DateTime Returns the DateTime object for method chaining.
46+
*/
47+
public function modify($modify): self
48+
{
49+
/** @var DateTime|false $result */
50+
$result = parent::modify($modify);
51+
if ($result === false) {
52+
throw DatetimeException::createFromPhpError();
53+
}
54+
return $result;
55+
}
56+
57+
/**
58+
* @param int $year
59+
* @param int $month
60+
* @param int $day
61+
* @return DateTime
62+
*/
63+
public function setDate($year, $month, $day): self
64+
{
65+
/** @var DateTime|false $result */
66+
$result = parent::setDate($year, $month, $day);
67+
if ($result === false) {
68+
throw DatetimeException::createFromPhpError();
69+
}
70+
return $result;
71+
}
72+
}

lib/DateTimeImmutable.php

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
3+
namespace Safe;
4+
5+
use DateInterval;
6+
use DateTime;
7+
use DateTimeInterface;
8+
use DateTimeZone;
9+
use Safe\Exceptions\DatetimeException;
10+
11+
//this class is used to implement a safe version of the DatetimeImmutable class
12+
class DateTimeImmutable extends \DateTimeImmutable
13+
{
14+
//switch from regular datetime to safe version
15+
private static function createFromRegular(\DateTimeImmutable $datetime): self
16+
{
17+
return new self($datetime->format('Y-m-d H:i:s'), $datetime->getTimezone());
18+
}
19+
20+
public static function createFromFormat($format, $time, DateTimeZone $timezone = null): self
21+
{
22+
$datetime = parent::createFromFormat($format, $time, $timezone);
23+
if ($datetime === false) {
24+
throw DatetimeException::createFromPhpError();
25+
}
26+
return self::createFromRegular($datetime);
27+
}
28+
29+
public function format($format): string
30+
{
31+
$result = parent::format($format);
32+
if ($result === false) {
33+
throw DatetimeException::createFromPhpError();
34+
}
35+
return $result;
36+
}
37+
38+
/**
39+
* @param DateTimeInterface $datetime2 <p>The date to compare to.</p>
40+
* @param bool $absolute [optional] <p>Should the interval be forced to be positive?</p>
41+
* @return DateInterval
42+
*/
43+
public function diff($datetime2, $absolute = false): DateInterval
44+
{
45+
/** @var \DateInterval|false $result */
46+
$result = parent::diff($datetime2, $absolute);
47+
if ($result === false) {
48+
throw DatetimeException::createFromPhpError();
49+
}
50+
return $result;
51+
}
52+
53+
/**
54+
* @param string $modify <p>A date/time string. Valid formats are explained in
55+
* {@link https://secure.php.net/manual/en/datetime.formats.php Date and Time Formats}.</p>
56+
* @return DateTimeImmutable
57+
*/
58+
public function modify($modify): self
59+
{
60+
/** @var \DateTimeImmutable|false $result */
61+
$result = parent::modify($modify);
62+
if ($result === false) {
63+
throw DatetimeException::createFromPhpError();
64+
}
65+
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
66+
}
67+
68+
/**
69+
* @param int $year <p>Year of the date.</p>
70+
* @param int $month <p>Month of the date.</p>
71+
* @param int $day <p>Day of the date.</p>
72+
* @return DateTimeImmutable
73+
*
74+
*/
75+
public function setDate($year, $month, $day): self
76+
{
77+
/** @var \DateTimeImmutable|false $result */
78+
$result = parent::setDate($year, $month, $day);
79+
if ($result === false) {
80+
throw DatetimeException::createFromPhpError();
81+
}
82+
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
83+
}
84+
85+
public function setISODate($year, $week, $day = 1): self
86+
{
87+
/** @var \DateTimeImmutable|false $result */
88+
$result = parent::setISODate($year, $week, $day);
89+
if ($result === false) {
90+
throw DatetimeException::createFromPhpError();
91+
}
92+
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
93+
}
94+
95+
public function setTime($hour, $minute, $second = 0, $microseconds = 0): self
96+
{
97+
/** @var \DateTimeImmutable|false $result */
98+
$result = parent::setTime($hour, $minute, $second, $microseconds);
99+
if ($result === false) {
100+
throw DatetimeException::createFromPhpError();
101+
}
102+
return self::createFromRegular($result);
103+
}
104+
105+
public function setTimestamp($unixtimestamp): self
106+
{
107+
/** @var \DateTimeImmutable|false $result */
108+
$result = parent::setTimestamp($unixtimestamp);
109+
if ($result === false) {
110+
throw DatetimeException::createFromPhpError();
111+
}
112+
return self::createFromRegular($result);
113+
}
114+
115+
public function setTimezone($timezone): self
116+
{
117+
/** @var \DateTimeImmutable|false $result */
118+
$result = parent::setTimezone($timezone);
119+
if ($result === false) {
120+
throw DatetimeException::createFromPhpError();
121+
}
122+
return self::createFromRegular($result);
123+
}
124+
125+
public function sub($interval): self
126+
{
127+
/** @var \DateTimeImmutable|false $result */
128+
$result = parent::sub($interval);
129+
if ($result === false) {
130+
throw DatetimeException::createFromPhpError();
131+
}
132+
return self::createFromRegular($result);
133+
}
134+
135+
//theses functions are overload to actually return a safe instance, since datetimeimmutable re-instante itself
136+
137+
public function add($interval): self
138+
{
139+
return self::createFromRegular(parent::add($interval));
140+
}
141+
142+
public static function createFromMutable($dateTime): self
143+
{
144+
return self::createFromRegular(parent::createFromMutable($dateTime));
145+
}
146+
147+
public static function __set_state(array $array): self
148+
{
149+
return self::createFromRegular(parent::__set_state($array));
150+
}
151+
}

0 commit comments

Comments
 (0)