Skip to content

Commit

Permalink
implemented safe versions of the classes DateTime and DateTimeImmutable
Browse files Browse the repository at this point in the history
  • Loading branch information
Kharhamel committed Sep 18, 2019
1 parent 277af72 commit 9f1c30c
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 0 deletions.
55 changes: 55 additions & 0 deletions generator/tests/DateTimeImmutableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php


namespace Safe;


use PHPUnit\Framework\TestCase;
use Safe\Exceptions\DatetimeException;

class DateTimeImmutableTest extends TestCase
{
protected function setUp()
{
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
require_once __DIR__ . '/../../generated/Exceptions/DatetimeException.php';
require_once __DIR__ . '/../../lib/DateTimeImmutable.php';
}

public function testSafeDatetimeImmutableCrashOnError(): void
{
$this->expectException(DatetimeException::class);
$datetime = DateTimeImmutable::createFromFormat('lol', 'super');
}

public function testSafeDatetimeImmutablePreserveTimeAndTimezone(): void
{
$timezone = new \DateTimeZone('Pacific/Chatham');
$datetime = DateTimeImmutable::createFromFormat('d-m-Y', '20-03-2006', $timezone);
$this->assertInstanceOf(DateTimeImmutable::class, $datetime);
$this->assertEquals('20-03-2006', $datetime->format('d-m-Y'));
$this->assertEquals($timezone, $datetime->getTimezone());
}

public function testSafeDatetimeImmutableSetDate(): void
{
$datetime = new DateTimeImmutable();
$datetime = $datetime->setDate(2017, 4, 6);
$this->assertInstanceOf(DateTimeImmutable::class, $datetime);
$this->assertEquals(2017, $datetime->format('Y'));
$this->assertEquals(4, $datetime->format('n'));
$this->assertEquals(6, $datetime->format('j'));

//todo: test an error case
}

public function testSafeDatetimeImmutableModify(): void
{
$datetime = new DateTimeImmutable();
$datetime = $datetime->setDate(2017, 4, 6);
$datetime = $datetime->modify('+1 day');
$this->assertInstanceOf(DateTimeImmutable::class, $datetime);
$this->assertEquals('7-4-2017', $datetime->format('j-n-Y'));
}
}
55 changes: 55 additions & 0 deletions generator/tests/DateTimeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php


namespace Safe;


use PHPUnit\Framework\TestCase;
use Safe\Exceptions\DatetimeException;

class DateTimeTest extends TestCase
{
protected function setUp()
{
require_once __DIR__ . '/../../lib/Exceptions/SafeExceptionInterface.php';
require_once __DIR__ . '/../../lib/Exceptions/AbstractSafeException.php';
require_once __DIR__ . '/../../generated/Exceptions/DatetimeException.php';
require_once __DIR__ . '/../../lib/DateTime.php';
}

public function testSafeDatetimeCrashOnError(): void
{
$this->expectException(DatetimeException::class);
$datetime = DateTime::createFromFormat('lol', 'super');
}

public function testSafeDatetimePreserveTimeAndTimezone(): void
{
$timezone = new \DateTimeZone('Pacific/Chatham');
$datetime = DateTime::createFromFormat('d-m-Y', '20-03-2006', $timezone);
$this->assertInstanceOf(DateTime::class, $datetime);
$this->assertEquals('20-03-2006', $datetime->format('d-m-Y'));
$this->assertEquals($timezone, $datetime->getTimezone());
}

public function testSafeDatetimeSetDate(): void
{
$datetime = new DateTime();
$datetime = $datetime->setDate(2017, 4, 6);
$this->assertInstanceOf(DateTime::class, $datetime);
$this->assertEquals(2017, $datetime->format('Y'));
$this->assertEquals(4, $datetime->format('n'));
$this->assertEquals(6, $datetime->format('j'));

//todo: test an error case
}

public function testSafeDatetimeModify(): void
{
$datetime = new DateTime();
$datetime = $datetime->setDate(2017, 4, 6);
$datetime = $datetime->modify('+1 day');
$this->assertInstanceOf(DateTime::class, $datetime);
$this->assertEquals('7-4-2017', $datetime->format('j-n-Y'));
}
}
1 change: 1 addition & 0 deletions generator/tests/SpecialCasesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

