Permalink
Browse files

DCOM-67 - Add immutable DateTime support and DateTimeFactory that uti…

…lizes immutability to reuse DateTime instances created from format.
  • Loading branch information...
1 parent 3655b6c commit 7140ad3ba0ba2a94238976dd7f310ff92b478c96 @beberlei beberlei committed Aug 27, 2011
@@ -0,0 +1,138 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Common\DateTime;
+
+/**
+ * Immutable DateTime class.
+ *
+ * Instead of modifying the original instance the methods #add(), #sub() and
+ * #modify() return new instances instead.
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.org
+ * @since 3.0
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class DateTime extends \DateTime
+{
+ /**
+ * Add interval to date by returning a new DateTime instance.
+ *
+ * The original DateTime instance is not modified.
+ *
+ * @param string $interval
+ * @return DateTime
+ */
+ public function add($interval)
+ {
+ $newDate = clone $this;
+ return \date_add($newDate, $interval);
+ }
+
+ /**
+ * Modify DateTime by returning a new DateTime instance.
+ *
+ * The original DateTime instance is not modified.
+ *
+ * @param string $modify
+ * @return DateTime
+ */
+ public function modify($modify)
+ {
+ $newDate = clone $this;
+ return \date_modify($newDate, $modify);
+ }
+
+ /**
+ * Substract interval from the date by returning a new DateTime instance.
+ *
+ * The original DateTime instance is not modified.
+ *
+ * @param string $interval
+ * @return DateTime
+ */
+ public function sub($interval)
+ {
+ $newDate = clone $this;
+ return \date_sub($newDate, $interval);
+ }
+
+ /**
+ * Always throws ImmutableException
+ *
+ * @throws ImmutableException
+ * @param type $year
+ * @param type $month
+ * @param type $day
+ */
+ public function setDate($year, $month, $day)
+ {
+ throw new ImmutableException();
+ }
+
+ /**
+ * Always throws ImmutableException
+ *
+ * @throws ImmutableException
+ * @param type $year
+ * @param type $week
+ * @param type $day
+ */
+ public function setISODate($year, $week, $day=null)
+ {
+ throw new ImmutableException();
+ }
+
+ /**
+ * Always throws ImmutableException
+ *
+ * @throws ImmutableException
+ * @param type $hour
+ * @param type $minute
+ * @param type $second
+ */
+ public function setTime($hour, $minute, $second=null)
+ {
+ throw new ImmutableException();
+ }
+
+ /**
+ * Always throws ImmutableException
+ *
+ * @throws ImmutableException
+ * @param type $timestamp
+ */
+ public function setTimestamp($timestamp)
+ {
+ throw new ImmutableException();
+ }
+
+ /**
+ * Returns a new instance of this datetime with the changed timezone.
+ *
+ * @param DateTimeZone|string $timezone
+ * @return DateTime
+ */
+ public function setTimezone($timezone)
+ {
+ $newDate = clone $this;
+ return date_timezone_set($newDate, $timezone);
+ }
+}
@@ -0,0 +1,81 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Common\DateTime;
+
+/**
+ * DateTimeFactory that makes use of the immutable property of Doctrines DateTime instances.
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.org
+ * @since 3.0
@stof

stof Aug 28, 2011

Member

shouldn't it be 2.2 ?

@guilhermeblanco

guilhermeblanco Aug 28, 2011

Owner

@beberlei is drinking too much. =P

@stof

stof Aug 28, 2011

Member

note that it should be changed in all files added in the commit, not just this one.

+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class DateTimeFactory
+{
+ /**
+ * Map of all the dates
+ *
+ * @todo Evaluate for WeakRef?
+ * @var array
+ */
+ static private $dates = array();
+
+ /**
+ * @var DateTime
+ */
+ static private $now;
+
+ /**
+ * @return DateTime
+ */
+ static public function now()
+ {
+ if (self::$now == null) {
+ self::$now = new DateTime("now");
+ }
+ return self::$now;
+ }
+
+ /**
+ * @param DateTime $now
+ */
+ static public function setTestingNow(DateTime $now)
+ {
+ self::$now = $now;
+ }
+
+ /**
+ * Create from format using the default timezone.
+ *
+ * This method is not overwritten on DateTime, so that you can still create instances with a non-default
+ * timezone. However only the DateTimeFactory#createFromFormat() can re-use instances.
+ *
+ * @param string $format
+ * @param string $time
+ * @return DateTime
+ */
+ static public function createFromFormat($format, $time)
+ {
+ if (!isset(self::$dates[$format][$time])) {
+ self::$dates[$format][$time] = DateTime::createFromFormat($format, $time);
+ }
+ return self::$dates[$format][$time];
+ }
+}
@@ -0,0 +1,41 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Common\DateTime;
+
+/**
+ * Thrown if a method is called on the Doctrine DateTime that semantically would change the
+ * inner state of the instance, hence immutability has to be enforced.
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.org
+ * @since 3.0
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class ImmutableException extends \Exception
+{
+ public function __construct()
+ {
+ parent::__construct(
+ "Cannot modify Doctrine\Common\DateTime\DateTime instance, its immutable. ".
+ "You can use #modify(), #add() and #sub() and work with the newly created instances " .
+ "that are returned."
+ );
+ }
+}
@@ -0,0 +1,42 @@
+<?php
+
+namespace Doctrine\Tests\Common\DateTime;
+
+use Doctrine\Common\DateTime\DateTimeFactory;
+use Doctrine\Common\DateTime\DateTime;
+
+/**
+ * @group DCOM-67
+ */
+class DateTimeFactoryTest extends \PHPUnit_Framework_TestCase
+{
+ public function testNow()
+ {
+ $now1 = DateTimeFactory::now();
+ $now2 = DateTimeFactory::now();
+
+ $this->assertSame($now1, $now2);
+ }
+
+ public function testSetTestingNow()
+ {
+ $now = new DateTime("2010-10-10");
+ DateTimeFactory::setTestingNow($now);
+
+ $this->assertSame($now, DateTimeFactory::now());
+ }
+
+ public function testCreateFromFormat()
+ {
+ $date1 = DateTimeFactory::createFromFormat('!Y-m-d', '2010-10-10');
+ $date2 = DateTimeFactory::createFromFormat('!Y-m-d', '2010-10-10');
+
+ $this->assertSame($date1, $date2);
+
+ $this->assertEquals('2010-10-10', $date1->format('Y-m-d'));
+
+ $date3 = DateTimeFactory::createFromFormat('!Y-m-d', '2010-10-09');
+
+ $this->assertNotSame($date1, $date3);
+ }
+}
@@ -0,0 +1,83 @@
+<?php
+
+namespace Doctrine\Tests\Common\DateTime;
+
+use Doctrine\Common\DateTime\DateTime;
+
+/**
+ * @group DCOM-67
+ */
+class DateTimeTest extends \PHPUnit_Framework_TestCase
+{
+ public function testAddImmutable()
+ {
+ $dateInterval = new \DateInterval('P2D');
+ $a = new DateTime("2009-12-12");
+ $b = $a->add($dateInterval);
+ $this->assertNotSame($a, $b);
+
+ $this->assertEquals('2009-12-12', $a->format('Y-m-d'));
+ $this->assertEquals('2009-12-14', $b->format('Y-m-d'));
+ }
+
+ public function testSubImmutable()
+ {
+ $dateInterval = new \DateInterval('P2D');
+ $a = new DateTime("2009-12-12");
+ $b = $a->sub($dateInterval);
+ $this->assertNotSame($a, $b);
+
+ $this->assertEquals('2009-12-12', $a->format('Y-m-d'));
+ $this->assertEquals('2009-12-10', $b->format('Y-m-d'));
+ }
+
+ public function testModifyImmutable()
+ {
+ $a = new DateTime("2009-12-12");
+ $b = $a->modify('+2 days');
+ $this->assertNotSame($a, $b);
+
+ $this->assertEquals('2009-12-12', $a->format('Y-m-d'));
+ $this->assertEquals('2009-12-14', $b->format('Y-m-d'));
+ }
+
+ public function testSetTimeZoneImmutable()
+ {
+ $a = new DateTime("2009-12-12");
+ $b = $a->setTimezone(new \DateTimeZone("UTC"));
+
+ $this->assertNotSame($a, $b);
+ }
+
+ public function testSetTimestamp()
+ {
+ $a = new DateTime("2009-12-12");
+
+ $this->setExpectedException("Doctrine\Common\DateTime\ImmutableException");
+ $a->setTimestamp(time());
+ }
+
+ public function testSetTime()
+ {
+ $a = new DateTime("2009-12-12");
+
+ $this->setExpectedException("Doctrine\Common\DateTime\ImmutableException");
+ $a->setTime(10, 20);
+ }
+
+ public function testSetDate()
+ {
+ $a = new DateTime("2009-12-12");
+
+ $this->setExpectedException("Doctrine\Common\DateTime\ImmutableException");
+ $a->setDate(2010, 10, 10);
+ }
+
+ public function testSetISODate()
+ {
+ $a = new DateTime("2009-12-12");
+
+ $this->setExpectedException("Doctrine\Common\DateTime\ImmutableException");
+ $a->setISODate(2010, 40);
+ }
+}

0 comments on commit 7140ad3

Please sign in to comment.