includes/framework/QDateTime.class.php
<?php
// These Aid with the PHP 5.2 DateTime error handling
class QDateTimeNullException extends QCallerException {}
function QDateTimeErrorHandler() {}
/**
* QDateTime
* This DateTime class provides a nice wrapper around the PHP DateTime class,
* which is included with all versions of PHP >= 5.2.0. It includes many enhancements,
* including the ability to specify a null date or time portion to represent a date only or
* time only object.
* Inherits from the php DateTime object, and the built-in methods are available for you to call
* as well. In particular, note that the built-in format, and the qFormat routines here take different
* specifiers. Feel free to use either.
*
* @property null|integer $Month
* @property null|integer $Day
* @property null|integer $Year
* @property null|integer $Hour
* @property null|integer $Minute
* @property null|integer $Second
* @property integer $Timestamp
* @property-read string $Age A string representation of the age relative to now.
* @property-read QDateTime $LastDayOfTheMonth A new QDateTime representing the last day of this date's month.
* @property-read QDateTime $FirstDayOfTheMonth A new QDateTime representing the first day of this date's month.
*/
class QDateTime extends DateTime implements JsonSerializable, Serializable {
/** Used to specify the time right now (used when creating new instances of this class) */
const Now = 'now';
/** Date and time in ISO format */
const FormatIso = 'YYYY-MM-DD hhhh:mm:ss';
/** Date and time in ISO compressed format */
const FormatIsoCompressed = 'YYYYMMDDhhhhmmss';
/** Format used for displaying short date */
const FormatDisplayDate = 'MMM DD YYYY';
/** Format used for displaying the full date */
const FormatDisplayDateFull = 'DDD, MMMM D, YYYY';
/** Format used for displaying the short date and time */
const FormatDisplayDateTime = 'MMM DD YYYY hh:mm zz';
/** Format used for displaying the full date and time */
const FormatDisplayDateTimeFull = 'DDDD, MMMM D, YYYY, h:mm:ss zz';
/** Format to display only the time */
const FormatDisplayTime = 'hh:mm:ss zz';
/** Date and time format according to RFC 822 */
const FormatRfc822 = 'DDD, DD MMM YYYY hhhh:mm:ss ttt';
/** Date and time format according to RFC 5322 */
const FormatRfc5322 = 'DDD, DD MMM YYYY hhhh:mm:ss ttttt';
/** Format used to represent date for SOAP */
const FormatSoap = 'YYYY-MM-DDThhhh:mm:ss';
/* Type in which the date and time has to be interpreted */
/** Unknown type interpretation */
const UnknownType = 0;
/** Interpret as only date (used in QDateTimePicket and MySqli database type) */
const DateOnlyType = 1;
/** Interpret as only time (used in QDateTimePicket and MySqli database type) */
const TimeOnlyType = 2;
/** Interpret as both date and type (used in QDateTimePicket and MySqli database type) */
const DateAndTimeType = 3;
/** @var bool true if date is null */
protected $blnDateNull = true;
/** @var bool true if time is null, rather than just zero (beginning of day) */
protected $blnTimeNull = true;
/**
* The "Default" Display Format
* @var string $DefaultFormat
*/
public static $DefaultFormat = QDateTime::FormatDisplayDateTime;
/**
* The "Default" Display Format for Times
* @var string $DefaultTimeFormat
*/
public static $DefaultTimeFormat = QDateTime::FormatDisplayTime;
/**
* The "Default" Display Format for Dates with null times
* @var string $DefaultDateOnlyFormat
*/
public static $DefaultDateOnlyFormat = QDateTime::FormatDisplayDate;
/**
* Returns a new QDateTime object that's set to "Now"
* Set blnTimeValue to true (default) for a DateTime, and set blnTimeValue to false for just a Date
*
* @param boolean $blnTimeValue whether or not to include the time value
* @return QDateTime the current date and/or time
*/
public static function Now($blnTimeValue = true) {
$dttToReturn = new QDateTime(QDateTime::Now);
if (!$blnTimeValue) {
$dttToReturn->blnTimeNull = true;
$dttToReturn->ReinforceNullProperties();
}
return $dttToReturn;
}
/**
* Return Now as a string. Uses the default datetime format if none speicifed.
* @param string|null $strFormat
* @return string
*/
public static function NowToString($strFormat = null) {
$dttNow = new QDateTime(QDateTime::Now);
return $dttNow->qFormat($strFormat);
}
/**
* @return bool
*/
public function IsDateNull() {
return $this->blnDateNull;
}
/**
* @return bool
*/
public function IsNull() {
return ($this->blnDateNull && $this->blnTimeNull);
}
/**
* @return bool
*/
public function IsTimeNull() {
return $this->blnTimeNull;
}
/**
* @param $strFormat
* @return string
*/
public function PhpDate($strFormat) {
// This just makes a call to format
return parent::format($strFormat);
}
/**
* @param QDateTime[] $dttArray
* @return array
*/
public function GetSoapDateTimeArray($dttArray) {
if (!$dttArray)
return null;
$strArrayToReturn = array();
foreach ($dttArray as $dttItem)
array_push($strArrayToReturn, $dttItem->qFormat(QDateTime::FormatSoap));
return $strArrayToReturn;
}
/**
* Create from a unix timestamp. Improves over php by taking into consideration the
* timezone, so that the internal format is automatically converted to the internal timezone,
* or the default timezone.
*
* @param integer $intTimestamp
* @param DateTimeZone $objTimeZone
* @return QDateTime
*/
public static function FromTimestamp($intTimestamp, DateTimeZone $objTimeZone = null) {
return new QDateTime(date('Y-m-d H:i:s', $intTimestamp), $objTimeZone);
}
/**
* Construct a QDateTime. Does a few things differently than the php version:
* - Always stores timestamps in local or given timezone, so time extraction is easy
* - Has settings to determine if you want a date only or time only type
* - Will NOT throw exceptions. Errors simply result in a null datetime.
*
* @param null|integer|string|QDateTime|DateTime $mixValue
* @param DateTimeZone $objTimeZone
* @param int $intType
*
* @throws QCallerException
*/
public function __construct($mixValue = null, DateTimeZone $objTimeZone = null, $intType = QDateTime::UnknownType) {
if ($mixValue instanceof QDateTime) {
// Cloning from another QDateTime object
if ($objTimeZone)
throw new QCallerException('QDateTime cloning cannot take in a DateTimeZone parameter');
parent::__construct($mixValue->format('Y-m-d H:i:s'), $mixValue->GetTimeZone());
$this->blnDateNull = $mixValue->IsDateNull();
$this->blnTimeNull = $mixValue->IsTimeNull();
$this->ReinforceNullProperties();
} else if ($mixValue instanceof DateTime) {
// Subclassing from a PHP DateTime object
if ($objTimeZone)
throw new QCallerException('QDateTime subclassing of a DateTime object cannot take in a DateTimeZone parameter');
parent::__construct($mixValue->format('Y-m-d H:i:s'), $mixValue->getTimezone());
// By definition, a DateTime object doesn't have anything nulled
$this->blnDateNull = false;
$this->blnTimeNull = false;
} else if (!$mixValue) {
// Set to "null date"
// And Do Nothing Else -- Default Values are already set to Nulled out
parent::__construct('2000-01-01 00:00:00', $objTimeZone);
} else if (strtolower($mixValue) == QDateTime::Now) {
// very common, so quickly deal with now string
parent::__construct('now', $objTimeZone);
$this->blnDateNull = false;
$this->blnTimeNull = false;
} else if (substr($mixValue, 0, 1) == '@') {
// unix timestamp. PHP superclass will always store ts in UTC. Our class will store in given timezone, or local tz
parent::__construct(date('Y-m-d H:i:s', substr($mixValue, 1)), $objTimeZone);
$this->blnDateNull = false;
$this->blnTimeNull = false;
}
else {
// string relative date or time
if ($intTime = strtotime($mixValue)) {
// The documentation states that:
// The valid range of a timestamp is typically from
// Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.
// (These are the dates that correspond to the minimum and maximum values
// for a 32-bit signed integer).
//
// But experimentally, 0000-01-01 00:00:00 is the least date displayed correctly
if ($intTime < -62167241486) {
// Set to "null date"
// And Do Nothing Else -- Default Values are already set to Nulled out
parent::__construct('2000-01-01 00:00:00', $objTimeZone);
} else {
parent::__construct(date('Y-m-d H:i:s', $intTime), $objTimeZone);
$this->blnDateNull = false;
$this->blnTimeNull = false;
}
} else { // error
parent::__construct();
$this->blnDateNull = true;
$this->blnTimeNull = true;
}
}
// User is requesting to force a particular type.
switch ($intType) {
case QDateTime::DateOnlyType:
$this->blnTimeNull = true;
$this->ReinforceNullProperties();
return;
case QDateTime::TimeOnlyType:
$this->blnDateNull = true;
$this->ReinforceNullProperties();
return;
case QDateTime::DateAndTimeType: // forcing both a date and time type to not be null
$this->blnDateNull = false;
$this->blnTimeNull = false;
break;
default:
break;
}
}
/**
* Returns a new QDateTime object set to the last day of the specified month.
*
* @param int $intMonth
* @param int $intYear
* @return QDateTime the last day to a month in a year
*/
public static function LastDayOfTheMonth($intMonth, $intYear) {
$temp = date('Y-m-t',mktime(0,0,0,$intMonth,1,$intYear));
return new QDateTime($temp);
}
/**
* Returns a new QDateTime object set to the first day of the specified month.
*
* @param int $intMonth
* @param int $intYear
* @return QDateTime the first day of the month
*/
public static function FirstDayOfTheMonth($intMonth, $intYear) {
$temp = date('Y-m-d',mktime(0,0,0,$intMonth,1,$intYear));
return new QDateTime($temp);
}
/**
* Formats a date as a string using the default format type.
* @return string
*/
public function __toString() {
return $this->qFormat();
}
/**
* The following code is a workaround for a PHP bug in 5.2 and greater (at least to 5.4).
*/
//protected $strSerializedData;
//protected $strSerializedTZ;
public function serialize() {
$tz = $this->getTimezone();
if ($tz && in_array ($tz->getName(), timezone_identifiers_list())) {
$strTz = $tz->getName();
$strDate = parent::format('Y-m-d H:i:s');
} else {
$strTz = null;
$strDate = parent::format (DateTime::ISO8601);
}
return serialize([
1, // version number of serialized data, in case format changes
$this->blnDateNull,
$this->blnTimeNull,
$strDate,
$strTz
]);
}
public function unserialize($s) {
$a = unserialize($s);
$this->blnDateNull = $a[1];
$this->blnTimeNull = $a[2];
$tz = $a[4];
if ($tz) {
$tz = new DateTimeZone($tz);
} else {
$tz = null;
}
parent::__construct($a[3], $tz);
}
/**
* Outputs the date as a string given the format strFormat. Will use
* the static defaults if none given.
*
* Properties of strFormat are (using Sunday, March 2, 1977 at 1:15:35 pm
* in the following examples):
*
* M - Month as an integer (e.g., 3)
* MM - Month as an integer with leading zero (e.g., 03)
* MMM - Month as three-letters (e.g., Mar)
* MMMM - Month as full name (e.g., March)
*
* D - Day as an integer (e.g., 2)
* DD - Day as an integer with leading zero (e.g., 02)
* DDD - Day of week as three-letters (e.g., Wed)
* DDDD - Day of week as full name (e.g., Wednesday)
*
* YY - Year as a two-digit integer (e.g., 77)
* YYYY - Year as a four-digit integer (e.g., 1977)
*
* h - Hour as an integer in 12-hour format (e.g., 1)
* hh - Hour as an integer in 12-hour format with leading zero (e.g., 01)
* hhh - Hour as an integer in 24-hour format (e.g., 13)
* hhhh - Hour as an integer in 24-hour format with leading zero (e.g., 13)
*
* mm - Minute as a two-digit integer
*
* ss - Second as a two-digit integer
*
* z - "pm" or "am"
* zz - "PM" or "AM"
* zzz - "p.m." or "a.m."
* zzzz - "P.M." or "A.M."
*
* ttt - Timezone Abbreviation as a three-letter code (e.g. PDT, GMT)
* tttt - Timezone Identifier (e.g. America/Los_Angeles)
*
* @param string $strFormat the format of the date
* @return string the formatted date as a string
*/
public function qFormat($strFormat = null) {
if ($this->blnDateNull && $this->blnTimeNull) {
return '';
}
if (is_null($strFormat)) {
if ($this->blnDateNull && !$this->blnTimeNull) {
$strFormat = QDateTime::$DefaultTimeFormat;
} elseif (!$this->blnDateNull && $this->blnTimeNull) {
$strFormat = QDateTime::$DefaultDateOnlyFormat;
} else {
$strFormat = QDateTime::$DefaultFormat;
}
}
/*
(?(?=D)([D]+)|
(?(?=M)([M]+)|
(?(?=Y)([Y]+)|
(?(?=h)([h]+)|
(?(?=m)([m]+)|
(?(?=s)([s]+)|
(?(?=z)([z]+)|
(?(?=t)([t]+)|
))))))))
*/
// $strArray = preg_split('/([^D^M^Y^h^m^s^z^t])+/', $strFormat);
preg_match_all('/(?(?=D)([D]+)|(?(?=M)([M]+)|(?(?=Y)([Y]+)|(?(?=h)([h]+)|(?(?=m)([m]+)|(?(?=s)([s]+)|(?(?=z)([z]+)|(?(?=t)([t]+)|))))))))/', $strFormat, $strArray);
$strArray = $strArray[0];
$strToReturn = '';
$intStartPosition = 0;
for ($intIndex = 0; $intIndex < count($strArray); $intIndex++) {
$strToken = trim($strArray[$intIndex]);
if ($strToken) {
$intEndPosition = strpos($strFormat, $strArray[$intIndex], $intStartPosition);
$strToReturn .= substr($strFormat, $intStartPosition, $intEndPosition - $intStartPosition);
$intStartPosition = $intEndPosition + strlen($strArray[$intIndex]);
switch ($strArray[$intIndex]) {
case 'M':
$strToReturn .= parent::format('n');
break;
case 'MM':
$strToReturn .= parent::format('m');
break;
case 'MMM':
$strToReturn .= parent::format('M');
break;
case 'MMMM':
$strToReturn .= parent::format('F');
break;
case 'D':
$strToReturn .= parent::format('j');
break;
case 'DD':
$strToReturn .= parent::format('d');
break;
case 'DDD':
$strToReturn .= parent::format('D');
break;
case 'DDDD':
$strToReturn .= parent::format('l');
break;
case 'YY':
$strToReturn .= parent::format('y');
break;
case 'YYYY':
$strToReturn .= parent::format('Y');
break;
case 'h':
$strToReturn .= parent::format('g');
break;
case 'hh':
$strToReturn .= parent::format('h');
break;
case 'hhh':
$strToReturn .= parent::format('G');
break;
case 'hhhh':
$strToReturn .= parent::format('H');
break;
case 'mm':
$strToReturn .= parent::format('i');
break;
case 'ss':
$strToReturn .= parent::format('s');
break;
case 'z':
$strToReturn .= parent::format('a');
break;
case 'zz':
$strToReturn .= parent::format('A');
break;
case 'zzz':
$strToReturn .= sprintf('%s.m.', substr(parent::format('a'), 0, 1));
break;
case 'zzzz':
$strToReturn .= sprintf('%s.M.', substr(parent::format('A'), 0, 1));
break;
case 'ttt':
$strToReturn .= parent::format('T');
break;
case 'tttt':
$strToReturn .= parent::format('e');
break;
case 'ttttt':
$strToReturn .= parent::format('O');
break;
default:
$strToReturn .= $strArray[$intIndex];
}
}
}
if ($intStartPosition < strlen($strFormat))
$strToReturn .= substr($strFormat, $intStartPosition);
return $strToReturn;
}
/**
* Sets the time portion to the given time. If a QDateTime is given, will use the time portion of that object.
* Works around a problem in php that if you set the time across a daylight savings time boundary, the timezone
* does not advance. This version will detect that and advance the timezone.
*
* @param int|QDateTime $mixValue
* @param int|null $intMinute
* @param int|null $intSecond
* @return QDateTime
*/
public function setTime($mixValue, $intMinute = null, $intSecond = null, $intMicroSeconds = null) {
if ($mixValue instanceof QDateTime) {
if ($mixValue->IsTimeNull()) {
$this->blnTimeNull = true;
$this->ReinforceNullProperties();
return $this;
}
// normalize the timezones
$tz = $this->getTimezone();
if ($tz && in_array ($tz->getName(), timezone_identifiers_list())) {
// php limits you to ID only timezones here, so make sure we have one of those
$mixValue->setTimezone ($tz);
}
$intHour = $mixValue->Hour;
$intMinute = $mixValue->Minute;
$intSecond = $mixValue->Second;
} else {
$intHour = $mixValue;
}
// If HOUR or MINUTE is NULL...
if (is_null($intHour) || is_null($intMinute)) {
if (version_compare(PHP_VERSION, '7.2.0', '>=')) {
parent::setTime($intHour, $intMinute, $intSecond, $intMicroSeconds);
} else {
parent::setTime($intHour, $intMinute, $intSecond);
}
$this->blnTimeNull = true;
$this->ReinforceNullProperties();
return $this;
}
$intHour = QType::Cast($intHour, QType::Integer);
$intMinute = QType::Cast($intMinute, QType::Integer);
$intSecond = QType::Cast($intSecond, QType::Integer);
$this->blnTimeNull = false;
/*
// Possible fix for a PHP problem. Can't reproduce, so leaving code here just in case it comes back.
// The problem is with setting times across dst barriers
if ($this->Hour == 0 && preg_match('/[0-9]+/', $this->getTimezone()->getName())) {
// fix a php problem with GMT and relative timezones
$s = 'PT' . $intHour . 'H' . $intMinute . 'M' . $intSecond . 'S';
$this->add (new DateInterval ($s));
// will continue and set again to make sure, because boundary crossing will change the time
}*/
if (version_compare(PHP_VERSION, '7.2.0', '>=')) {
parent::setTime($intHour, $intMinute, $intSecond, $intMicroSeconds);
} else {
parent::setTime($intHour, $intMinute, $intSecond);
}
return $this;
}
/**
* Set the date.
*
* @param int $intYear
* @param int $intMonth
* @param int $intDay
* @return $this|DateTime
*/
public function setDate($intYear, $intMonth, $intDay) {
$intYear = QType::Cast($intYear, QType::Integer);
$intMonth = QType::Cast($intMonth, QType::Integer);
$intDay = QType::Cast($intDay, QType::Integer);
$this->blnDateNull = false;
parent::setDate($intYear, $intMonth, $intDay);
return $this;
}
protected function ReinforceNullProperties() {
if ($this->blnDateNull)
parent::setDate(2000, 1, 1);
if ($this->blnTimeNull) {
parent::setTime(0, 0, 0);
}
}
/**
* Converts the current QDateTime object to a different TimeZone.
*
* TimeZone should be passed in as a string-based identifier.
*
* Note that this is different than the built-in DateTime::SetTimezone() method which expicitly
* takes in a DateTimeZone object. QDateTime::ConvertToTimezone allows you to specify any
* string-based Timezone identifier. If none is specified and/or if the specified timezone
* is not a valid identifier, it will simply remain unchanged as opposed to throwing an exeception
* or error.
*
* @param string $strTimezoneIdentifier a string-based parameter specifying a timezone identifier (e.g. America/Los_Angeles)
* @return void
*/
public function ConvertToTimezone($strTimezoneIdentifier) {
try {
$dtzNewTimezone = new DateTimeZone($strTimezoneIdentifier);
$this->SetTimezone($dtzNewTimezone);
} catch (Exception $objExc) {}
}
/**
* Returns true if give QDateTime is the same.
*
* @param QDateTime $dttCompare
* @return bool
*/
public function IsEqualTo(QDateTime $dttCompare) {
// All comparison operations MUST have operands with matching Date Nullstates
if ($this->blnDateNull != $dttCompare->blnDateNull)
return false;
// If mismatched Time nullstates, then only compare the Date portions
if ($this->blnTimeNull != $dttCompare->blnTimeNull) {
// Let's "Null Out" the Time
$dttThis = new QDateTime($this);
$dttThat = new QDateTime($dttCompare);
$dttThis->Hour = null;
$dttThat->Hour = null;
// Return the Result
return ($dttThis->Timestamp == $dttThat->Timestamp);
} else {
// Return the Result for the both Date and Time components
return ($this->Timestamp == $dttCompare->Timestamp);
}
}
/**
* Returns true if current date time is earlier than the given one.
* @param QDateTime $dttCompare
* @return bool
*/
public function IsEarlierThan(QDateTime $dttCompare) {
// All comparison operations MUST have operands with matching Date Nullstates
if ($this->blnDateNull != $dttCompare->blnDateNull)
return false;
// If mismatched Time nullstates, then only compare the Date portions
if ($this->blnTimeNull != $dttCompare->blnTimeNull) {
// Let's "Null Out" the Time
$dttThis = new QDateTime($this);
$dttThat = new QDateTime($dttCompare);
$dttThis->Hour = null;
$dttThat->Hour = null;
// Return the Result
return ($dttThis->Timestamp < $dttThat->Timestamp);
} else {
// Return the Result for the both Date and Time components
return ($this->Timestamp < $dttCompare->Timestamp);
}
}
/**
* Returns true if current date time is earlier than the given one.
* @param QDateTime $dttCompare
* @return bool
*/
public function IsEarlierOrEqualTo(QDateTime $dttCompare) {
// All comparison operations MUST have operands with matching Date Nullstates
if ($this->blnDateNull != $dttCompare->blnDateNull)
return false;
// If mismatched Time nullstates, then only compare the Date portions
if ($this->blnTimeNull != $dttCompare->blnTimeNull) {
// Let's "Null Out" the Time
$dttThis = new QDateTime($this);
$dttThat = new QDateTime($dttCompare);
$dttThis->Hour = null;
$dttThat->Hour = null;
// Return the Result
return ($dttThis->Timestamp <= $dttThat->Timestamp);
} else {
// Return the Result for the both Date and Time components
return ($this->Timestamp <= $dttCompare->Timestamp);
}
}
/**
* Returns true if current date time is later than the given one.
* @param QDateTime $dttCompare
* @return bool
*/
public function IsLaterThan(QDateTime $dttCompare) {
// All comparison operations MUST have operands with matching Date Nullstates
if ($this->blnDateNull != $dttCompare->blnDateNull)
return false;
// If mismatched Time nullstates, then only compare the Date portions
if ($this->blnTimeNull != $dttCompare->blnTimeNull) {
// Let's "Null Out" the Time
$dttThis = new QDateTime($this);
$dttThat = new QDateTime($dttCompare);
$dttThis->Hour = null;
$dttThat->Hour = null;
// Return the Result
return ($dttThis->Timestamp > $dttThat->Timestamp);
} else {
// Return the Result for the both Date and Time components
return ($this->Timestamp > $dttCompare->Timestamp);
}
}
/**
* Returns true if current date time is later than or equal to the given one.
* @param QDateTime $dttCompare
* @return bool
*/
public function IsLaterOrEqualTo(QDateTime $dttCompare) {
// All comparison operations MUST have operands with matching Date Nullstates
if ($this->blnDateNull != $dttCompare->blnDateNull)
return false;
// If mismatched Time nullstates, then only compare the Date portions
if ($this->blnTimeNull != $dttCompare->blnTimeNull) {
// Let's "Null Out" the Time
$dttThis = new QDateTime($this);
$dttThat = new QDateTime($dttCompare);
$dttThis->Hour = null;
$dttThat->Hour = null;
// Return the Result
return ($dttThis->Timestamp >= $dttThat->Timestamp);
} else {
// Return the Result for the both Date and Time components
return ($this->Timestamp >= $dttCompare->Timestamp);
}
}
/**
* Compare the current date with the given date. Return -1 if current date is less than given date, 0 if equal,
* and 1 if greater. Null dates are considered the earliest possible date.
*
* @param QDateTime $dttCompare
* @return int
*/
public function Compare(QDateTime $dttCompare) {
// All comparison operations MUST have operands with matching Date Nullstates
if ($this->blnDateNull && !$dttCompare->blnDateNull) {
return -1;
} elseif (!$this->blnDateNull && $dttCompare->blnDateNull) {
return 1;
}
// If mismatched Time nullstates, then only compare the Date portions
if ($this->blnTimeNull != $dttCompare->blnTimeNull) {
// Let's "Null Out" the Time
$dttThis = new QDateTime($this);
$dttThat = new QDateTime($dttCompare);
$dttThis->Hour = null;
$dttThat->Hour = null;
} else {
$dttThis = $this;
$dttThat = $dttCompare;
}
return ($dttThis->Timestamp < $dttThat->Timestamp ? -1 : ($dttThis->Timestamp == $dttThat->Timestamp ? 0 : 1));
}
/**
* Returns the difference as a QDateSpan, which is easier to work with and more full featured than
* the php DateTimeInterval class.
*
* @param QDateTime $dttDateTime
* @return QDateTimeSpan
*/
public function Difference(QDateTime $dttDateTime) {
$intDifference = $this->Timestamp - $dttDateTime->Timestamp;
return new QDateTimeSpan($intDifference);
}
/**
* Add a datespan or interval to the current date.
*
* @param DateInterval|QDateTimeSpan $dtsSpan
* @return QDateTime
* @throws QCallerException
*/
public function Add($dtsSpan){
if ($dtsSpan instanceof DateInterval) {
parent::add($dtsSpan);
return $this;
}
elseif (!$dtsSpan instanceof QDateTimeSpan) {
throw new QCallerException("Can only add DateTimeInterval or QDateTimeSpan objects");
}
// And add the Span Second count to it
$this->Timestamp = $this->Timestamp + $dtsSpan->Seconds;
return $this;
}
/**
* Add a number of seconds. Use negative value to go earlier in time.
*
* @param integer $intSeconds
* @return QDateTime
*/
public function AddSeconds($intSeconds){
$this->Second += $intSeconds;
return $this;
}
/**
* Add minutes to the time.
*
* @param integer $intMinutes
* @return QDateTime
*/
public function AddMinutes($intMinutes){
$this->Minute += $intMinutes;
return $this;
}
/**
* Add hours to the time.
*
* @param integer $intHours
* @return QDateTime
*/
public function AddHours($intHours){
$this->Hour += $intHours;
return $this;
}
/**
* Add days to the time.
*
* @param integer $intDays
* @return QDateTIme
*/
public function AddDays($intDays){
$this->Day += $intDays;
return $this;
}
/**
* Add months to the time. If the day on the new month is greater than the month will allow, the day is adjusted
* to be the last day of that month.
*
* @param integer $intMonths
* @return QDateTime
*/
public function AddMonths($intMonths){
$prevDay = $this->Day;
$this->Month += $intMonths;
if ($this->Day != $prevDay) {
$this->Day = 1;
$this->AddDays (-1);
}
return $this;
}
/**
* Add years to the time.
*
* @param integer $intYears
* @return QDateTime
*/
public function AddYears($intYears){
$this->Year += $intYears;
return $this;
}
/**
* Modifies the date or time based on values found int a string.
*
* @see DateTime::modify()
* @param string $mixValue
* @return QDateTime
*/
public function Modify($mixValue) {
parent::modify($mixValue);
return $this;
}
/**
* Convert the object to a javascript object. This is code that if executed in javascript would produce a Date
* javascript object. Note that the date will be created in the browser's local timezone, so convert to the
* browser's timezone first if that is important for you.
*
* @return string
*/
public function toJsObject() {
if ($this->blnDateNull) {
$dt = self::Now(); // time only will use today's date.
$dt->setTime($this);
} else {
$dt = clone $this;
}
if ($this->blnTimeNull) {
return sprintf ('new Date(%s, %s, %s)', $dt->Year, $dt->Month - 1, $dt->Day);
} else {
return sprintf ('new Date(%s, %s, %s, %s, %s, %s)', $dt->Year, $dt->Month - 1, $dt->Day, $dt->Hour, $dt->Minute, $dt->Second);
}
}
/**
* Returns a datetime in a way that it will pass through a json_encode and be decodalbe in qcubed.js.
* qcubed.unpackParams looks for this.
*
* @return array|mixed;
*/
public function jsonSerialize() {
if ($this->blnDateNull) {
$dt = self::Now(); // time only will use today's date.
$dt->setTime($this);
} else {
$dt = clone $this;
}
if ($this->blnTimeNull) {
return [JavaScriptHelper::ObjectType=>'qDateTime', 'year'=>$dt->Year, 'month'=>$dt->Month - 1,
'day'=>$dt->Day];
} else {
return [JavaScriptHelper::ObjectType=>'qDateTime', 'year'=>$dt->Year, 'month'=>$dt->Month - 1,
'day'=>$dt->Day, 'hour'=>$dt->Hour, 'minute'=>$dt->Minute, 'second'=>$dt->Second];
}
}
/**
* PHP magic method
* @param $strName
*
* @return QDateTime|string
* @throws QUndefinedPropertyException
*/
public function __get($strName) {
switch ($strName) {
case 'Month':
if ($this->blnDateNull)
return null;
else
return (int) parent::format('m');
case 'Day':
if ($this->blnDateNull)
return null;
else
return (int) parent::format('d');
case 'Year':
if ($this->blnDateNull)
return null;
else
return (int) parent::format('Y');
case 'Hour':
if ($this->blnTimeNull)
return null;
else
return (int) parent::format('H');
case 'Minute':
if ($this->blnTimeNull)
return null;
else
return (int) parent::format('i');
case 'Second':
if ($this->blnTimeNull)
return null;
else
return (int) parent::format('s');
case 'Timestamp':
return (int) parent::format('U'); // range depends on the platform's max and min integer values
case 'Age':
// Figure out the Difference from "Now"
$dtsFromCurrent = $this->Difference(QDateTime::Now());
// It's in the future ('about 2 hours from now')
if ($dtsFromCurrent->IsPositive())
return $dtsFromCurrent->SimpleDisplay() . QApplication::Translate(' from now');
// It's in the past ('about 5 hours ago')
else if ($dtsFromCurrent->IsNegative()) {
$dtsFromCurrent->Seconds = abs($dtsFromCurrent->Seconds);
return $dtsFromCurrent->SimpleDisplay() . QApplication::Translate(' ago');
// It's current
} else
return QApplication::Translate('right now');
case 'LastDayOfTheMonth':
return self::LastDayOfTheMonth($this->Month, $this->Year);
case 'FirstDayOfTheMonth':
return self::FirstDayOfTheMonth($this->Month, $this->Year);
default:
throw new QUndefinedPropertyException('GET', 'QDateTime', $strName);
}
}
/**
* PHP magic method
*
* @param $strName
* @param $mixValue
*
* @return mixed
* @throws Exception|QCallerException|QDateTimeNullException|QInvalidCastException|QUndefinedPropertyException
*/
public function __set($strName, $mixValue) {
try {
switch ($strName) {
case 'Month':
if ($this->blnDateNull && (!is_null($mixValue)))
throw new QDateTimeNullException('Cannot set the Month property on a null date. Use SetDate().');
if (is_null($mixValue)) {
$this->blnDateNull = true;
$this->ReinforceNullProperties();
return null;
}
$mixValue = QType::Cast($mixValue, QType::Integer);
parent::setDate(parent::format('Y'), $mixValue, parent::format('d'));
return $mixValue;
case 'Day':
if ($this->blnDateNull && (!is_null($mixValue)))
throw new QDateTimeNullException('Cannot set the Day property on a null date. Use SetDate().');
if (is_null($mixValue)) {
$this->blnDateNull = true;
$this->ReinforceNullProperties();
return null;
}
$mixValue = QType::Cast($mixValue, QType::Integer);
parent::setDate(parent::format('Y'), parent::format('m'), $mixValue);
return $mixValue;
case 'Year':
if ($this->blnDateNull && (!is_null($mixValue)))
throw new QDateTimeNullException('Cannot set the Year property on a null date. Use SetDate().');
if (is_null($mixValue)) {
$this->blnDateNull = true;
$this->ReinforceNullProperties();
return null;
}
$mixValue = QType::Cast($mixValue, QType::Integer);
parent::setDate($mixValue, parent::format('m'), parent::format('d'));
return $mixValue;
case 'Hour':
if ($this->blnTimeNull && (!is_null($mixValue)))
throw new QDateTimeNullException('Cannot set the Hour property on a null time. Use SetTime().');
if (is_null($mixValue)) {
$this->blnTimeNull = true;
$this->ReinforceNullProperties();
return null;
}
$mixValue = QType::Cast($mixValue, QType::Integer);
parent::setTime($mixValue, parent::format('i'), parent::format('s'));
return $mixValue;
case 'Minute':
if ($this->blnTimeNull && (!is_null($mixValue)))
throw new QDateTimeNullException('Cannot set the Minute property on a null time. Use SetTime().');
if (is_null($mixValue)) {
$this->blnTimeNull = true;
$this->ReinforceNullProperties();
return null;
}
$mixValue = QType::Cast($mixValue, QType::Integer);
parent::setTime(parent::format('H'), $mixValue, parent::format('s'));
return $mixValue;
case 'Second':
if ($this->blnTimeNull && (!is_null($mixValue)))
throw new QDateTimeNullException('Cannot set the Second property on a null time. Use SetTime().');
if (is_null($mixValue)) {
$this->blnTimeNull = true;
$this->ReinforceNullProperties();
return null;
}
$mixValue = QType::Cast($mixValue, QType::Integer);
parent::setTime(parent::format('H'), parent::format('i'), $mixValue);
return $mixValue;
case 'Timestamp':
$mixValue = QType::Cast($mixValue, QType::Integer);
$this->setTimestamp($mixValue);
$this->blnDateNull = false;
$this->blnTimeNull = false;
return $mixValue;
default:
throw new QUndefinedPropertyException('SET', 'QDateTime', $strName);
}
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
}
}
/*
This is a reference to the documentation for hte PHP DateTime classes (as of PHP 5.2)
DateTime::ATOM
DateTime::COOKIE
DateTime::ISO8601
DateTime::RFC822
DateTime::RFC850
DateTime::RFC1036
DateTime::RFC1123
DateTime::RFC2822
DateTime::RFC3339
DateTime::RSS
DateTime::W3C
DateTime::__construct([string time[, DateTimeZone object]])
- Returns new DateTime object
string DateTime::format(string format)
- Returns date formatted according to given format
long DateTime::getOffset()
- Returns the DST offset
DateTimeZone DateTime::getTimezone()
- Return new DateTimeZone object relative to give DateTime
void DateTime::modify(string modify)
- Alters the timestamp
array DateTime::parse(string date)
- Returns associative array with detailed info about given date
void DateTime::setDate(long year, long month, long day)
- Sets the date
void DateTime::setISODate(long year, long week[, long day])
- Sets the ISO date
void DateTime::setTime(long hour, long minute[, long second])
- Sets the time
void DateTime::setTimezone(DateTimeZone object)
- Sets the timezone for the DateTime object
*/
/* Some quick and dirty test harnesses
$dtt1 = new QDateTime();
$dtt2 = new QDateTime();
printTable($dtt1, $dtt2);
$dtt2->setDate(2000, 1, 1);
$dtt1->setTime(0,0,3);
$dtt2->setTime(0,0,2);
// $dtt2->Month++;
printTable($dtt1, $dtt2);
function printTable($dtt1, $dtt2) {
print('<table border="1" cellpadding="2"><tr><td>');
printDate($dtt1);
print('</td><td>');
printDate($dtt2);
print ('</td></tr>');
print ('<tr><td colspan="2" align="center">IsEqualTo: <b>' . (($dtt1->IsEqualTo($dtt2)) ? 'Yes' : 'No') . '</b></td></tr>');
print ('<tr><td colspan="2" align="center">IsEarlierThan: <b>' . (($dtt1->IsEarlierThan($dtt2)) ? 'Yes' : 'No') . '</b></td></tr>');
print ('<tr><td colspan="2" align="center">IsLaterThan: <b>' . (($dtt1->IsLaterThan($dtt2)) ? 'Yes' : 'No') . '</b></td></tr>');
print ('<tr><td colspan="2" align="center">IsEarlierOrEqualTo: <b>' . (($dtt1->IsEarlierOrEqualTo($dtt2)) ? 'Yes' : 'No') . '</b></td></tr>');
print ('<tr><td colspan="2" align="center">IsLaterOrEqualTo: <b>' . (($dtt1->IsLaterOrEqualTo($dtt2)) ? 'Yes' : 'No') . '</b></td></tr>');
print('</table>');
}
function printDate($dtt) {
print ('Time Null: ' . (($dtt->IsTimeNull()) ? 'Yes' : 'No'));
print ('<br/>');
print ('Date Null: ' . (($dtt->IsDateNull()) ? 'Yes' : 'No'));
print ('<br/>');
print ('Date: ' . $dtt->qFormat(QDateTime::FormatDisplayDateTimeFull));
print ('<br/>');
print ('Month: ' . $dtt->Month . '<br/>');
print ('Day: ' . $dtt->Day . '<br/>');
print ('Year: ' . $dtt->Year . '<br/>');
print ('Hour: ' . $dtt->Hour . '<br/>');
print ('Minute: ' . $dtt->Minute . '<br/>');
print ('Second: ' . $dtt->Second . '<br/>');
}*/