Date object without timestamps (almost)
Posted: Sun Feb 18, 2007 7:54 am
I wasn't really sure where else to put this. It's basically something I started working on a couple of months back but haven't really got heaps of time to focus on. UNIX timestamps have the unfortunate problem of not being able to deal with dates in far out ranges (some say you can use negative timestamps, but still, you can't go very far).
I know other (PEAR) classes exist but I wanted to (roughly) recreate the Java Calendar API in PHP. The API is here:
http://java.sun.com/j2se/1.5.0/docs/api ... endar.html
http://java.sun.com/j2se/1.5.0/docs/api ... endar.html
I'm positive that without a great deal more work I can get it working but my concern is that I'm going to have to fall back to iteratively seeking through units of time when setting dates. This is mostly because I can't come up with a better logic for keeping tags of what day of the week it is, what week of the year etc etc whilst taking into account leap years etc... (if anyone can produce that logic, you're a legend!).
The esscence of the API is that you get() and set() units in the date, but you can also add() (I've called mine adjust()) to individual units of the date with negative or postive adjustments.
I was going to look at a purely literal translation of the calendar class from java but it' got too many java dependencies in it.
The reason I'm posting here is to:
a) See if anyone has better logic -- I'm sure someone will -- for the calculations
b) See if anyone wants to help finish it. It HAS to remain LGPL.
I currently fall back to using a timestamp to set-up the values in the class at instantiation, but from thereon in I don't touch the date/time functions. I'd change that too if I could.
Here's the class, sorry for lack of comments:
And here's a Unit Test:
I know other (PEAR) classes exist but I wanted to (roughly) recreate the Java Calendar API in PHP. The API is here:
http://java.sun.com/j2se/1.5.0/docs/api ... endar.html
http://java.sun.com/j2se/1.5.0/docs/api ... endar.html
I'm positive that without a great deal more work I can get it working but my concern is that I'm going to have to fall back to iteratively seeking through units of time when setting dates. This is mostly because I can't come up with a better logic for keeping tags of what day of the week it is, what week of the year etc etc whilst taking into account leap years etc... (if anyone can produce that logic, you're a legend!).
The esscence of the API is that you get() and set() units in the date, but you can also add() (I've called mine adjust()) to individual units of the date with negative or postive adjustments.
I was going to look at a purely literal translation of the calendar class from java but it' got too many java dependencies in it.
The reason I'm posting here is to:
a) See if anyone has better logic -- I'm sure someone will -- for the calculations
b) See if anyone wants to help finish it. It HAS to remain LGPL.
I currently fall back to using a timestamp to set-up the values in the class at instantiation, but from thereon in I don't touch the date/time functions. I'd change that too if I could.
Here's the class, sorry for lack of comments:
Code: Select all
<?php
/*
ISSUES TO ADDRESS:
* Seeking is slow (is it???)
* Determine day of week (seeking would be the solution)
* Determine day of year (seeking would be the solution)
* DST?
* What should happen if month is changed to feb but day was 31? (the day should change to 3)
*/
//I'll add some useful debug stuff I'm sure
class SDateException extends Exception {}
/**
* A logic-based calendar/date class for PHP. TimeStamp independant (well, almost)
* @license GNU Lesser General Public License
*/
class SDate
{
const NOW = 0;
const YEAR = 1;
const MONTH = 2;
const WEEK = 3;
const DAY_OF_YEAR = 4;
const DAY_OF_MONTH = 5;
const DAY_OF_WEEK = 6;
const HOUR = 7;
const MINUTE = 8;
const SECOND = 9;
const SUNDAY = 0;
const MONDAY = 1;
const TUESDAY = 2;
const WEDNESDAY = 3;
const THURSDAY = 4;
const FRIDAY = 5;
const SATURDAY = 6;
const JANUARY = 0;
const FEBRUARY = 1;
const MARCH = 2;
const APRIL = 3;
const MAY = 4;
const JUNE = 5;
const JULY = 6;
const AUGUST = 7;
const SEPTEMBER = 8;
const OCTOBER = 9;
const NOVEMBER = 10;
const DECEMBER = 11;
protected $model = array(); //Set in Ctor
public function __construct($stamp=null)
{
if ($stamp === null) $stamp = time();
$this->set(self::YEAR, date("Y", $stamp));
$this->set(self::MONTH, date("n", $stamp));
$this->set(self::WEEK, date("W", $stamp));
$this->set(self::DAY_OF_YEAR, date("z", $stamp));
$this->set(self::DAY_OF_MONTH, date("j", $stamp));
$this->set(self::DAY_OF_WEEK, date("w", $stamp));
$this->set(self::HOUR, date("G", $stamp));
$this->set(self::MINUTE, ltrim(date("i", $stamp), "0"));
$this->set(self::SECOND, ltrim(date("s", $stamp), "0"));
}
protected function getAffectedUnit($unit)
{
switch ($unit)
{
case self::SECOND:
return self::MINUTE;
case self::MINUTE:
return self::HOUR;
case self::HOUR:
return self::DAY_OF_MONTH;
case self::DAY_OF_MONTH;
return self::MONTH;
case self::MONTH;
return self::YEAR;
default:
return false;
}
}
public function isDefinedUnit($unit)
{
return ($unit >= self::YEAR && $unit <= self::SECOND);
}
public function validateRange($unit, $value)
{
if (!$this->isDefinedUnit($unit))
{
throw new SDateException(
"Unable to validate the range given as the unit does not correspond with one of the SDate class constants");
}
$range = $this->getRange($unit);
if ((false!== $range[0] && $value < $range[0]) || (false !== $range[1] && $value > $range[1]))
{
throw new SDateException("Value " . $value . " is out of range [" . implode(",", $range) . "]");
}
}
public function getRange($unit)
{
if (!$this->isDefinedUnit($unit))
{
throw new SDateException(
"Unable to get the allowed range as the unit does not correspond with one of the SDate class constants");
}
switch ($unit)
{
case self::YEAR:
return array(-2000, 4000);
case self::MONTH:
return array(1, 12);
case self::WEEK:
return array(0, 51);
case self::DAY_OF_YEAR:
return array(0, $this->getDaysInYear());
case self::DAY_OF_MONTH:
return array(1, $this->getDaysInMonth());
case self::DAY_OF_WEEK:
return array(0, 6);
case self::HOUR:
return array(0, 23);
case self::MINUTE:
return array(0, 59);
case self::SECOND:
return array(0, 59);
}
}
public function getDaysInMonth()
{
$month = $this->get(self::MONTH);
if (in_array($month, array(1, 3, 5, 7, 8, 10, 12)))
{
return 31;
}
elseif (in_array($month, array(4, 6, 9, 11)))
{
return 30;
}
elseif ($month == 2)
{
if (!$this->isLeapYear()) return 28;
else return 29;
}
else return 31;
}
public function getDaysInYear()
{
$leap_year = $this->isLeapYear();
return $leap_year ? 365 : 364;
}
public function adjust($unit, $size)
{
$size = (int)$size;
$range = $this->getRange($unit);
$margin = $range[1] - $range[0] + 1;
$current_value = $this->get($unit);
if ($size < 0)
{
do {
$new_value = $current_value + $size;
$size = $new_value - $range[0];
$current_value = $range[1] + 1;
if (($size < 0)
&& (false !== $affected_unit = $this->getAffectedUnit($unit)))
{
$this->adjust($affected_unit, -1);
}
} while ($new_value < $range[0]);
}
else
{
do {
$new_value = $current_value + $size;
$size = $new_value - $range[1];
$current_value = $range[0] - 1;
if (($size > 0)
&& (false !== $affected_unit = $this->getAffectedUnit($unit)))
{
$this->adjust($affected_unit, 1);
}
} while ($new_value > $range[1]);
}
$this->set($unit, $new_value);
}
protected function getHcf($value, $factor)
{
return ($value - ($value % $factor));
}
public function set($unit, $value)
{
$unit = (int)$unit;
$value = (int)$value;
if (!$this->isDefinedUnit($unit))
{
throw new SDateException("You must use one of the supplied class constants to set the unit of time.");
}
$this->validateRange($unit, $value);
$this->model[$unit] = $value;
//Re-check some fields if needed
if ($unit == self::MONTH && $this->has(self::DAY_OF_MONTH))
{
$this->validateRange(self::DAY_OF_MONTH, $this->get(self::DAY_OF_MONTH));
}
if ($unit == self::YEAR && $this->has(self::DAY_OF_MONTH))
{
$this->validateRange(self::DAY_OF_MONTH, $this->get(self::DAY_OF_MONTH));
}
if ($unit == self::YEAR && $this->has(self::DAY_OF_YEAR))
{
$this->validateRange(self::DAY_OF_YEAR, $this->get(self::DAY_OF_YEAR));
}
}
public function get($unit)
{
$unit = (int)$unit;
if (!$this->isDefinedUnit($unit))
{
throw new SDateException("You must use one of the supplied class constants to get the time.");
}
return $this->model[$unit];
}
protected function has($unit)
{
$unit = (int)$unit;
if (!$this->isDefinedUnit($unit))
{
throw new SDateException("Unit not defined as class constant");
}
return array_key_exists($unit, $this->model);
}
public function isLeapYear($year=null)
{
if ($year === null) $year = $this->get(self::YEAR);
if ($year < 1000) return false; //No leap years
if ($year%4 == 0)
{
if ($year < 1582) return true;
elseif ($year%100 != 0 || $year%400 == 0) return true;
}
return false;
}
}Code: Select all
<?php
error_reporting(E_ALL);
set_time_limit(5); //In case I scew up and create an infinite loop :-\
require_once "/Users/d11wtq/simpletest/unit_tester.php";
require_once "/Users/d11wtq/simpletest/reporter.php";
require_once "./SDate.php";
class TestOfSDate extends UnitTestCase
{
protected function assertChangesToValuesAreVisible($unit, $min, $max) {
$date = new SDate();
$date->set(SDate::DAY_OF_MONTH, 1);
for ($i = 0; $i < 10; $i++)
{
$value = rand($min, $max);
$date->set($unit, $value);
$this->assertEqual($value, $date->get($unit));
}
}
public function testChangesToYearAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::YEAR, 1700, 3000);
}
public function testChangesToMonthAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::MONTH, 1, 12);
}
public function testChangesToWeekAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::WEEK, 0, 51);
}
public function testChangesToDayOfYearAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::DAY_OF_YEAR, 1, 365);
}
public function testChangesToDayOfMonthAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::DAY_OF_MONTH, 1, 31);
}
public function testChangesToDayOfWeekAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::DAY_OF_WEEK, 0, 6);
}
public function testChangesToHourAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::HOUR, 0, 23);
}
public function testChangesToMinuteAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::MINUTE, 0, 59);
}
public function testChangesToSecondAreVisible() {
$this->assertChangesToValuesAreVisible(SDate::SECOND, 0, 59);
}
public function testAdjustmentsToValuesAreVisible() {
$date = new SDate();
$date->set(SDate::YEAR, 2000);
$date->adjust(SDate::YEAR, +3);
$this->assertEqual(2003, $date->get(SDate::YEAR));
$date->adjust(SDate::YEAR, -10);
$this->assertEqual(1993, $date->get(SDate::YEAR));
$date->set(SDate::MONTH, 1);
$date->adjust(SDate::MONTH, +4);
$this->assertEqual(5, $date->get(SDate::MONTH));
$date->set(SDate::HOUR, 4);
$date->adjust(SDate::HOUR, -4);
$this->assertEqual(0, $date->get(SDate::HOUR));
//assume everything works if this does since it's the same mechanism
}
public function assertOutOfRangeValuesTriggerException($unit, $min, $max, $str_unit) {
$date = new SDate();
$date->set(SDate::YEAR, 2001);
try {
$v = $min-1;
$date->set($unit, $v);
$this->fail("Expected value " . $v . " to be out of range for " . $str_unit);
} catch (SDateException $e) {
$this->pass();
}
try {
$v = $min-20;
$date->set($unit, $v);
$this->fail("Expected value " . $v . " to be out of range for " . $str_unit);
} catch (SDateException $e) {
$this->pass();
}
try {
$v = $max+1;
$date->set($unit, $v);
$this->fail("Expected value " . $v . " to be out of range for " . $str_unit);
} catch (SDateException $e) {
$this->pass();
}
try {
$v = $max+20;
$date->set($unit, $max+21);
$this->fail("Expected value " . $v . " to be out of range for " . $str_unit);
} catch (SDateException $e) {
$this->pass();
}
}
public function testSettingMonthToOutOfRangeValueThrowsException()
{
$this->assertOutOfRangeValuesTriggerException(SDate::MONTH, 1, 12, "MONTH");
}
public function testSettingWeekToOutOfRangeValueThrowsException() {
$this->assertOutOfRangeValuesTriggerException(SDate::WEEK, 0, 51, "WEEK");
}
public function testSettingDayOfYearToOutOfRangeValueThrowsException() {
$this->assertOutOfRangeValuesTriggerException(SDate::DAY_OF_YEAR, 0, 365, "DAY_OF_YEAR");
}
public function testSettingDayOfMonthToOutOfRangeValueThrowsException() {
$this->assertOutOfRangeValuesTriggerException(SDate::DAY_OF_MONTH, 1, 31, "DAY_OF_MONTH");
}
public function testSettingDayOfWeekToOutOfRangeValueThrowsException() {
$this->assertOutOfRangeValuesTriggerException(SDate::DAY_OF_WEEK, 0, 6, "DAY_OF_WEEK");
}
public function testSettingHourToOutOfRangeValueThrowsException() {
$this->assertOutOfRangeValuesTriggerException(SDate::HOUR, 0, 23, "HOUR");
}
public function testSettingMinuteToOutOfRangeValueThrowsException() {
$this->assertOutOfRangeValuesTriggerException(SDate::MINUTE, 0, 59, "MINUTE");
}
public function testSettingSecondToOutOfRangeValueThrowsException() {
$this->assertOutOfRangeValuesTriggerException(SDate::SECOND, 0, 59, "SECOND");
}
public function testRangeForSelectedMonthsIsLessThan1To31() {
$date = new SDate();
$date->set(SDate::YEAR, 2001);
$date->set(SDate::DAY_OF_MONTH, 1);
$date->set(SDate::MONTH, 2); //Feb
try {
$date->set(SDate::DAY_OF_MONTH, 30);
$this->fail("Days in Feb is 28 or 29 so 30 is out of range");
} catch (SDateException $e) {
$this->pass();
}
$months_with_30_days = array(4, 6, 9, 11);
foreach ($months_with_30_days as $month)
{
$date->set(SDate::MONTH, $month);
try {
$this->assertNull($date->set(SDate::DAY_OF_MONTH, 30));
$date->set(SDate::DAY_OF_MONTH, 31);
$this->fail("Days in this month should never be more than 30");
} catch (SDateException $e) {
$this->pass();
}
}
}
public function testLeapYearIsDetectedAtCorrectYearValues() {
$date = new SDate();
$this->assertFalse($date->isLeapYear(1999));
$this->assertFalse($date->isLeapYear(2003));
$this->assertFalse($date->isLeapYear(1991));
$this->assertTrue($date->isLeapYear(2004));
$this->assertTrue($date->isLeapYear(2000));
$this->assertTrue($date->isLeapYear(1996));
//Prior to 1000AD leap years were not on the calendar
$this->assertFalse($date->isLeapYear(994));
$this->assertFalse($date->isLeapYear(990));
$this->assertFalse($date->isLeapYear(400));
//Before 1582, it was as basic as once every 4 years
$this->assertTrue($date->isLeapYear(1004));
$this->assertTrue($date->isLeapYear(1400));
$this->assertTrue($date->isLeapYear(1500));
}
public function testRangeDropsToMaximumOf28DaysInFebruaryIfNotALeapYear() {
$date = new SDate();
$date->set(SDate::DAY_OF_MONTH, 1);
$non_leap_years = array(2001, 1999, 1993, 1701);
foreach ($non_leap_years as $year)
{
$date->set(SDate::YEAR, $year);
$date->set(SDate::MONTH, 2); //Feb
try {
$date->set(SDate::DAY_OF_MONTH, 29);
$this->fail("Days in Feb on non-leap year is 28 so 29 is out of range");
} catch (SDateException $e) {
$this->pass();
}
}
$leap_years = array(2000, 4000, 1200, 2004);
foreach ($leap_years as $year)
{
$date->set(SDate::YEAR, $year);
$date->set(SDate::MONTH, 2); //Feb
try {
$this->assertNull($date->set(SDate::DAY_OF_MONTH, 29));
$date->set(SDate::DAY_OF_MONTH, 30);
$this->fail("Days in Feb on leap year is 29 so 30 is out of range");
} catch (SDateException $e) {
$this->pass();
}
}
}
public function testRangeIs366DaysPerYearOnLeapYears() {
$date = new SDate();
$date->set(SDate::DAY_OF_MONTH, 1);
$non_leap_years = array(2001, 1999, 1993, 1701);
foreach ($non_leap_years as $year)
{
$date->set(SDate::YEAR, $year);
try {
$date->set(SDate::DAY_OF_YEAR, 365);
$this->fail("$year is not a leap year so cannot have 366 days");
} catch (SDateException $e) {
$this->pass();
}
}
$leap_years = array(2000, 4000, 1200, 2004);
foreach ($leap_years as $year)
{
$date->set(SDate::YEAR, $year);
$this->assertNull($date->set(SDate::DAY_OF_YEAR, 365));
}
}
public function testDayOfMonthIsReCheckedWhenMonthIsChanged() {
$date = new SDate();
$date->set(SDate::DAY_OF_MONTH, 1);
$date->set(SDate::MONTH, 12);
$date->set(SDate::DAY_OF_MONTH, 31);
try {
$date->set(SDate::MONTH, 6);
$this->fail("June cannot have 31 days so this should have thrown exception");
} catch (SDateException $e) {
$this->pass();
}
$date->set(SDate::MONTH, 1);
$date->set(SDate::DAY_OF_MONTH, 30);
try {
$date->set(SDate::MONTH, 2);
$this->fail("February cannot have 30 days so this should have thrown exception");
} catch (SDateException $e) {
$this->pass();
}
}
public function testDayOfMonthIsReCheckedWhenYearIsChanged() {
$date = new SDate();
$date->set(SDate::DAY_OF_MONTH, 1);
$date->set(SDate::YEAR, 2000);
$date->set(SDate::MONTH, 2);
$date->set(SDate::DAY_OF_MONTH, 29);
try {
$date->set(SDate::YEAR, 1999);
$this->fail("February cannot have 29 days in 1999 so this should have thrown exception");
} catch (SDateException $e) {
$this->pass();
}
}
public function testDayOfYearIsReCheckedWhenYearIsChanged() {
$date = new SDate();
$date->set(SDate::YEAR, 2000);
$date->set(SDate::DAY_OF_YEAR, 365); //Indexing starts at zero
try {
$date->set(SDate::YEAR, 1999);
$this->fail("There were not 366 days in 1999 so this should have thrown exception");
} catch (SDateException $e) {
$this->pass();
}
}
public function testReducingSecondBeyondZeroWrapsAround()
{
$date = new SDate();
$date->set(SDate::SECOND, 10);
$date->adjust(SDate::SECOND, -15);
$this->assertEqual(55, $date->get(SDate::SECOND));
$date->set(SDate::SECOND, 1);
$date->adjust(SDate::SECOND, -2);
$this->assertEqual(59, $date->get(SDate::SECOND));
$date->set(SDate::SECOND, 1);
$date->adjust(SDate::SECOND, -125);
$this->assertEqual(56, $date->get(SDate::SECOND));
}
public function testRaisingSecondAbove59WrapsAround()
{
$date = new SDate();
$date->set(SDate::SECOND, 10);
$date->adjust(SDate::SECOND, 55);
$this->assertEqual(5, $date->get(SDate::SECOND));
$date->set(SDate::SECOND, 59);
$date->adjust(SDate::SECOND, +1);
$this->assertEqual(0, $date->get(SDate::SECOND));
$date->set(SDate::SECOND, 51);
$date->adjust(SDate::SECOND, 180);
$this->assertEqual(51, $date->get(SDate::SECOND));
}
public function testReducingSecondsReducesMinutesIfNeeded()
{
$date = new SDate();
$date->set(SDate::MINUTE, 59);
$date->set(SDate::SECOND, 10);
$date->adjust(SDate::SECOND, -15);
$this->assertEqual(58, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 59);
$date->set(SDate::SECOND, 1);
$date->adjust(SDate::SECOND, -2);
$this->assertEqual(58, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 59);
$date->set(SDate::SECOND, 1);
$minute_start = $date->get(SDate::MINUTE);
$date->adjust(SDate::SECOND, -125);
$this->assertEqual(56, $date->get(SDate::MINUTE));
}
public function testIncreasingSecondsIncreasesMinutesIfNeeded()
{
$date = new SDate();
$date->set(SDate::MINUTE, 3);
$date->set(SDate::SECOND, 10);
$date->adjust(SDate::SECOND, 55);
$this->assertEqual(4, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 15);
$date->set(SDate::SECOND, 58);
$date->adjust(SDate::SECOND, 63);
$this->assertEqual(17, $date->get(SDate::MINUTE));
}
public function testReducingMinutesBeyondZeroWrapsAround()
{
$date = new SDate();
$date->set(SDate::MINUTE, 10);
$date->adjust(SDate::MINUTE, -15);
$this->assertEqual(55, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 1);
$date->adjust(SDate::MINUTE, -121);
$this->assertEqual(0, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 1);
$date->adjust(SDate::MINUTE, -125);
$this->assertEqual(56, $date->get(SDate::MINUTE));
}
public function testRaisingMinutesAbove59WrapsAround()
{
$date = new SDate();
$date->set(SDate::MINUTE, 10);
$date->adjust(SDate::MINUTE, 55);
$this->assertEqual(5, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 59);
$date->adjust(SDate::MINUTE, +1);
$this->assertEqual(0, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 51);
$date->adjust(SDate::MINUTE, 180);
$this->assertEqual(51, $date->get(SDate::MINUTE));
}
public function testReducingMinutesReducesHoursIfNeeded()
{
$date = new SDate();
$date->set(SDate::HOUR, 23);
$date->set(SDate::MINUTE, 10);
$date->adjust(SDate::MINUTE, -15);
$this->assertEqual(22, $date->get(SDate::HOUR));
$date->set(SDate::HOUR, 23);
$date->set(SDate::MINUTE, 1);
$date->adjust(SDate::MINUTE, -2);
$this->assertEqual(22, $date->get(SDate::HOUR));
$date->set(SDate::HOUR, 23);
$date->set(SDate::MINUTE, 1);
$minute_start = $date->get(SDate::HOUR);
$date->adjust(SDate::MINUTE, -125);
$this->assertEqual(20, $date->get(SDate::HOUR));
}
public function testIncreasingMinutesIncreasesHoursIfNeeded()
{
$date = new SDate();
$date->set(SDate::HOUR, 3);
$date->set(SDate::MINUTE, 10);
$date->adjust(SDate::MINUTE, 55);
$this->assertEqual(4, $date->get(SDate::HOUR));
$date->set(SDate::HOUR, 15);
$date->set(SDate::MINUTE, 58);
$date->adjust(SDate::MINUTE, 63);
$this->assertEqual(17, $date->get(SDate::HOUR));
}
public function testReducingHoursBeyondZeroWrapsAround()
{
$date = new SDate();
$date->set(SDate::HOUR, 10);
$date->adjust(SDate::HOUR, -15);
$this->assertEqual(19, $date->get(SDate::HOUR));
$date->set(SDate::HOUR, 1);
$date->adjust(SDate::HOUR, -2);
$this->assertEqual(23, $date->get(SDate::HOUR));
$date->set(SDate::HOUR, 1);
$date->adjust(SDate::HOUR, -49);
$this->assertEqual(0, $date->get(SDate::HOUR));
}
public function testRaisingHoursAbove23WrapsAround()
{
$date = new SDate();
$date->set(SDate::MINUTE, 10);
$date->adjust(SDate::MINUTE, 55);
$this->assertEqual(5, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 59);
$date->adjust(SDate::MINUTE, +1);
$this->assertEqual(0, $date->get(SDate::MINUTE));
$date->set(SDate::MINUTE, 51);
$date->adjust(SDate::MINUTE, 180);
$this->assertEqual(51, $date->get(SDate::MINUTE));
}
public function testReducingHoursReducesDaysIfNeeded() {
$date = new SDate();
$date->set(SDate::MONTH, 7);
$date->set(SDate::DAY_OF_MONTH, 31);
$date->set(SDate::HOUR, 1);
$date->adjust(SDate::HOUR, -2);
$this->assertEqual(30, $date->get(SDate::DAY_OF_MONTH));
$date->set(SDate::DAY_OF_MONTH, 23);
$date->set(SDate::HOUR, 11);
$date->adjust(SDate::HOUR, -10);
$this->assertEqual(23, $date->get(SDate::DAY_OF_MONTH));
$date->set(SDate::DAY_OF_MONTH, 23);
$date->set(SDate::HOUR, 11);
$date->adjust(SDate::HOUR, -48);
$this->assertEqual(21, $date->get(SDate::DAY_OF_MONTH));
}
public function testIncreasingHoursIncreasesDaysIfNeeded() {
$date = new SDate();
$date->set(SDate::MONTH, 7);
$date->set(SDate::DAY_OF_MONTH, 15);
$date->set(SDate::HOUR, 22);
$date->adjust(SDate::HOUR, +3);
$this->assertEqual(16, $date->get(SDate::DAY_OF_MONTH));
$date->set(SDate::DAY_OF_MONTH, 23);
$date->set(SDate::HOUR, 11);
$date->adjust(SDate::HOUR, 13);
$this->assertEqual(24, $date->get(SDate::DAY_OF_MONTH));
$date->set(SDate::DAY_OF_MONTH, 23);
$date->set(SDate::HOUR, 11);
$date->adjust(SDate::HOUR, 48);
$this->assertEqual(25, $date->get(SDate::DAY_OF_MONTH));
}
public function testIncreasingDaysIncreasesMonthIfNeeded() {
$date = new SDate();
$date->set(SDate::DAY_OF_MONTH, 1);
$date->set(SDate::YEAR, 2001); //Not a leap year
$date->set(SDate::MONTH, 7);
$date->set(SDate::DAY_OF_MONTH, 30);
$date->adjust(SDate::DAY_OF_MONTH, +2);
$this->assertEqual(1, $date->get(SDate::DAY_OF_MONTH));
$this->assertEqual(8, $date->get(SDate::MONTH));
$date = new SDate();
$date->set(SDate::DAY_OF_MONTH, 1);
$date->set(SDate::YEAR, 2001); //Not a leap year
$date->set(SDate::MONTH, 2);
$date->set(SDate::DAY_OF_MONTH, 27);
$date->adjust(SDate::DAY_OF_MONTH, 4);
$this->assertEqual(3, $date->get(SDate::DAY_OF_MONTH));
$this->assertEqual(3, $date->get(SDate::MONTH));
$date = new SDate();
$date->set(SDate::DAY_OF_MONTH, 1);
$date->set(SDate::YEAR, 2000); //Leap year
$date->set(SDate::MONTH, 2);
$date->set(SDate::DAY_OF_MONTH, 27);
$date->adjust(SDate::DAY_OF_MONTH, 4);
$this->assertEqual(2, $date->get(SDate::DAY_OF_MONTH));
$this->assertEqual(3, $date->get(SDate::MONTH));
}
public function testReducingDaysReducesMonthIfNeeded() {
}
}
$test = new TestOfSDate();
$test->run(new HtmlReporter());