Rev 1 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download
<?php namespace ProcessWire;/*** ProcessWire Date/Time Tools ($datetime API variable)** #pw-summary The $datetime API variable provides helpers for working with dates/times and conversion between formats.** ProcessWire 3.x, Copyright 2019 by Ryan Cramer* https://processwire.com** @method string relativeTimeStr($ts, $abbreviate = false, $useTense = true)**/class WireDateTime extends Wire {/*** Date formats in date() format**/static protected $dateFormats = array(// Gregorian little-endian, starting with day (used by most of world)'l, j F Y', // Monday, 1 April 2012'j F Y', // 1 April 2012'd-M-Y', // 01-Apr-2012'dMy', // 01Apr12'd/m/Y', // 01/04/2012'd.m.Y', // 01.04.2012'd/m/y', // 01/04/12'd.m.y', // 01.04.12'j/n/Y', // 1/4/2012'j.n.Y', // 1.4.2012'j/n/y', // 1/4/12'j.n.y', // 1.4.12// Gregorian big-endian, starting with year (used in Asian countries, Hungary, Sweden, and US armed forces)'Y-m-d', // 2012-04-01 (ISO-8601)'Y/m/d', // 2012/04/01 (ISO-8601)'Y.n.j', // 2012.4.1 (common in China)'Y/n/j', // 2012/4/1'Y F j', // 2012 April 1'Y-M-j, l', // 2012-Apr-1, Monday'Y-M-j', // 2012-Apr-1'YMj', // 2012Apr1// Middle-endian, starting with month (US and some Canada)'l, F j, Y', // Monday, April 1, 2012'F j, Y', // April 1, 2012'M j, Y', // Apr 1, 2012'm/d/Y', // 04/01/2012'm.d.Y', // 04.01.2012'm/d/y', // 04/01/12'm.d.y', // 04.01.12'n/j/Y', // 4/1/2012'n.j.Y', // 4.1.2012'n/j/y', // 4/1/12'n.j.y', // 4.1.12// Other//'%x', // 04/01/12 (locale based date)//'%c', // Tue Feb 5 00:45:10 2012 (locale based date/time)// 'U', // Unix Timestamp);static protected $timeFormats = array('g:i a', // 5:01 pm'h:i a', // 05:01 pm'g:i A', // 5:01 PM'h:i A', // 05:01 PM'H:i', // 17:01 (with leading zeros)'H:i:s', // 17:01:01 (with leading zeros)// Relative'!relative','!relative-','!rel','!rel-','!r','!r-',);/*** Date/time translation table from PHP date() to strftime() and JS/jQuery**/static protected $dateConversion = array(// date strftime js regex'l' => array( '%A', 'DD', '\w+'), // Name of day, long Monday'D' => array( '%a', 'D', '\w+'), // Name of day, short Mon'F' => array( '%B', 'MM', '\w+'), // Name of month, long April'M' => array( '%b', 'M', '\w+'), // Name of month, short Apr'j' => array( '%-d', 'd', '\d{1,2}'), // Day without leading zeros 1'd' => array( '%d', 'dd', '\d{2}'), // Day with leading zeros 01'n' => array( '%-m', 'm', '\d{1,2}'), // Month without leading zeros 4'm' => array( '%m', 'mm', '\d{2}'), // Month with leading zeros 04'y' => array( '%y', 'y', '\d{2}'), // Year 2 character 12'Y' => array( '%Y', 'yy', '\d{4}'), // Year 4 character 2012'N' => array( '%u', '', '[1-7]'), // Day of the week (1-7) 1'w' => array( '%w', '', '[0-6]'), // Zero-based day of week (0-6) 0'S' => array( '', '', '\w{2}'), // Day ordinal suffix st, nd, rd, th'z' => array( '%-j', 'o', '\d{1,3}'), // Day of the year (0-365) 123'W' => array( '%W', '', '\d{1,2}'), // Week # of the year 42't' => array( '', '', '(28|29|30|31)'), // Number of days in month 28'o' => array( '%V', '', '\d{1,2}'), // ISO-8601 week number 42'a' => array( '%P', 'tt', '[aApP][mM]'), // am or pm am'A' => array( '%p', 'TT', '[aApP][mM]'), // AM or PM AM'g' => array( '%-I', 'h', '\d{1,2}'), // 12-hour format, no leading zeros 5'h' => array( '%I', 'hh', '\d{2}'), // 12-hour format, leading zeros 05'G' => array( '%-H', 'h24', '\d{1,2}'), // 24-hour format, no leading zeros 5'H' => array( '%H', 'hh24', '\d{2}'), // 24-hour format, leading zeros 05'i' => array( '%M', 'mm', '[0-5][0-9]'), // Minutes 09's' => array( '%S', 'ss', '[0-5][0-9]'), // Seconds 59'e' => array( '', '', '\w+'), // Timezone Identifier UTC, GMT, Atlantic/Azores'I' => array( '', '', '[01]'), // Daylight savings time? 1=yes, 0=no'O' => array( '', '', '[-+]\d{4}'), // Difference to Greenwich time/hrs +0200'P' => array( '', '', '[-+]\d{2}:\d{2}'), // Same as above, but with colon +02:00'T' => array( '', '', '\w+'), // Timezone abbreviation MST, EST'Z' => array( '', '', '-?\d+'), // Timezone offset in seconds -43200 through 50400'c' => array( '', '', '[-+:T\d]{19,25}'), // ISO-8601 date 2004-02-12T15:19:21+00:00'r' => array( '', '', '\w+, \d+ \w+ \d{4}'), // RFC-2822 date Thu, 21 Dec 2000 16:01:07 +0200'U' => array( '%s', '@', '\d+'), // Unix timestamp 123344556);/*** Return all predefined PHP date() formats for use as dates** ~~~~~* // output all date formats* $formats = $datetime->getDateFormats();* echo "<pre>" . print_r($formats, true) . "</pre>";* ~~~~~** #pw-advanced** @return array Returns an array of strings containing recognized date formats**/public function getDateFormats() { return self::$dateFormats; }public static function _getDateFormats() { return self::$dateFormats; }/*** Return all predefined PHP date() formats for use as times** ~~~~~* // output all time formats* $formats = $datetime->getTimeFormats();* echo "<pre>" . print_r($formats, true) . "</pre>";* ~~~~~** #pw-advanced** @return array Returns an array of strings containing recognized time formats**/public function getTimeFormats() { return self::$timeFormats; }public static function _getTimeFormats() { return self::$timeFormats; }/*** Given a date/time string and expected format, convert it to a unix timestamp** @param string $str Date/time string* @param string $format Format of the date/time string in PHP date syntax* @return int Unix timestamp**/public function stringToTimestamp($str, $format) {if(empty($str)) return '';// already a timestampif(ctype_digit(ltrim("$str", '-'))) return (int) $str;$format = trim("$format");if(!strlen($format)) return strtotime($str);// use PHP 5.3's date parser if its availableif(function_exists('date_parse_from_format')) {// PHP 5.3+$a = date_parse_from_format($format, $str);if(isset($a['warnings']) && count($a['warnings'])) {foreach($a['warnings'] as $warning) {$this->warning($warning . " (value='$str', format='$format')");}}if($a['year'] && $a['month'] && $a['day']) {return mktime($a['hour'], $a['minute'], $a['second'], $a['month'], $a['day'], $a['year']);}}$regex = '!^' . $this->convertDateFormat($format, 'regex') . '$!';if(!preg_match($regex, $str, $m)) {// wire('pages')->message("'$format' - '$regex' - '$str'");// if we can't match it, then just send it to strtotimereturn strtotime($str);}$s = '';// monthif(isset($m['n'])) $s .= $m['n'] . '/';else if(isset($m['m'])) $s .= $m['m'] . '/';else if(isset($m['F'])) $s .= $m['F'] . ' ';else if(isset($m['M'])) $s .= $m['M'] . ' ';else return strtotime($str);// separator character$c = substr($s, -1);// dayif(isset($m['j'])) $s .= $m['j'] . $c;else if(isset($m['d'])) $s .= $m['d'] . $c;else $s .= '1' . $c;// yearif(isset($m['y'])) $s .= $m['y'];else if(isset($m['Y'])) $s .= $m['Y'];else $s .= date('Y');$s .= ' ';$useTime = true;// hourif(isset($m['g'])) $s .= $m['g'] . ':';else if(isset($m['h'])) $s .= $m['h'] . ':';else if(isset($m['G'])) $s .= $m['G'] . ':';else if(isset($m['H'])) $s .= $m['H'] . ':';else $useTime = false;// timeif($useTime) {// minuteif(isset($m['i'])) {$s .= $m['i'];// secondif(isset($m['s'])) $s .= ':' . $m['s'];}// am/pmif(isset($m['a'])) $s .= ' ' . $m['a'];else if(isset($m['A'])) $s .= ' ' . $m['A'];}return strtotime($s);}/*** Format a date with the given PHP date() or PHP strftime() format** This method will accept either [PHP date](http://php.net/manual/en/function.date.php)* or [PHP strftime](http://php.net/manual/en/function.strftime.php) compatible formats,* determine what kind it is, and return the timestamp formatted with it.** ~~~~~~* // Output the current date/time in ISO-8601 like format* echo $datetime->formatDate(time(), 'Y-m-d H:i a');* ~~~~~~** #pw-internal** @param int $value Unix timestamp of date/time* @param string $format PHP date() or strftime() format string to use for formatting* @return string Formatted date string**/public function formatDate($value, $format) {if(!$value) return '';if(!strlen("$format") || $format === 'U' || $format === '%s') return (int) $value; // unix timestamp$relativeStr = '';if(strpos($format, '!') !== false) {if(preg_match('/([!][relativ]+-?)/', $format, $matches)) {$relativeStr = $this->date(ltrim($matches[1], '!'), $value);$format = str_replace($matches[1], '///', $format);}}if(strpos($format, '%') !== false) {// use strftime() if format string contains a %if(strpos($format, '%-') !== false) {// not all systems support the '%-' option in strftime to trim leading zeros// so we are doing our own implementation here$TRIM0 = true;$format = str_replace('%-', 'TRIM0%', $format);} else {$TRIM0 = false;}$value = $this->strftime($format, $value);if($TRIM0) $value = str_replace(array('TRIM00', 'TRIM0'), '', $value);} else {$value = $this->date($format, $value);}if(strlen($relativeStr)) $value = str_replace('///', $relativeStr, $value);return $value;}/*** Given a PHP date() format, convert it to either 'js', 'strftime' or 'regex' format** #pw-advanced** @param string $format PHP date() format* @param string $type New format to convert to: either 'js', 'strftime', or 'regex'* @return string**/public function convertDateFormat($format, $type) {$newFormat = '';$lastc = '';for($n = 0; $n < strlen($format); $n++) {$c = $format[$n];if($c == '\\') {// begin escaped character$lastc = $c;continue;}if($lastc == '\\') {// literal character, not translated$lastc = $c;$newFormat .= $c;continue;}if(!isset(self::$dateConversion[$c])) {// unknown characterif($type == 'regex' && in_array($c, array('.', '[', ']', '(', ')', '*', '+', '?'))) {$c = '\\' . $c; // escape regex chars}$newFormat .= $c;continue;}list($strftime, $js, $regex) = self::$dateConversion[$c];if($type == 'js') {$newFormat .= $js;} else if($type == 'strftime') {$newFormat .= $strftime;} else if($type == 'regex') {$newFormat .= '\b(?<' . $c . '>' . $regex . ')\b'; // regex captured with name of date() format char} else {$newFormat .= $c;}$lastc = $c;}return $newFormat;}/*** Format a date, using PHP date(), strftime() or other special strings (see arguments).** This is designed to work the same way as PHP's `date()` but be able to accept any common format* used in ProcessWire. This is helpful for reducing code in places where you might have logic* determining when to use `date()`, `strftime()`, `wireRelativeTimeStr()` or some other date* formatting function.** ~~~~~~* // Output the current date/time in relative format* echo $datetime->date('relative');* ~~~~~~** @param string|int $format Use one of the following:* - [PHP date](http://php.net/manual/en/function.date.php) format* - [PHP strftime](http://php.net/manual/en/function.strftime.php) format (detected by presence of a '%' somewhere in it)* - `relative` for a relative date/time string.* - `relative-` for a relative date/time string with no tense.* - `rel` for an abbreviated relative date/time string.* - `rel-` for an abbreviated relative date/time string with no tense.* - `r` for an extra-abbreviated relative date/time string.* - `r-` for an extra-abbreviated relative date/time string with no tense.* - `ts` makes it return a unix timestamp* - blank string makes it use the system date format ($config->dateFormat)* - If given an integer and no second argument specified, it is assumed to be the second ($ts) argument.* @param int|string|null $ts Optionally specify the date/time stamp or strtotime() compatible string. If not specified, current time is used.* @return string|bool Formatted date/time, or boolean false on failure**/function date($format = '', $ts = null) {if(is_null($ts)) {// ts not specified, or it was specified in $formatif(ctype_digit("$format")) {// ts specified in format$ts = (int) $format;$format = '';} else {// use current timestamp$ts = time();}} else if(is_string($ts) && ctype_digit("$ts")) {// ts is a digit string, convert to integer$ts = (int) $ts;} else if(is_string($ts)) {// ts is a non-integer string, we assume to be a strtotime() compatible formatted date$ts = strtotime($ts);}if($format == '') $format = $this->wire('config')->dateFormat;if($format == 'relative') $value = $this->relativeTimeStr($ts);else if($format == 'relative-') $value = $this->relativeTimeStr($ts, false, false);else if($format == 'rel') $value = $this->relativeTimeStr($ts, true);else if($format == 'rel-') $value = $this->relativeTimeStr($ts, true, false);else if($format == 'r') $value = $this->relativeTimeStr($ts, 1);else if($format == 'r-') $value = $this->relativeTimeStr($ts, 1, false);else if($format == 'ts') $value = $ts;else if(strpos($format, '%') !== false && version_compare(PHP_VERSION, '8.1.0', '<')) $value = $this->strftime($format, $ts);else $value = date($format, $ts);return $value;}/*** Parse about any English textual datetime description into a Unix timestamp using PHP’s strtotime()** This function behaves the same as PHP’s version except that it optionally accepts an `$options` array* and lets you specify the return value for empty or zeroed dates like 0000-00-00. If given a zerod date* then it returns null by default (rather than throwing an error as PHP8 does).** @param string $str Date/time string* @param array|int $options Options to modify behavior, or specify int for the `baseTimestamp` option.* - `emptyReturnValue` (int|null|false): Value to return for empty or zero-only date strings (default=null)* - `baseTimestamp` (int|null): The timestamp which is used as a base for the calculation of relative dates.* @return false|int|null* @see https://www.php.net/manual/en/function.strtotime.php* @since 3.0.178**/function strtotime($str, $options = array()) {$defaults = array('emptyReturnValue' => null,'baseTimestamp' => null,);if(is_int($options)) $defaults['baseTimestamp'] = $options;$options = is_array($options) ? array_merge($defaults, $options) : $defaults;$str = trim($str);if(empty($str)) return $options['emptyReturnValue'];if(strpos($str, '00') === 0) {$test = trim(preg_replace('/[^\d]/', '', $str), '0');if(!strlen($test)) return $options['emptyReturnValue'];}if($options['baseTimestamp'] === null) return strtotime($str);return strtotime($str, $options['baseTimestamp']) ;}/*** strftime() replacement function that works in PHP 8.1+ (though not locale aware)** @param string $format* @param null|int|string $timestamp* @return string|false* @since 3.0.197**/public function strftime($format, $timestamp = null) {$format = (string) $format;if(is_string($timestamp)) {if(empty($timestamp)) {$timestamp = null;} else if(ctype_digit($timestamp)) {$timestamp = (int) $timestamp;} else {$timestamp = $this->strtotime($timestamp);}}if($timestamp === null) $timestamp = time();if(version_compare(PHP_VERSION, '8.1.0', '<')) {return strftime($format, $timestamp);}$format = $this->strftimeToDateFormat($format);return date($format, $timestamp);}/*** Convert strftime() format to date() format** @param string $format* @return string* @since 3.0.197**/protected function strftimeToDateFormat($format) {// replacements, in addition to those specified in self::$dateConversion// strftime format => date format$replacements = array('%e' => 'j', // Day of the month without leading zeros'%j' => 'z', // Day of the year, 3 digits with leading zeros'%U' => '_', // Week number of the given year, starting with the first Sunday as the first week (not implemented)'%h' => 'M', // Abbreviated month name'%C' => '_', // Two digit representation of the century (year divided by 100, truncated to an integer) (not implemented)'%g' => 'y', // Two digit representation of the year going by ISO-8601:1988 standards (see %V)'%G' => 'Y', // 4 digit year'%k' => 'G', // Hour in 24-hour format'%l' => 'g', // Hour in 12-hour format'%r' => 'h:i:s A', // Example: 09:34:17 PM'%R' => 'G:i', // Example: 00:35 for 12:35 AM'%T' => 'G:i:s', // Example: 21:34:17 for 09:34:17 PM'%X' => 'G:i:s', // Preferred time representation based on locale, without the date, Example: 03:59:16 or 15:59:16'%Z' => 'T', // The time zone abbreviation. Example: EST for Eastern Time'%c' => 'Y-m-d H:i:s', // Preferred date and time stamp based on locale'%D' => 'm/d/y', // Example: 02/05/09 for February 5, 2009'%F' => 'Y-m-d', // Example: 2009-02-05 for February 5, 2009'%n' => '\\n', // newline'%t' => '\\t', // tab'%%' => '%', // literal percent);foreach(self::$dateConversion as $dateFormat => $formats) {$strftimeFormat = $formats[0];if(empty($strftimeFormat)) continue;if(strpos($format, $strftimeFormat) !== false) $replacements[$strftimeFormat] = $dateFormat;}return strtr($format, $replacements);}/*** Given a unix timestamp (or date string), returns a formatted string indicating the time relative to now** For example:** - 2 years ago* - 3 months ago* - 1 day ago* - 30 seconds ago* - Just now* - 1 day from now* - 5 months from now* - 3 years from now** This method also supports multi-language and will output in the current user's language, so long as the* phrases in /wire/core/WireDateTime.php are translated in the language pack.** @param int|string $ts Unix timestamp or date string* @param bool|int|array $abbreviate Whether to use abbreviations for shorter strings.* - Specify boolean TRUE for abbreviations (abbreviated where common, not always different from non-abbreviated)* - Specify integer 1 for extra short abbreviations (all terms abbreviated into shortest possible string)* - Specify boolean FALSE or omit for no abbreviations.* - Specify associative array of key=value pairs of terms to use for abbreviations. The possible keys are:* just now, ago, from now, never, second, minute, hour, day, week, month, year, decade, seconds, minutes,* hours, days, weeks, months, years, decades* @param bool $useTense Whether to append a tense like "ago" or "from now".* - May be ok to disable in situations where all times are assumed in future or past.* - In abbreviate=1 (shortest) mode, this removes the leading "+" or "-" from the string.* @return string Formatted relative time string**/public function ___relativeTimeStr($ts, $abbreviate = false, $useTense = true) {// Originally based upon: <http://www.php.net/manual/en/function.time.php#89415>if(empty($ts)) {if(is_array($abbreviate) && isset($abbreviate['never'])) return $abbreviate['never'];return $this->_('Never');}$justNow = $this->_('just now');$ago = $this->_('ago');$prependAgo = '';$fromNow = $this->_('from now');$prependFromNow = '';$space = ' ';if($abbreviate === 1) {// extra short abbreviations$justNow = $this->_('now');$ago = '';$prependAgo = '-';$fromNow = '';$prependFromNow = '+';$space = '';} else if($abbreviate === true) {// standard abbreviations$justNow = $this->_('now');$fromNow = '';$prependFromNow = $this->_('in') . ' ';} else if(is_array($abbreviate)) {// user substitutionsif(isset($abbreviate['just now'])) $justNow = $abbreviate['just now'];if(isset($abbreviate['from now'])) $fromNow = $abbreviate['from now'];if(isset($abbreviate['ago'])) $ago = $abbreviate['ago'];}$lengths = array("60","60","24","7","4.35","12","10");$now = time();if(!ctype_digit("$ts")) $ts = strtotime($ts);if(empty($ts)) return "";// is it future date or past dateif($now > $ts) {$difference = $now - $ts;$tense = $ago;$prepend = $prependAgo;} else {$difference = $ts - $now;$tense = $fromNow;$prepend = $prependFromNow;}if(!$useTense) {$prepend = '';$tense = '';}for($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) {$difference /= $lengths[$j];}$difference = round($difference);if(!$difference) return $justNow;$periods = $difference != 1 ? $this->getPeriods($abbreviate, true) : $this->getPeriods($abbreviate, false);$period = $periods[$j];// return sprintf('%s%d%s%s %s', $prepend, (int) $difference, $space, $period, $tense); // i.e. 2 days ago (d=qty, 2=period, 3=tense)$quantity = $prepend . $difference . $space;$format = $this->_('Q P T'); // Relative time order: Q=Quantity, P=Period, T=Tense (i.e. 2 Days Ago)$format = str_replace(array('Q', 'P', 'T'), array('{Q}', '{P}', '{T}'), $format);$out = str_replace(array('{Q}', '{P}', '{T}'), array(" $quantity", " $period", " $tense"), $format);if($abbreviate === 1) {$out = str_replace(" ", "", $out); // no space when extra-abbreviate is active} else if(strpos($out, ' ') !== false) {$out = preg_replace('/\s\s+/', ' ', $out);}return trim($out);}/*** Render an elapsed time string** @param int|string $start Starting timestamp or date/time string.* @param int|string $stop Ending timestamp or date/time string, or omit for now.* @param bool|int|array $abbreviate* - Specify boolean FALSE for verbose elapsed time string without abbreviations (default).* - Specify boolean TRUE for abbreviations (abbreviated where common, not always different from non-abbreviated).* - Specify integer 1 for extra short abbreviations (all terms abbreviated into shortest possible string).* - Specify integer 0 for digital elapsed time string like “00:01:12” referring to hours:minutes:seconds.* @param array $options Additional options:* - `delimiter` (string): String to separate time periods (default=' ').* - `exclude` (array|string): Exclude these periods, one or more of: 'seconds', 'minutes', 'hours', 'days', 'weeks' (default=[])* @return string* @since 3.0.129**/public function elapsedTimeStr($start, $stop = null, $abbreviate = false, array $options = array()) {$defaults = array('delimiter' => ' ','exclude' => array(),);$options = array_merge($defaults, $options);if(is_string($options['exclude'])) $options['exclude'] = explode(' ', $options['exclude']);if($stop === null) $stop = time();if(!ctype_digit("$start")) $start = strtotime($start);if(!ctype_digit("$stop")) $stop = strtotime($stop);$times = array();$seconds = $stop - $start;if($seconds >= 604800 && $abbreviate !== 0 && !in_array('weeks', $options['exclude'])) {$weeks = floor($seconds / 604800);$seconds = $seconds - ($weeks * 604800);$key = $weeks === 1 ? 'week' : 'weeks';$times[$key] = $weeks;}if($seconds >= 86400 && $abbreviate !== 0 && !in_array('days', $options['exclude'])) {$days = floor($seconds / 86400);$seconds = $seconds - ($days * 86400);$key = $days === 1 ? 'day' : 'days';$times[$key] = $days;}if($seconds >= 3600 && !in_array('hours', $options['exclude'])) {$hours = floor($seconds / 3600);$seconds = $seconds - ($hours * 3600);$key = $hours === 1 ? 'hour' : 'hours';$times[$key] = $hours;} else {$hours = 0;}if($seconds >= 60 && !in_array('minutes', $options['exclude'])) {$minutes = floor($seconds / 60);$seconds = $seconds - ($minutes * 60);$key = $minutes === 1 ? 'minute' : 'minutes';$times[$key] = $minutes;} else {$minutes = 0;}if(($seconds > 0 || empty($times)) && !in_array('seconds', $options['exclude'])) {$key = $seconds === 1 ? 'second' : 'seconds';$times[$key] = $seconds;} else {$seconds = 0;}if($abbreviate === 0) {if(strlen($hours) < 2) $hours = "0$hours";if(strlen($minutes) < 2) $minutes = "0$minutes";if(strlen($seconds) < 2) $seconds = "0$seconds";$str = "$hours:$minutes:$seconds";} else {$periods = $this->getPeriods($abbreviate);$a = array();foreach($times as $key => $qty) {$sep = $abbreviate === 1 ? '' : ' ';$a[] = $qty . $sep . $periods[$key];}$str = implode($options['delimiter'], $a);}return $str;}/*** Get named time periods** Returns regular array(s) of periods in this order:* seconds, minutes, hours, days, weeks, months, years decades** If $plural argument is null (or omitted) it instead returns an array* indexed by period name including both singular and plural periods.** @param $abbreviate* - Specify 1 to get shortest possible abbreviations* - Specify true to get standard/medium abbreviations* - Specify false to get large/full terms (no abbreviations)* - Specify associative array to get large/full terms and substitute your own* @param null|true|int $plural* - Specify true to get plural,* - Specify false to get singular,* - Specify 1 to get array where [ 0 => [singulars], 1 => [plurals] ]* - Omit (or null) to get all in an indexed array* @return array**/protected function getPeriods($abbreviate, $plural = null) {static $definitions = array();if(empty($definitions)) $definitions = array('keys-singular' => array('second','minute','hour','day','week','month','year','decade'),'keys-plural' => array('seconds','minutes','hours','days','weeks','months','years','decades'),'short-singular' => array($this->_("s"),$this->_("m"),$this->_("hr"),$this->_("d"),$this->_("wk"),$this->_("mon"),$this->_("yr"),$this->_("decade")),'short-plural' => array($this->_("s"),$this->_("m"),$this->_("hr"),$this->_("d"),$this->_("wks"),$this->_("mths"),$this->_("yrs"),$this->_("decades")),'medium-singular' => array($this->_("sec"),$this->_("min"),$this->_("hr"),$this->_("day"),$this->_("week"),$this->_("month"),$this->_("year"),$this->_("decade")),'medium-plural' => array($this->_("secs"),$this->_("mins"),$this->_("hrs"),$this->_("days"),$this->_("weeks"),$this->_("months"),$this->_("years"),$this->_("decades")),'large-singular' => array($this->_("second"),$this->_("minute"),$this->_("hour"),$this->_("day"),$this->_("week"),$this->_("month"),$this->_("year"),$this->_("decade")),'large-plural' => array($this->_("seconds"),$this->_("minutes"),$this->_("hours"),$this->_("days"),$this->_("weeks"),$this->_("months"),$this->_("years"),$this->_("decades")),);if($abbreviate === 1) {// extra short abbreviations$periodsPlural = $definitions['short-plural'];$periodsSingular = $definitions['short-singular'];} else if($abbreviate === true) {// standard abbreviations$periodsPlural = $definitions['medium-plural'];$periodsSingular = $definitions['medium-singular'];} else {// no abbreviations$periodsPlural = $definitions['large-plural'];$periodsSingular = $definitions['large-singular'];// merge in any user-supplied abbreviationsif(is_array($abbreviate)) {foreach($definitions['keys-plural'] as $key => $term) {if(isset($abbreviate[$term])) $periodsPlural[$key] = $abbreviate[$term];}foreach($definitions['keys-singular'] as $key => $term) {if(isset($abbreviate[$term])) $periodsSingular[$key] = $abbreviate[$term];}}}if($plural === true) return $periodsPlural;if($plural === false) return $periodsSingular;// get all indexed by term$periods = array();foreach($definitions['keys-plural'] as $key => $term) {$periods[$term] = $periodsPlural[$key];}foreach($definitions['keys-singular'] as $key => $term) {$periods[$term] = $periodsSingular[$key];}return $periods;}}