Rev 1 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download
<?php namespace ProcessWire;/*** ProcessWire Datetime Fieldtype** Holds date and optionally time values.** For documentation about the fields used in this class, please see:* /wire/core/Fieldtype.php** ProcessWire 3.x, Copyright 2019 by Ryan Cramer* https://processwire.com**/class FieldtypeDatetime extends Fieldtype {public static function getModuleInfo() {return array('title' => 'Datetime','version' => 105,'summary' => 'Field that stores a date and optionally time',);}/*** Default date output format**/const defaultDateOutputFormat = 'Y-m-d';/*** Return all predefined PHP date() formats for use as dates** Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.** @deprecated Use WireDateTime class instead* @return array**/static public function getDateFormats() {return WireDateTime::_getDateFormats();}/*** Return all predefined PHP date() formats for use as times** Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.** @deprecated Use WireDateTime class instead* @return array**/static public function getTimeFormats() {return WireDateTime::_getTimeFormats();}/*** Given a date/time string and expected format, convert it to a unix timestamp** Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.** @param string $str Date/time string* @param string $format Format of the date/time string in PHP date syntax* @return int* @deprecated Use WireDateTime class instead**/static public function stringToTimestamp($str, $format) {$wdt = new WireDateTime();return $wdt->stringToTimestamp($str, $format);}/*** Format a date with the given PHP date() or PHP strftime() format** Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.** @param int $value Unix timestamp of date* @param string $format date() or strftime() format string to use for formatting* @return string Formatted date string* @deprecated Use WireDateTime class instead**/static public function formatDate($value, $format) {$wdt = new WireDateTime();return $wdt->formatDate($value, $format);}/*** Given a date() format, convert it to either 'js', 'strftime' or 'regex' format** Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.** @param string $format PHP date() format* @param string $type New format to convert to: either 'js', 'strftime', or 'regex'* @return string* @deprecated Use WireDateTime class instead**/static public function convertDateFormat($format, $type) {$wdt = new WireDateTime();return $wdt->convertDateFormat($format, $type);}/******************************************************************************************************************************************************************************************************************************************************************//*** Return the Inputfield used for date/time (InputfieldDatetime)** @param Page $page* @param Field $field* @return InputfieldDatetime**/public function getInputfield(Page $page, Field $field) {/** @var InputfieldDatetime $inputfield */$inputfield = $this->wire('modules')->get('InputfieldDatetime');$inputfield->class = $this->className();return $inputfield;}/*** Sanitize value, per Fieldtype interface** @param Page $page* @param Field $field* @param string|int|\DateTime $value* @return int**/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** @param string|int|\DateTime* @return int|string Returns unix timestamp integer or blank string if empty or invalid value**/protected function _sanitizeValue($value) {if(empty($value)) {// empty value$value = '';} else if(is_int($value)) {// value okay as-is} else if($value instanceof \DateTime) {// instance of DateTime$value = $value->getTimestamp();} else if(ctype_digit(ltrim("$value", '-'))) {// already a timestamp$value = (int) $value;} else {// convert date string to time$value = strtotime($value);if($value === false) $value = '';}return $value;}/*** Format the value for output, according to selected format and language** @param Page $page* @param Field $field* @param int $value* @return string**/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->get('dateOutputFormat');return $this->wire('datetime')->formatDate($value, $format);}/*** Export value** @param Page $page* @param Field $field* @param int $value* @param array $options** @return array|false|float|int|string**/public function ___exportValue(Page $page, Field $field, $value, array $options = array()) {if(!$value) return '';return date('Y-m-d H:i:s', $value);}/*** Return whether the given value is considered empty or not** @param Field $field* @param mixed $value* @return bool**/public function isEmptyValue(Field $field, $value) {if(is_object($value) && $value instanceof Selector) {// PageFinder is asking if it should let this Fieldtype handle the operator/value// combination with potential empty value present in a Selector$selector = $value;$op = substr($selector->operator, 0, 1);// tell PageFinder we will handle greater-than/less-than conditions in our getMatchQuery()if($op === '>' || $op === '<') return true;}// note: 0000-00-00 intentionally returns true, which is what $value is when PageFinder is testing// whether the Fieldtype recognizes an empty ISO-8601 date that it will convert to matching null$value = trim($value, '-0 ');return !strlen($value);}/*** Match a date/time value in the database, as used by PageFinder** @param PageFinderDatabaseQuerySelect $query* @param string $table* @param string $subfield* @param string $operator* @param int|string $value* @return DatabaseQuerySelect* @throws WireException if given invalid operator**/public function getMatchQuery($query, $table, $subfield, $operator, $value) {$database = $this->wire()->database;$intValue = $this->_sanitizeValue($value);$table = $database->escapeTable($table);$subfield = $subfield ? $database->escapeCol($subfield) : 'data';$minDT = '1000-01-01 00:00:00'; // $maxDT = '9999-12-31 23:59:59';if(is_string($value) && in_array($operator, array('%=', '^='))) {// partial date string matchif(!ctype_digit($value)) {$value = str_replace(array('/', '.'), '-', trim($value));}if(!ctype_digit(str_replace(array('-', ' '), '', $value))) {throw new WireException("Invalid partial date string '$value' (numbers, hyphens and space only)");}$value = str_replace(array('%', '_'), '', $value);$value = $operator === '^=' ? "$value%" : "%$value%";$query->where("$table.$subfield LIKE ?", $value);} else if(!$database->isOperator($operator)) {// invalid operatorthrow new WireException("$this invalid date operator: $operator");} else if(is_int($intValue)) {// matching a populated value that successfully converted to unix timestamp$dateString = date('Y-m-d H:i:s', $intValue);if($dateString !== false) {$query->where("$table.$subfield$operator?", $dateString);}} else {// matching an empty valueif(in_array($operator, array('!=', '>', '>='))) {// match NOT empty (!=0, >0)$query->where("$table.$subfield>=?", $minDT);} else if(in_array($operator, array('=', '<', '<='))) {// match empty (=0, <0, <=0): match null or value below unix timestamp range// this includes 0000-00-00 when present and used by MySQL version$query->where("$table.$subfield IS NULL OR $table.$subfield<?", $minDT);} else {// unsupported operatorthrow new WireException("$this operator cannot be used here: $operator");}}return $query;}/*** Get selector info** @param Field $field* @param array $data* @return array**/public function ___getSelectorInfo(Field $field, array $data = array()) {$a = parent::___getSelectorInfo($field, $data);$a['operators'] = array('=', '!=', '>', '>=', '<', '<=', '%=', '^=', '=""', '!=""');return $a;}/*** Return database schema used by this field** @param Field $field* @return array**/public function getDatabaseSchema(Field $field) {$schema = parent::getDatabaseSchema($field);$schema['data'] = 'datetime NOT NULL';$schema['keys']['data'] = 'KEY data (data)';return $schema;}/*** Convert value from timestamp to Y-m-d H:i:s date string** @param Page $page* @param Field $field* @param string|int $value* @return 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** @param Page $page* @param Field $field* @param string $value* @return int**/public function ___wakeupValue(Page $page, Field $field, $value) {if(empty($value)) return '';$value = strtotime($value);if($value === false) $value = '';return $value;}/*** Get compatible Fieldtypes** @param Field $field* @return Fieldtypes* @throws WireException**/public function ___getCompatibleFieldtypes(Field $field) {$fieldtypes = $this->wire(new Fieldtypes());foreach($this->wire('fieldtypes') as $fieldtype) {if($fieldtype instanceof FieldtypeDatetime) $fieldtypes->add($fieldtype);}return $fieldtypes;}/*** Field configuration screen** @param Field $field* @return InputfieldWrapper**/public function ___getConfigInputfields(Field $field) {$inputfields = parent::___getConfigInputfields($field);$wdt = $this->wire('datetime');/** @var InputfieldSelect $f */$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->description .= $this->_('For relative date/time formats, see the Time Output Format.');$f->icon = 'calendar';$f->addOption('', $this->_('None'));$f->columnWidth = 50;$date = strlen(date('jn')) < 4 ? time() : strtotime('2016-04-08 5:10:02 PM');$found = false;foreach($wdt->getDateFormats() as $format) {$dateFormatted = $wdt->formatDate($date, $format);if($format == 'U') $dateFormatted .= " " . $this->_('(unix timestamp)');$f->addOption($format, "$dateFormatted [$format]");if(!$found && strpos($field->get('dateOutputFormat'), $format) !== false) {$f->attr('value', $format);$found = true;}}$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->icon = 'clock-o';$f->addOption('', $this->_('None'));$f->columnWidth = 50;$date = strtotime('5:10:02 PM');$dateOutputFormat = $field->get('dateOutputFormat');foreach($wdt->getTimeFormats() as $format) {$timeFormatted = $wdt->formatDate($date, $format);$f->addOption($format, "$timeFormatted [$format]");if(strpos($dateOutputFormat, $format) !== false) {list(,$test) = explode($format, $dateOutputFormat);if(!strlen($test)) $f->attr('value', $format);}}$f->attr('onchange', "$('#Inputfield_dateOutputFormat').val($('#Inputfield__dateOutputFormat').val() + ' ' + $(this).val());");//$f->collapsed = Inputfield::collapsedBlank;$inputfields->add($f);/** @var InputfieldText $f */$f = $this->modules->get("InputfieldText");$f->attr('name', 'dateOutputFormat');$f->attr('value', $field->get('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->description .= $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->icon = 'code';$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;}}