use \DateTime; // http://php.net/manual/en/class.datetime.php
7 years ago
use \DateInterval; // http://php.net/manual/en/class.dateinterval.php
use \DateTimeZone; // http://php.net/manual/en/class.datetimezone.php
7 years ago
7 years ago
use \ArrayAccess; // http://php.net/manual/en/class.arrayaccess.php Interface to provide accessing objects as arrays.
use \IteratorAggregate; // http://php.net/manual/en/class.iteratoraggregate.php Interface to create an external Iterator.
use \Closure;
7 years ago
/*
use ArrayAccess; // http://php.net/manual/en/class.arrayaccess.php Interface to provide accessing objects as arrays.
use ArrayIterator; // http://php.net/manual/en/class.arrayiterator.php This iterator allows to unset and modify values and keys while iterating over Arrays and Objects.
use Countable; // http://php.net/manual/en/class.countable.php Classes implementing Countable can be used with the count() function.
use IteratorAggregate; // http://php.net/manual/en/class.iteratoraggregate.php Interface to create an external Iterator.
use Exception; // http://php.net/manual/en/class.exception.php Exception is the base class for all Exceptions in PHP 5, and the base class for all user exceptions in PHP 7.
use InvalidArgumentException; // http://php.net/manual/en/class.invalidargumentexception.php Exception thrown if an argument is not of the expected type.
use OutOfBoundsException; // http://php.net/manual/en/class.outofboundsexception.php Exception thrown if a value is not a valid key. This represents errors that cannot be detected at compile time.
use OutOfRangeException; // http://php.net/manual/en/class.outofrangeexception.php Exception thrown when an illegal index was requested. This represents errors that should be detected at compile time.
use BadMethodCallException; // http://php.net/manual/en/class.badmethodcallexception.php Exception thrown if a callback refers to an undefined method or if some arguments are missing.
use LengthException; // http://php.net/manual/en/class.lengthexception.php Exception thrown if a length is invalid.
use LogicException; // http://php.net/manual/en/class.logicexception.php Exception that represents error in the program logic. This kind of exception should lead directly to a fix in your code.
use DomainException; // http://php.net/manual/en/class.domainexception.php Exception thrown if a value does not adhere to a defined valid data domain.
use RangeException; // http://php.net/manual/en/class.rangeexception.php Exception thrown to indicate range errors during program execution. Normally this means there was an arithmetic error other than under/overflow. This is the runtime version of DomainException.
use UnexpectedValueException; // http://php.net/manual/en/class.unexpectedvalueexception.php Exception thrown if a value does not match with a set of values. Typically this happens when a function calls another function and expects the return value to be of a certain type or value not including arithmetic or buffer related errors.
use OverflowException; // http://php.net/manual/en/class.overflowexception.php Exception thrown when adding an element to a full container.
use UnderflowException; // http://php.net/manual/en/class.underflowexception.php Exception thrown when performing an invalid operation on an empty container, such as removing an element.
*/
7 years ago
class Date implements ArrayAccess, IteratorAggregate
throw new InvalidArgumentException('Array constructor not implemented');
}
else
{
throw new InvalidArgumentException(sprintf(
'Invalid type passed to Date constructor; expecting a string or DateTime object, received "%s"; use normalize() to create Date objects from various formats',
* Date::check([8, 5, 2017]) === true - year is 3rd array member, same as `checkdate` and MUST be over 100
* Date::check([30, 4, 2017]) === false - year is 3rd array member, same as `checkdate` BUT `day` MUST be 2nd param and `month` MUST be 3rd param like `checkdate()`
*
* @param mixed $str Value to modify, after being cast to string
7 years ago
* @return new Twister\Date instance or false on failure
7 years ago
* @throws \InvalidArgumentException if an array or object without a
* __toString method is passed as the first argument
*/
public static function check(...$params)
{
return self::normalize(...$params) !== false;
}
/**
* Normalize a given value or values to MySQL date format (YYYY-MM-DD)
* Date::check([8, 5, 2017]) === true - year is 3rd array member, same as `checkdate` and MUST be over 100
* Date::check([30, 4, 2017]) === false - year is 3rd array member, same as `checkdate` BUT `day` MUST be 2nd param and `month` MUST be 3rd param like `checkdate()`
*
* @param mixed $str Value to modify, after being cast to string
7 years ago
* @return new Twister\Date instance or false on failure
7 years ago
* @throws \InvalidArgumentException if an array or object without a
* __toString method is passed as the first argument
*/
public static function normalize(...$params)
{
switch (count($params))
{
case 2:
if ( ! is_bool($params[1]))
{
if (is_string($params[1])) // $params[1] can be a format like 'Y-m-d h:i' or whatever, we need to `extract` the date component to match '~^...$~' below
{
// TODO: extract date from format
}
return false;
}
else
{
if ($params[1] === true)
{
$date = $params[0];
if (is_string($date) && $date === '0000-00-00')
{
return $date; // `true` allows 0000-00-00
}
}
}
7 years ago
// fallthrough
7 years ago
case 1:
$date = $params[0];
if (is_string($date))
{
7 years ago
if (preg_match('~^([1-9]\d\d\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$~', $date, $matches) === 1)
else if (preg_match('~([1-9]\d\d\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])~', $date, $matches) === 1) // removed the ~^ ... $~ first & last matches! Also added more delimeters
* @param mixed $offset The index from which to retrieve the char
* @return mixed The character at the specified index
* @throws \OutOfBoundsException If the positive or negative offset does
* not exist
*/
public function offsetGet($offset)
{
7 years ago
switch ($name)
{
case 'year': return $this->date->format('Y');
case 'month': return $this->date->format('m');
case 'day': return $this->date->format('d');
case 0: return $this->date->format('Y');
case 1: return $this->date->format('m');
case 2: return $this->date->format('d');
}
7 years ago
7 years ago
if (strlen($name) === 1)
return $this->date->format($name);
/*
// http://php.net/manual/en/function.date.php
case 'd': return ; // Day of the month, 2 digits with leading zeros eg. 01 to 31
case 'D': return ; // A textual representation of a day, three letters eg. Mon through Sun
case 'j': return ; // Day of the month without leading zeros eg. 1 to 31
case 'l': return ; // A full textual representation of the day of the week eg. Sunday through Saturday
case 'N': return ; // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0 eg. 1 (for Monday) through 7 (for Sunday)
case 'S': return ; // English ordinal suffix for the day of the month, 2 characters eg. st, nd, rd or th. Works well with j
case 'w': return ; // Numeric representation of the day of the week eg. 0 (for Sunday) through 6 (for Saturday)
case 'z': return ; // The day of the year (starting from 0) eg. 0 through 365
case 'W': return ; // ISO-8601 week number of year, weeks starting on Monday eg. Example: 42 (the 42nd week in the year)
case 'F': return ; // A full textual representation of a month, such as January or March eg. January through December
case 'm': return ; // Numeric representation of a month, with leading zeros eg. 01 through 12
case 'M': return ; // A short textual representation of a month, three letters eg. Jan through Dec
case 'n': return ; // Numeric representation of a month, without leading zeros eg. 1 through 12
case 't': return ; // Number of days in the given month eg. 28 through 31
case 'L': return ; // Whether it's a leap year eg. 1 if it is a leap year, 0 otherwise.
case 'o': return ; // ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0) eg. Examples: 1999 or 2003
case 'Y': return ; // A full numeric representation of a year, 4 digits eg. Examples: 1999 or 2003
case 'y': return ; // A two digit representation of a year eg. Examples: 99 or 03
case 'U': return ; // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) eg. See also time()
case 'dayname': return $this->date->format('l'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayname MySQL: Returns the name of the weekday for date. The language used for the name is controlled by the value of the lc_time_names system variable
case 'dayofmonth': return $this->date->format('j'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayofmonth MySQL: Returns the day of the month for date, in the range 1 to 31, or 0 for dates such as '0000-00-00' or '2008-00-00' that have a zero day part.
case 'dayofyear': return $this->date->format('z') + 1; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayofyear MySQL: Returns the day of the year for date, in the range 1 to 366.
case 'monthname': return $this->date->format('F'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_monthname MySQL: Returns the full name of the month for date. The language used for the name is controlled by the value of the lc_time_names system variable
case 'timestamp': return $this->date->format('U'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timestamp MySQL: With a single argument, this function returns the date or datetime expression expr as a datetime value. With two arguments, it adds the time expression expr2 to the date or datetime expression expr1 and returns the result as a datetime value.
case 'unix_timestamp': return $this->date->format('U'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp MySQL: If called with no argument, returns a Unix timestamp (seconds since '1970-01-01 00:00:00' UTC).
case 'to_days': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_to-days MySQL: Given a date date, returns a day number (the number of days since year 0).
case 'utc_date': return $this->date->format('Y-m-d'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-date MySQL: Returns the current UTC date as a value in 'YYYY-MM-DD' or YYYYMMDD format, depending on whether the function is used in a string or numeric context.
case 'utc_time': return $this->date->format('H:i:s'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-time MySQL: Returns the current UTC time as a value in 'HH:MM:SS'
case 'utc_timestamp': return $this->date->format('Y-m-d H:i:s'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-timestamp MySQL: Returns the current UTC date and time as a value in 'YYYY-MM-DD HH:MM:SS' or YYYYMMDDHHMMSS format, depending on whether the function is used in a string or numeric context.
case 'quarter': return $this->date->format('m') / 4 + 1; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_quarter MySQL: Returns the quarter of the year for date, in the range 1 to 4.
case 'week': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_week MySQL: This function returns the week number for date. The two-argument form of WEEK() enables you to specify whether the week starts on Sunday or Monday and whether the return value should be in the range from 0 to 53 or from 1 to 53. If the mode argument is omitted, the value of the default_week_format system variable is used. See Section 5.1.5, “Server System Variables”.
case 'weekday': return $this->date->format('N') - 1; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_weekday MySQL: Returns the weekday index for date (0 = Monday, 1 = Tuesday, … 6 = Sunday).
case 'weekofyear': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_weekofyear MySQL: Returns the calendar week of the date as a number in the range from 1 to 53. WEEKOFYEAR() is a compatibility function that is equivalent to WEEK(date,3).
case 'yearweek': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_yearweek MySQL: Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.
case 'date': return $this->date->format('Y-m-d'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date MySQL: Extracts the date part of the date or datetime expression expr.
case 'hour': return $this->date->format('G'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour MySQL: Returns the hour for time. The range of the return value is 0 to 23 for time-of-day values. However, the range of TIME values actually is much larger, so HOUR can return values greater than 23.
case 'minute': return $this->date->format('i'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_minute MySQL: Returns the minute for time, in the range 0 to 59.
case 'second': return $this->date->format('s'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour MySQL: Returns the second for time, in the range 0 to 59.
7 years ago
}
7 years ago
throw new \InvalidArgumentException('Property offsetGet["' . $name . '"] does not exist!');
7 years ago
}
/**
7 years ago
* Implements part of the ArrayAccess interface
7 years ago
*
7 years ago
* @param mixed $offset The index of the character
* @param mixed $value Value to set
* @throws \Exception When called
7 years ago
*/
public function offsetSet($offset, $value)
{
7 years ago
throw new \Exception('Cannot set array indexes!');
7 years ago
}
/**
* Implements part of the ArrayAccess interface, but throws an exception
* when called. This maintains the immutability of Stringy objects.
*
* @param mixed $offset The index of the character
* @throws \Exception When called
*/
public function offsetUnset($offset)
{
7 years ago
throw new \Exception('Cannot unset array indexes!');
7 years ago
}
7 years ago
/**
* PHP equivalent of the LAST_DAY() MySQL function
*
* `Takes a date or datetime value and returns the corresponding value for the last day of the month. Returns NULL if the argument is invalid.`
case 'd': return ; // Day of the month, 2 digits with leading zeros eg. 01 to 31
case 'D': return ; // A textual representation of a day, three letters eg. Mon through Sun
case 'j': return ; // Day of the month without leading zeros eg. 1 to 31
case 'l': return ; // A full textual representation of the day of the week eg. Sunday through Saturday
case 'N': return ; // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0 eg. 1 (for Monday) through 7 (for Sunday)
7 years ago
case 'S': return ; // English ordinal suffix for the day of the month, 2 characters eg. st, nd, rd or th. Works well with j
case 'w': return ; // Numeric representation of the day of the week eg. 0 (for Sunday) through 6 (for Saturday)
case 'z': return ; // The day of the year (starting from 0) eg. 0 through 365
case 'W': return ; // ISO-8601 week number of year, weeks starting on Monday eg. Example: 42 (the 42nd week in the year)
case 'F': return ; // A full textual representation of a month, such as January or March eg. January through December
case 'm': return ; // Numeric representation of a month, with leading zeros eg. 01 through 12
case 'M': return ; // A short textual representation of a month, three letters eg. Jan through Dec
case 'n': return ; // Numeric representation of a month, without leading zeros eg. 1 through 12
case 't': return ; // Number of days in the given month eg. 28 through 31
case 'L': return ; // Whether it's a leap year eg. 1 if it is a leap year, 0 otherwise.
7 years ago
case 'o': return ; // ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0) eg. Examples: 1999 or 2003
7 years ago
case 'Y': return ; // A full numeric representation of a year, 4 digits eg. Examples: 1999 or 2003
case 'y': return ; // A two digit representation of a year eg. Examples: 99 or 03
case 'U': return ; // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) eg. See also time()
case 'dayname': return $this->date->format('l'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayname MySQL: Returns the name of the weekday for date. The language used for the name is controlled by the value of the lc_time_names system variable
case 'dayofmonth': return $this->date->format('j'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayofmonth MySQL: Returns the day of the month for date, in the range 1 to 31, or 0 for dates such as '0000-00-00' or '2008-00-00' that have a zero day part.
case 'dayofyear': return $this->date->format('z') + 1; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayofyear MySQL: Returns the day of the year for date, in the range 1 to 366.
case 'monthname': return $this->date->format('F'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_monthname MySQL: Returns the full name of the month for date. The language used for the name is controlled by the value of the lc_time_names system variable
case 'timestamp': return $this->date->format('U'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_timestamp MySQL: With a single argument, this function returns the date or datetime expression expr as a datetime value. With two arguments, it adds the time expression expr2 to the date or datetime expression expr1 and returns the result as a datetime value.
case 'unix_timestamp': return $this->date->format('U'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp MySQL: If called with no argument, returns a Unix timestamp (seconds since '1970-01-01 00:00:00' UTC).
case 'to_days': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_to-days MySQL: Given a date date, returns a day number (the number of days since year 0).
case 'utc_date': return $this->date->format('Y-m-d'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-date MySQL: Returns the current UTC date as a value in 'YYYY-MM-DD' or YYYYMMDD format, depending on whether the function is used in a string or numeric context.
case 'utc_time': return $this->date->format('H:i:s'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-time MySQL: Returns the current UTC time as a value in 'HH:MM:SS'
case 'utc_timestamp': return $this->date->format('Y-m-d H:i:s'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-timestamp MySQL: Returns the current UTC date and time as a value in 'YYYY-MM-DD HH:MM:SS' or YYYYMMDDHHMMSS format, depending on whether the function is used in a string or numeric context.
case 'quarter': return $this->date->format('m') / 4 + 1; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_quarter MySQL: Returns the quarter of the year for date, in the range 1 to 4.
case 'week': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_week MySQL: This function returns the week number for date. The two-argument form of WEEK() enables you to specify whether the week starts on Sunday or Monday and whether the return value should be in the range from 0 to 53 or from 1 to 53. If the mode argument is omitted, the value of the default_week_format system variable is used. See Section 5.1.5, “Server System Variables”.
case 'weekday': return $this->date->format('N') - 1; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_weekday MySQL: Returns the weekday index for date (0 = Monday, 1 = Tuesday, … 6 = Sunday).
case 'weekofyear': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_weekofyear MySQL: Returns the calendar week of the date as a number in the range from 1 to 53. WEEKOFYEAR() is a compatibility function that is equivalent to WEEK(date,3).
case 'yearweek': break; // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_yearweek MySQL: Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.
case 'date': return $this->date->format('Y-m-d'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date MySQL: Extracts the date part of the date or datetime expression expr.
case 'age': return (new \DateTime('now', self::$utc))->diff($this->date)->y;
case 'days': return (new \DateTime('now', self::$utc))->diff($this->date)->days;
case 'yesterday': return (clone $this)->modify('-1 day');
case 'tomorrow': return (clone $this)->modify('+1 day');
case 'prevday': return (clone $this)->modify('-1 day');
case 'nextday': return (clone $this)->modify('+1 day');
case 'prev_day': return (clone $this)->modify('-1 day');
case 'next_day': return (clone $this)->modify('+1 day');
case 'prevweek': return (clone $this)->modify('-7 days');
case 'nextweek': return (clone $this)->modify('+7 days');
case 'prev_week': return (clone $this)->modify('-7 days');
case 'next_week': return (clone $this)->modify('+7 days');
case 'prevmonth': return (clone $this)->modify('-1 month');
case 'nextmonth': return (clone $this)->modify('+1 month');
case 'prev_month': return (clone $this)->modify('-1 month');
case 'next_month': return (clone $this)->modify('+1 month');
case 'prevyear': return (clone $this)->modify('-1 year');
case 'nextyear': return (clone $this)->modify('+1 year');
case 'prev_year': return (clone $this)->modify('-1 year');
case 'next_year': return (clone $this)->modify('+1 year');
7 years ago
// strtolower($name) versions
case 'year': return $this->date->format('Y');
case 'month': return $this->date->format('m');
case 'day': return $this->date->format('d');
case 'hour': return $this->date->format('G'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour MySQL: Returns the hour for time. The range of the return value is 0 to 23 for time-of-day values. However, the range of TIME values actually is much larger, so HOUR can return values greater than 23.
case 'minute': return $this->date->format('i'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_minute MySQL: Returns the minute for time, in the range 0 to 59.
case 'second': return $this->date->format('s'); // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_hour MySQL: Returns the second for time, in the range 0 to 59.
}
7 years ago
7 years ago
throw new \InvalidArgumentException('Property ->' . $name . ' does not exist!');
}
7 years ago
7 years ago
function __set($name, $value)
{
switch ($name)
{
case 'year':
case 'month':
case 'day':
7 years ago
7 years ago
break;
7 years ago
7 years ago
case 'timestamp':
$this->date->setTimestamp($value);
break;
7 years ago
}
7 years ago
throw new \InvalidArgumentException('Property ->' . $name . ' cannot be set!');
7 years ago
}
7 years ago
7 years ago
public function getYear()
7 years ago
{
7 years ago
return $this->date->format('Y');
7 years ago
}
7 years ago
public function getMonth()
7 years ago
{
7 years ago
return $this->date->format('m');
7 years ago
}
7 years ago
public function getDay()
7 years ago
{
7 years ago
return $this->date->format('d');
7 years ago
}
7 years ago
public function getHour()
7 years ago
{
7 years ago
return $this->date->format('H');
}
7 years ago
7 years ago
public function getMinute()
{
return $this->date->format('i');
7 years ago
}
7 years ago
public function getSecond()
{
return $this->date->format('s');
}
public function getHours() // @alias getHour()
{
return $this->date->format('H');
}
public function getMinutes() // @alias getMinute()
{
return $this->date->format('i');
}
public function getSeconds() // @alias getSecond()
{
return $this->date->format('s');
}
/**
* 'D' == A textual representation of a day, three letters Mon through Sun
* 'l' == A full textual representation of the day of the week Sunday through Saturday
* 'N' == ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday)
* 'w' == Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
*/
public function getDayOfWeek($format = 'D')
{
return $this->date->format($format);
}
// CURDATE includes timezone, use UTC_DATE() for UTC date
public static function curdate()
{
return new static(date('Y-m-d'));
}
public static function utc_date()
{
return new static(gmdate('Y-m-d'));
}
public static function utcDate()
{
return new static(gmdate('Y-m-d'));
}
/**
* Create a Twister\DateTime instance from the current date and time.
*
* @param \DateTimeZone|string|null $tz
*
* @return static
*/
public static function now()
{
return new static(date('Y-m-d'));
}
7 years ago
/**
7 years ago
* Returns true if the string contains a date in the format 'YYYY-MM-DD' AND is a valid Gregorian date