'Datetime', 'version' => 103, 'summary' => 'Field that stores a date and optionally time', ); } /** * 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) ); /** * 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 * */ static public function getDateFormats() { return self::$dateFormats; } /** * Return all predefined PHP date() formats for use as times * */ static public 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 * */ static public function stringToTimestamp($str, $format) { if(empty($str)) return ''; // already a timestamp if(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 available if(function_exists('date_parse_from_format')) { // PHP 5.3+ $a = date_parse_from_format($format, $str); if($a['year'] && $a['month'] && $a['day']) { return mktime($a['hour'], $a['minute'], $a['second'], $a['month'], $a['day'], $a['year']); } } $regex = '!^' . self::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 strtotime return strtotime($str); } $s = ''; // month if(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); // day if(isset($m['j'])) $s .= $m['j'] . $c; else if(isset($m['d'])) $s .= $m['d'] . $c; else $s .= '1' . $c; // year if(isset($m['y'])) $s .= $m['y']; else if(isset($m['Y'])) $s .= $m['Y']; else $s .= date('Y'); $s .= ' '; $useTime = true; // hour if(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; // time if($useTime) { // minute if(isset($m['i'])) { $s .= $m['i']; // second if(isset($m['s'])) $s .= ':' . $m['s']; } // am/pm if(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 * * @param int $value Unix timestamp of date * @param string $format date() or strftime() format string to use for formatting * @return string Formatted date string * */ static public function formatDate($value, $format) { if(!$value) return ''; if(!strlen($format) || $format == 'U' || $format == '%s') return (int) $value; // unix timestamp 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 = strftime($format, $value); if($TRIM0) $value = str_replace(array('TRIM00', 'TRIM0'), '', $value); } else { // use PHP date() $value = date($format, $value); } return $value; } /** * Given a date() format, convert it to either 'js', 'strftime' or 'regex' format * * @param string $format PHP date() format * @param string $type New format to convert to: either 'js', 'strftime', or 'regex' * @return string * */ static 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 character if($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; } /********************************************************************************************************************************* *********************************************************************************************************************************/ /** * Initlialize this module * */ public function init() { parent::init(); $this->allowTextFormatters(false); } /** * Return the Inputfield used for date/time (InputfieldDatetime) * */ public function getInputfield(Page $page, Field $field) { $inputfield = $this->modules->get('InputfieldDatetime'); $inputfield->class = $this->className(); return $inputfield; } /** * Sanitize value, per Fieldtype interface * */ public function sanitizeValue(Page $page, Field $field, $value) { return $this->_sanitizeValue($value); } /** * Sanitize a value assumed to be either a timestamp or in strtotime() compatible format * */ protected function _sanitizeValue($value) { if(empty($value)) return ''; // already a timestamp if(ctype_digit(ltrim($value, '-'))) return (int) $value; return strtotime($value); } /** * Format the value for output, according to selected format and language * */ public function ___formatValue(Page $page, Field $field, $value) { $format = ''; if($this->languages && !$this->user->language->isDefault()) $format = $field->get("dateOutputFormat{$this->user->language}"); if(!$format) $format = $field->dateOutputFormat; return $this->formatDate($value, $format); } public function ___exportValue(Page $page, Field $field, $value, array $options = array()) { if(!$value) return ''; return date('Y-m-d H:i:s'); } /** * Match a date/time value in the database, as used by PageFinder * */ public function getMatchQuery($query, $table, $subfield, $operator, $value) { $value = (int) $this->_sanitizeValue($value); if($value) $value = date('Y-m-d H:i:s', $value); else $value = ''; $database = $this->wire('database'); if($database->isOperator($operator)) { $table = $database->escapeTable($table); $subfield = $database->escapeCol($subfield); $value = $database->escapeStr($value); $query->where("$table.{$subfield}{$operator}'$value'"); } return $query; } /** * Return database schema used by this field * */ public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'datetime NOT NULL'; unset($schema['keys']['data_exact']); $schema['keys']['data'] = 'KEY data (data)'; // wipe out keys from parent return $schema; } /** * Convert value from timestamp to Y-m-d H:i:s date string * */ public function ___sleepValue(Page $page, Field $field, $value) { $value = $this->_sanitizeValue($value); return is_int($value) ? date('Y-m-d H:i:s', $value) : ''; } /** * Convert value from Y-m-d H:i:s string to timestamp * */ public function ___wakeupValue(Page $page, Field $field, $value) { if(empty($value)) return ''; $value = strtotime($value); if($value === false) $value = ''; return $value; } /** * Field configuration screen * */ public function ___getConfigInputfields(Field $field) { $inputfields = parent::___getConfigInputfields($field); $f = $this->modules->get('InputfieldSelect'); $f->attr('name', '_dateOutputFormat'); $f->label = $this->_('Date Output Format'); $f->description = $this->_('Select the format to be used when outputting dates with this field.'); $f->addOption('', $this->_('None')); $date = strtotime('2012-04-08 5:10:02 PM'); foreach(self::$dateFormats as $format) { $dateFormatted = self::formatDate($date, $format); if($format == 'U') $dateFormatted .= " (unix timestamp)"; $f->addOption($format, $dateFormatted); if(strpos($field->dateOutputFormat, $format) !== false) $f->attr('value', $format); } $f->attr('onchange', "$('#Inputfield_dateOutputFormat').val($(this).val() + ' ' + $('#Inputfield__timeOutputFormat').val());"); $inputfields->add($f); $f = $this->modules->get('InputfieldSelect'); $f->attr('name', '_timeOutputFormat'); $f->label = $this->_('Time Output Format'); $f->description = $this->_('If you want to output time in addition to the date, select the format to be used when outputting time with this field. This will be combined with the date format.'); $f->addOption('', $this->_('None')); foreach(self::$timeFormats as $format) { $timeFormatted = self::formatDate($date, $format); $f->addOption($format, $timeFormatted); if(strpos($field->dateOutputFormat, $format) !== false) $f->attr('value', $format); } $f->attr('onchange', "$('#Inputfield_dateOutputFormat').val($('#Inputfield__dateOutputFormat').val() + ' ' + $(this).val());"); $f->collapsed = Inputfield::collapsedBlank; $inputfields->add($f); $f = $this->modules->get("InputfieldText"); $f->attr('name', 'dateOutputFormat'); $f->attr('value', $field->dateOutputFormat); $f->attr('size', 20); $f->label = $this->_('Date/Time Output Format Code'); $f->description = $this->_('The date/time will be output according to the format below. This is automatically built from the date/time selections above, but you may change it as needed to suit your needs.'); $f->notes = $this->_('See the [PHP date](http://www.php.net/manual/en/function.date.php) function reference for more information on how to customize this format. Alternatively, you may use a [PHP strftime](http://www.php.net/manual/en/function.strftime.php) format if desired for localization.'); $f->collapsed = Inputfield::collapsedYes; if($this->languages) { $f->useLanguages = true; foreach($this->languages as $language) { if($language->isDefault()) continue; $f->set("value$language", (string) $field->get('dateOutputFormat' . $language)); } } $inputfields->add($f); return $inputfields; } }