diff --git a/core/model/fieldtypes/SSDatetime.php b/core/model/fieldtypes/SSDatetime.php index 64437ae92..662c2cb9f 100644 --- a/core/model/fieldtypes/SSDatetime.php +++ b/core/model/fieldtypes/SSDatetime.php @@ -6,6 +6,11 @@ * Alternatively you can set a timestamp that is evaluated through * PHP's built-in date() and strtotime() function according to your system locale. * + * For all computations involving the current date and time, + * please use {@link SSDatetime::now()} instead of PHP's built-in date() and time() + * methods. This ensures that all time-based computations are testable with mock dates + * through {@link SSDatetime::set_mock_now()}. + * * @todo Add localization support, see http://open.silverstripe.com/ticket/2931 * * @package sapphire @@ -44,7 +49,7 @@ class SSDatetime extends Date { function requireField() { $parts=Array('datatype'=>'datetime'); - $values=Array('type'=>'ssdatetime', 'parts'=>$parts); + $values=Array('type'=>'SSDatetime', 'parts'=>$parts); DB::requireField($this->tableName, $this->name, $values); } @@ -55,6 +60,50 @@ class SSDatetime extends Date { public function scaffoldFormField($title = null, $params = null) { return new PopupDateTimeField($this->name, $title); } + + /** + * + */ + protected static $mock_now = null; + + /** + * Returns either the current system date as determined + * by date(), or a mocked date through {@link set_mock_now()}. + * + * @return SSDatetime + */ + static function now() { + if(self::$mock_now) { + return self::$mock_now; + } else { + return DBField::create('SSDatetime', date('Y-m-d H:i:s')); + } + } + + /** + * Mock the system date temporarily, which is useful for time-based unit testing. + * Use {@link clear_mock_now()} to revert to the current system date. + * Caution: This sets a fixed date that doesn't increment with time. + * + * @param SSDatetime|string $datetime Either in object format, or as a SSDatetime compatible string. + */ + static function set_mock_now($datetime) { + if($datetime instanceof SSDatetime) { + self::$mock_now = $datetime; + } elseif(is_string($datetime)) { + self::$mock_now = DBField::create('SSDatetime', $datetime); + } else { + throw new Exception('SSDatetime::set_mock_now(): Wrong format: ' . $datetime); + } + } + + /** + * Clear any mocked date, which causes + * {@link Now()} to return the current system date. + */ + static function clear_mock_now() { + self::$mock_now = null; + } } ?> \ No newline at end of file diff --git a/tests/model/SSDateTimeTest.php b/tests/model/SSDateTimeTest.php new file mode 100644 index 000000000..fe59bb681 --- /dev/null +++ b/tests/model/SSDateTimeTest.php @@ -0,0 +1,36 @@ +assertEquals($systemDatetime->Date(), $nowDatetime->Date()); + } + + function testNowWithMockDate() { + // Test setting + $mockDate = '2001-12-31 22:10:59'; + SSDatetime::set_mock_now($mockDate); + $systemDatetime = DBField::create('SSDatetime', date('Y-m-d H:i:s')); + $nowDatetime = SSDatetime::now(); + $this->assertNotEquals($systemDatetime->Date(), $nowDatetime->Date()); + $this->assertEquals($nowDatetime->getValue(), $mockDate); + + // Test clearing + SSDatetime::clear_mock_now(); + $systemDatetime = DBField::create('SSDatetime', date('Y-m-d H:i:s')); + $nowDatetime = SSDatetime::now(); + $this->assertEquals($systemDatetime->Date(), $nowDatetime->Date()); + } +} \ No newline at end of file