class SpecialCasesTest extends TestCase
{

public function testPregReplace()
{
require_once __DIR__.'/../../lib/special_cases.php';
Expand Down
72 changes: 72 additions & 0 deletions lib/DateTime.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace Safe;

use DateInterval;
use DateTimeInterface;
use DateTimeZone;
use Safe\Exceptions\DatetimeException;

//this class is used to implement a safe version of the Datetime
class DateTime extends \DateTime
{
//switch from regular datetime to safe version
private static function createFromRegular(\DateTime $datetime): self
{
return new self($datetime->format('Y-m-d H:i:s'), $datetime->getTimezone());
}

public static function createFromFormat($format, $time, DateTimeZone $timezone = null): self
{
$datetime = parent::createFromFormat($format, $time, $timezone);
if ($datetime === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($datetime);
}

/**
* @param DateTimeInterface $datetime2 The date to compare to.
* @param boolean $absolute [optional] Whether to return absolute difference.
* @return DateInterval The DateInterval object representing the difference between the two dates.
*/
public function diff($datetime2, $absolute = false): DateInterval
{
/** @var \DateInterval|false $result */
$result = parent::diff($datetime2, $absolute);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return $result;
}

/**
* @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>.
* @return DateTime Returns the DateTime object for method chaining.
*/
public function modify($modify): self
{
/** @var DateTime|false $result */
$result = parent::modify($modify);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return $result;
}

/**
* @param int $year
* @param int $month
* @param int $day
* @return DateTime
*/
public function setDate($year, $month, $day): self
{
/** @var DateTime|false $result */
$result = parent::setDate($year, $month, $day);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return $result;
}
}
151 changes: 151 additions & 0 deletions lib/DateTimeImmutable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace Safe;

use DateInterval;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Safe\Exceptions\DatetimeException;

//this class is used to implement a safe version of the DatetimeImmutable class
class DateTimeImmutable extends \DateTimeImmutable
{
//switch from regular datetime to safe version
private static function createFromRegular(\DateTimeImmutable $datetime): self
{
return new self($datetime->format('Y-m-d H:i:s'), $datetime->getTimezone());
}

public static function createFromFormat($format, $time, DateTimeZone $timezone = null): self
{
$datetime = parent::createFromFormat($format, $time, $timezone);
if ($datetime === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($datetime);
}

public function format($format): string
{
$result = parent::format($format);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return $result;
}

/**
* @param DateTimeInterface $datetime2 <p>The date to compare to.</p>
* @param bool $absolute [optional] <p>Should the interval be forced to be positive?</p>
* @return DateInterval
*/
public function diff($datetime2, $absolute = false): DateInterval
{
/** @var \DateInterval|false $result */
$result = parent::diff($datetime2, $absolute);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return $result;
}

/**
* @param string $modify <p>A date/time string. Valid formats are explained in
* {@link https://secure.php.net/manual/en/datetime.formats.php Date and Time Formats}.</p>
* @return DateTimeImmutable
*/
public function modify($modify): self
{
/** @var \DateTimeImmutable|false $result */
$result = parent::modify($modify);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
}

/**
* @param int $year <p>Year of the date.</p>
* @param int $month <p>Month of the date.</p>
* @param int $day <p>Day of the date.</p>
* @return DateTimeImmutable
*
*/
public function setDate($year, $month, $day): self
{
/** @var \DateTimeImmutable|false $result */
$result = parent::setDate($year, $month, $day);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
}

public function setISODate($year, $week, $day = 1): self
{
/** @var \DateTimeImmutable|false $result */
$result = parent::setISODate($year, $week, $day);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($result); //we have to recreate a safe datetime because modify create a new instance of \DateTimeImmutable
}

public function setTime($hour, $minute, $second = 0, $microseconds = 0): self
{
/** @var \DateTimeImmutable|false $result */
$result = parent::setTime($hour, $minute, $second, $microseconds);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($result);
}

public function setTimestamp($unixtimestamp): self
{
/** @var \DateTimeImmutable|false $result */
$result = parent::setTimestamp($unixtimestamp);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($result);
}

public function setTimezone($timezone): self
{
/** @var \DateTimeImmutable|false $result */
$result = parent::setTimezone($timezone);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($result);
}

public function sub($interval): self
{
/** @var \DateTimeImmutable|false $result */
$result = parent::sub($interval);
if ($result === false) {
throw DatetimeException::createFromPhpError();
}
return self::createFromRegular($result);
}

//theses functions are overload to actually return a safe instance, since datetimeimmutable re-instante itself

public function add($interval): self
{
return self::createFromRegular(parent::add($interval));
}

public static function createFromMutable($dateTime): self
{
return self::createFromRegular(parent::createFromMutable($dateTime));
}

public static function __set_state(array $array): self
{
return self::createFromRegular(parent::__set_state($array));
}
}

0 comments on commit 9f1c30c

Please sign in to comment.