You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
430 lines
12 KiB
430 lines
12 KiB
<?php
|
|
/**
|
|
* Part of the Fuel framework.
|
|
*
|
|
* @package Fuel
|
|
* @version 1.8
|
|
* @author Fuel Development Team
|
|
* @license MIT License
|
|
* @copyright 2010 - 2016 Fuel Development Team
|
|
* @link http://fuelphp.com
|
|
*/
|
|
|
|
namespace Fuel\Core;
|
|
|
|
/**
|
|
* Date Class
|
|
*
|
|
* DateTime replacement that supports internationalization and does correction to GMT
|
|
* when your webserver isn't configured correctly.
|
|
*
|
|
* @package Fuel
|
|
* @subpackage Core
|
|
* @category Core
|
|
* @link http://docs.fuelphp.com/classes/date.html
|
|
*
|
|
* Notes:
|
|
* - Always returns Date objects, will accept both Date objects and UNIX timestamps
|
|
* - create_time() uses strptime and has currently a very bad hack to use strtotime for windows servers
|
|
* - Uses strftime formatting for dates www.php.net/manual/en/function.strftime.php
|
|
*/
|
|
class Date
|
|
{
|
|
/**
|
|
* Time constants (and only those that are constant, thus not MONTH/YEAR)
|
|
*/
|
|
const WEEK = 604800;
|
|
const DAY = 86400;
|
|
const HOUR = 3600;
|
|
const MINUTE = 60;
|
|
|
|
/**
|
|
* @var int server's time() offset from gmt in seconds
|
|
*/
|
|
protected static $server_gmt_offset = 0;
|
|
|
|
/**
|
|
* @var string the timezone to be used to output formatted data
|
|
*/
|
|
public static $display_timezone = null;
|
|
|
|
public static function _init()
|
|
{
|
|
static::$server_gmt_offset = \Config::get('server_gmt_offset', 0);
|
|
|
|
static::$display_timezone = \Config::get('default_timezone') ?: date_default_timezone_get();
|
|
|
|
// Ugly temporary windows fix because windows doesn't support strptime()
|
|
// It attempts conversion between glibc style formats and PHP's internal style format (no 100% match!)
|
|
if ( ! function_exists('strptime') && ! function_exists('Fuel\Core\strptime'))
|
|
{
|
|
function strptime($input, $format)
|
|
{
|
|
// convert the format string from glibc to date format (where possible)
|
|
$new_format = str_replace(
|
|
array('%a', '%A', '%d', '%e', '%j', '%u', '%w', '%U', '%V', '%W', '%b', '%B', '%h', '%m', '%C', '%g', '%G', '%y', '%Y', '%H', '%k', '%I', '%l', '%M', '%p', '%P', '%r', '%R', '%S', '%T', '%X', '%z', '%Z', '%c', '%D', '%F', '%s', '%x', '%n', '%t', '%%'),
|
|
array('D', 'l', 'd', 'j', 'N', 'z', 'w', '[^^]', 'W', '[^^]', 'M', 'F', 'M', 'm', '[^^]', 'Y', 'o', 'y', 'Y', 'H', 'G', 'h', 'g', 'i', 'A', 'a', 'H:i:s A', 'H:i', 's', 'H:i:s', '[^^]', 'O', 'T ', '[^^]', 'm/d/Y', 'Y-m-d', 'U', '[^^]', "\n", "\t", '%'),
|
|
$format
|
|
);
|
|
|
|
// parse the input
|
|
$parsed = date_parse_from_format($new_format, $input);
|
|
|
|
// parse succesful?
|
|
if (is_array($parsed) and empty($parsed['errors']))
|
|
{
|
|
return array(
|
|
'tm_year' => $parsed['year'] - 1900,
|
|
'tm_mon' => $parsed['month'] - 1,
|
|
'tm_mday' => $parsed['day'],
|
|
'tm_hour' => $parsed['hour'] ?: 0,
|
|
'tm_min' => $parsed['minute'] ?: 0,
|
|
'tm_sec' => $parsed['second'] ?: 0,
|
|
);
|
|
}
|
|
else
|
|
{
|
|
$masks = array(
|
|
'%d' => '(?P<d>[0-9]{2})',
|
|
'%m' => '(?P<m>[0-9]{2})',
|
|
'%Y' => '(?P<Y>[0-9]{4})',
|
|
'%H' => '(?P<H>[0-9]{2})',
|
|
'%M' => '(?P<M>[0-9]{2})',
|
|
'%S' => '(?P<S>[0-9]{2})',
|
|
);
|
|
|
|
$rexep = "#" . strtr(preg_quote($format), $masks) . "#";
|
|
|
|
if ( ! preg_match($rexep, $input, $result))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return array(
|
|
"tm_sec" => isset($result['S']) ? (int) $result['S'] : 0,
|
|
"tm_min" => isset($result['M']) ? (int) $result['M'] : 0,
|
|
"tm_hour" => isset($result['H']) ? (int) $result['H'] : 0,
|
|
"tm_mday" => isset($result['d']) ? (int) $result['d'] : 0,
|
|
"tm_mon" => isset($result['m']) ? ($result['m'] ? $result['m'] - 1 : 0) : 0,
|
|
"tm_year" => isset($result['Y']) ? ($result['Y'] > 1900 ? $result['Y'] - 1900 : 0) : 0,
|
|
);
|
|
}
|
|
}
|
|
|
|
// This really is some fugly code, but someone at PHP HQ decided strptime should
|
|
// output this awful array instead of a timestamp LIKE EVERYONE ELSE DOES!!!
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create Date object from timestamp, timezone is optional
|
|
*
|
|
* @param int $timestamp UNIX timestamp from current server
|
|
* @param string $timezone valid PHP timezone from www.php.net/timezones
|
|
* @return Date
|
|
*/
|
|
public static function forge($timestamp = null, $timezone = null)
|
|
{
|
|
return new static($timestamp, $timezone);
|
|
}
|
|
|
|
/**
|
|
* Returns the current time with offset
|
|
*
|
|
* @param string $timezone valid PHP timezone from www.php.net/timezones
|
|
* @return Date
|
|
*/
|
|
public static function time($timezone = null)
|
|
{
|
|
return static::forge(null, $timezone);
|
|
}
|
|
|
|
/**
|
|
* Returns the current time with offset
|
|
*
|
|
* @param string $timezone valid PHP timezone from www.php.net/timezones
|
|
* @return string
|
|
*/
|
|
public static function display_timezone($timezone = null)
|
|
{
|
|
is_string($timezone) and static::$display_timezone = $timezone;
|
|
|
|
return static::$display_timezone;
|
|
}
|
|
|
|
/**
|
|
* Uses the date config file to translate string input to timestamp
|
|
*
|
|
* @param string $input date/time input
|
|
* @param string $pattern_key key name of pattern in config file
|
|
* @return Date
|
|
*/
|
|
public static function create_from_string($input, $pattern_key = 'local')
|
|
{
|
|
\Config::load('date', 'date');
|
|
|
|
$pattern = \Config::get('date.patterns.'.$pattern_key, null);
|
|
empty($pattern) and $pattern = $pattern_key;
|
|
|
|
$time = strptime($input, $pattern);
|
|
if ($time === false)
|
|
{
|
|
throw new \UnexpectedValueException('Input was not recognized by pattern.');
|
|
}
|
|
|
|
// convert it into a timestamp
|
|
$timestamp = mktime($time['tm_hour'], $time['tm_min'], $time['tm_sec'],
|
|
$time['tm_mon'] + 1, $time['tm_mday'], $time['tm_year'] + 1900);
|
|
|
|
if ($timestamp === false)
|
|
{
|
|
throw new \OutOfBoundsException('Input was invalid.'.(PHP_INT_SIZE == 4 ? ' A 32-bit system only supports dates between 1901 and 2038.' : ''));
|
|
}
|
|
|
|
return static::forge($timestamp);
|
|
}
|
|
|
|
/**
|
|
* Fetches an array of Date objects per interval within a range
|
|
*
|
|
* @param int|Date $start start of the range
|
|
* @param int|Date $end end of the range
|
|
* @param int|string $interval Length of the interval in seconds or valid strtotime time difference
|
|
* @return array array of Date objects
|
|
*/
|
|
public static function range_to_array($start, $end, $interval = '+1 Day')
|
|
{
|
|
// make sure start and end are date objects
|
|
$start = ( ! $start instanceof Date) ? static::forge($start) : $start;
|
|
$end = ( ! $end instanceof Date) ? static::forge($end) : $end;
|
|
|
|
$range = array();
|
|
|
|
// if end > start, the range is empty
|
|
if ($end->get_timestamp() >= $start->get_timestamp())
|
|
{
|
|
$current = $start;
|
|
$increment = $interval;
|
|
|
|
do
|
|
{
|
|
$range[] = $current;
|
|
|
|
if ( ! is_int($interval))
|
|
{
|
|
$increment = strtotime($interval, $current->get_timestamp()) - $current->get_timestamp();
|
|
if ($increment <= 0)
|
|
{
|
|
throw new \UnexpectedValueException('Input was not recognized by pattern.');
|
|
}
|
|
}
|
|
|
|
$current = static::forge($current->get_timestamp() + $increment);
|
|
}
|
|
while ($current->get_timestamp() <= $end->get_timestamp());
|
|
}
|
|
|
|
return $range;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of days in the requested month
|
|
*
|
|
* @param int $month month as a number (1-12)
|
|
* @param int $year the year, leave empty for current
|
|
* @return int the number of days in the month
|
|
*/
|
|
public static function days_in_month($month, $year = null)
|
|
{
|
|
$year = ! empty($year) ? (int) $year : (int) date('Y');
|
|
$month = (int) $month;
|
|
|
|
if ($month < 1 or $month > 12)
|
|
{
|
|
throw new \UnexpectedValueException('Invalid input for month given.');
|
|
}
|
|
elseif ($month == 2)
|
|
{
|
|
if ($year % 400 == 0 or ($year % 4 == 0 and $year % 100 != 0))
|
|
{
|
|
return 29;
|
|
}
|
|
}
|
|
|
|
$days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
|
return $days_in_month[$month-1];
|
|
}
|
|
|
|
/**
|
|
* Returns the time ago
|
|
*
|
|
* @param int $timestamp UNIX timestamp from current server
|
|
* @param int $from_timestamp UNIX timestamp to compare against. Default to the current time
|
|
* @param string $unit Unit to return the result in
|
|
* @return string Time ago
|
|
*/
|
|
public static function time_ago($timestamp, $from_timestamp = null, $unit = null)
|
|
{
|
|
if ($timestamp === null)
|
|
{
|
|
return '';
|
|
}
|
|
|
|
! is_numeric($timestamp) and $timestamp = static::create_from_string($timestamp)->get_timestamp();
|
|
|
|
$from_timestamp == null and $from_timestamp = time();
|
|
|
|
\Lang::load('date', true);
|
|
|
|
$difference = $from_timestamp - $timestamp;
|
|
$periods = array('second', 'minute', 'hour', 'day', 'week', 'month', 'year', 'decade');
|
|
$lengths = array(60, 60, 24, 7, 4.35, 12, 10);
|
|
|
|
for ($j = 0; isset($lengths[$j]) and $difference >= $lengths[$j] and (empty($unit) or $unit != $periods[$j]); $j++)
|
|
{
|
|
$difference /= $lengths[$j];
|
|
}
|
|
|
|
$difference = round($difference);
|
|
|
|
if ($difference != 1)
|
|
{
|
|
$periods[$j] = \Inflector::pluralize($periods[$j]);
|
|
}
|
|
|
|
$text = \Lang::get('date.text', array(
|
|
'time' => \Lang::get('date.'.$periods[$j], array('t' => $difference)),
|
|
));
|
|
|
|
return $text;
|
|
}
|
|
|
|
/**
|
|
* @var int instance timestamp
|
|
*/
|
|
protected $timestamp;
|
|
|
|
/**
|
|
* @var string output timezone
|
|
*/
|
|
protected $timezone;
|
|
|
|
public function __construct($timestamp = null, $timezone = null)
|
|
{
|
|
is_null($timestamp) and $timestamp = time() + static::$server_gmt_offset;
|
|
! $timezone and $timezone = \Fuel::$timezone;
|
|
|
|
$this->timestamp = $timestamp;
|
|
$this->set_timezone($timezone);
|
|
}
|
|
|
|
/**
|
|
* Returns the date formatted according to the current locale
|
|
*
|
|
* @param string $pattern_key either a named pattern from date config file or a pattern, defaults to 'local'
|
|
* @param mixed $timezone vald timezone, or if true, output the time in local time instead of system time
|
|
* @return string
|
|
*/
|
|
public function format($pattern_key = 'local', $timezone = null)
|
|
{
|
|
\Config::load('date', 'date');
|
|
|
|
$pattern = \Config::get('date.patterns.'.$pattern_key, $pattern_key);
|
|
|
|
// determine the timezone to switch to
|
|
$timezone === true and $timezone = static::$display_timezone;
|
|
is_string($timezone) or $timezone = $this->timezone;
|
|
|
|
// Temporarily change timezone when different from default
|
|
if (\Fuel::$timezone != $timezone)
|
|
{
|
|
date_default_timezone_set($timezone);
|
|
}
|
|
|
|
// Create output
|
|
$output = strftime($pattern, $this->timestamp);
|
|
|
|
// Change timezone back to default if changed previously
|
|
if (\Fuel::$timezone != $timezone)
|
|
{
|
|
date_default_timezone_set(\Fuel::$timezone);
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Returns the internal timestamp
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_timestamp()
|
|
{
|
|
return $this->timestamp;
|
|
}
|
|
|
|
/**
|
|
* Returns the internal timezone
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_timezone()
|
|
{
|
|
return $this->timezone;
|
|
}
|
|
|
|
/**
|
|
* Returns the internal timezone or the display timezone abbreviation
|
|
*
|
|
* @param boolean $display_timezone
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_timezone_abbr($display_timezone = false)
|
|
{
|
|
// determine the timezone to switch to
|
|
$display_timezone and $timezone = static::$display_timezone;
|
|
empty($timezone) and $timezone = $this->timezone;
|
|
|
|
// Temporarily change timezone when different from default
|
|
if (\Fuel::$timezone != $timezone)
|
|
{
|
|
date_default_timezone_set($timezone);
|
|
}
|
|
|
|
// Create output
|
|
$output = date('T');
|
|
|
|
// Change timezone back to default if changed previously
|
|
if (\Fuel::$timezone != $timezone)
|
|
{
|
|
date_default_timezone_set(\Fuel::$timezone);
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Change the timezone
|
|
*
|
|
* @param string $timezone timezone from www.php.net/timezones
|
|
* @return Date
|
|
*/
|
|
public function set_timezone($timezone)
|
|
{
|
|
$this->timezone = $timezone;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Allows you to just put the object in a string and get it inserted in the default pattern
|
|
*
|
|
* @return string
|
|
*/
|
|
public function __toString()
|
|
{
|
|
return $this->format();
|
|
}
|
|
}
|