language = $this->Application->recallObject('lang.current'); } /** * Sets mixed format (date + time) for field if not set directly * * @param Array $field_options options of field * @param Array $format separate formats for date & time * @param string $type destination key in field_options to store mixed format */ function SetMixedFormat(&$field_options, &$format, $type) { if (!isset($field_options[$type])) { // default value is date+separator+time $field_options[$type] = '_regional_DateTimeFormat'; } if ($field_options[$type] == '_regional_DateTimeFormat') { $field_options[$type] = $format['date'].$field_options['date_time_separator'].$format['time']; } else if(preg_match('/_regional_(.*)/', $field_options[$type], $regs)) { $field_options[$type] = $this->language->GetDBField($regs[1]); } $format['mixed'] = $field_options[$type]; } /** * Returns separate formats for date,time,combined for input & display formats * * @param Array $field_options options of field * @param string $type type of requested information = {mixed,date,time} * @return Array display & input formats */ function GetSeparateFormats(&$field_options, $type) { if ($type == 'mixed') { if (!isset($field_options['date_time_separator'])) $field_options['date_time_separator'] = ' '; $display_format = Array (); $input_format = Array (); list ($display_format['date'], $input_format['date']) = $this->GetSeparateFormats($field_options, 'date'); list ($display_format['time'], $input_format['time']) = $this->GetSeparateFormats($field_options, 'time'); $this->SetMixedFormat($field_options, $display_format, 'format'); $this->SetMixedFormat($field_options, $input_format, 'input_format'); return Array ($display_format, $input_format); } else { // 1. set display format if (isset($field_options[$type.'_format'])) { $format = $field_options[$type.'_format']; } else { $format = $this->language->GetDBField(ucfirst($type).'Format'); } // 2. set input format if (isset($field_options['input_'.$type.'_format'])) { $input_format = $field_options['input_'.$type.'_format']; } else { $input_format = $this->language->GetDBField('Input'.ucfirst($type).'Format'); } return Array ($format, $input_format); } } /** * The method is supposed to alter config options or configure object in some way based on its usage of formatters * The methods is called for every field with formatter defined when configuring item. * Could be used for adding additional VirtualFields to an object required by some special Formatter * * @param string $field_name * @param array $field_options * @param kDBBase $object */ function PrepareOptions($field_name, &$field_options, &$object) { list ($display_format, $input_format) = $this->GetSeparateFormats($field_options, 'mixed'); $field_options['sub_fields'] = Array('date' => $field_name.'_date', 'time' => $field_name.'_time'); if (!isset($field_options['use_timezone'])) { // apply timezone from server $field_options['use_timezone'] = true; } // 1. add field to indicate, that date is already combined into one field $add_fields = Array ( $field_name . '_combined' => Array ('type' => 'int', 'default' => 0), ); // 2. add DATE virtual field $opts = Array('master_field' => $field_name, 'formatter' => 'kDateFormatter', 'format' => $display_format['date'], 'input_format' => $input_format['date']); $copy_options = Array ('type', 'default', 'required', 'use_timezone', 'error_msgs'); foreach ($copy_options as $copy_option) { if ( array_key_exists($copy_option, $field_options) ) { $opts[$copy_option] = $field_options[$copy_option]; } } $add_fields[$field_name . '_date'] = $opts; // 3. add TIME virtual field $opts['format'] = $display_format['time']; $opts['input_format'] = $input_format['time']; $add_fields[$field_name . '_time'] = $opts; $filter_type = getArrayValue($field_options, 'filter_type'); if ( $filter_type == 'range' ) { $opts['format'] = $field_options['format']; $add_fields[$field_name . '_rangefrom'] = $opts; $add_fields[$field_name . '_rangeto'] = $opts; } if ( !$object->isVirtualField($field_name) ) { // adding calculated field to format date directly in the query $object->addCalculatedField($field_name . '_date', '%1$s.' . $field_name); $object->addCalculatedField($field_name . '_time', '%1$s.' . $field_name); } $virtual_fields = $object->getVirtualFields(); $add_fields = kUtil::array_merge_recursive($add_fields, $virtual_fields); $object->setVirtualFields($add_fields); } /** * Used for split fields like timestamp -> date, time * Called from DBItem to update sub fields values after loading item * * @param string $field * @param string $value * @param Array $options * @param kDBItem $object * @return void * @access public */ public function UpdateSubFields($field, $value, &$options, &$object) { $sub_fields = getArrayValue($options, 'sub_fields'); if ( !$sub_fields || !isset($value) || !$value ) { return ; } $object->SetDBField($sub_fields['date'], $value); $object->SetDBField($sub_fields['time'], $value); } /** * Used for split fields like timestamp -> date, time * Called from DBItem Validate (before validation) to get back master field value from its sub_fields * * @param string $field * @param mixed $value * @param Array $options * @param kDBItem $object */ function UpdateMasterFields($field, $value, &$options, &$object) { $sub_fields = getArrayValue($options, 'sub_fields'); $master_field = getArrayValue($options, 'master_field'); if ( $master_field ) { // when in one of sub_fields - call update for master_field to update its value from sub_fields [are you following ? :) ] $opt = $object->GetFieldOptions($master_field); $this->UpdateMasterFields($master_field, null, $opt, $object); } elseif ( $sub_fields && !$object->GetDBField($field . '_combined') ) { // when in master field - set own value from sub_fields if ( $object->GetDBField($sub_fields['date']) != '' && $object->GetDBField($sub_fields['time']) == '' ) { // when time is not supplied, then use "midnight" (or unit config override) $empty_time = getArrayValue($options, 'empty_time'); if ( $empty_time === false ) { $empty_time = adodb_mktime(0, 0, 0); } $object->SetDBField($sub_fields['time'], $empty_time); } elseif ( $object->GetDBField($sub_fields['time']) != '' && $object->GetDBField($sub_fields['date']) == '' ) { // when date is not supplied, then use "1970-01-01 00:00:00" instead (or unit config override) $empty_date = getArrayValue($options, 'empty_date'); if ( $empty_date === false ) { $empty_date = adodb_mktime(0, 0, 0, 1, 1, 1970); } $object->SetDBField($sub_fields['date'], $empty_date); } $input_format['date'] = $object->GetFieldOption($sub_fields['date'], 'input_format'); $input_format['time'] = $object->GetFieldOption($sub_fields['time'], 'input_format'); $object->SetField($field, $object->GetField($sub_fields['date'], $input_format['date']) . $options['date_time_separator'] . $object->GetField($sub_fields['time'], $input_format['time'])); } } /** * Formats value of a given field * * @param string $value Value. * @param string $field_name Field name. * @param kDBBase $object Object. * @param string $format Format. * * @return string */ public function Format($value, $field_name, &$object, $format = null) { $options = $object->GetFieldOptions($field_name); if ( is_null($value) ) { if ( $format != 'picker' || !isset($options['picker_default']) ) { return ''; } $value = strtotime($options['picker_default']); } if ( $format == 'picker' ) { $format = '_input_'; } if ( !is_numeric($value) ) { return $value; // for leaving badly formatted date on the form } settype($value, 'int'); if ( !is_int($value) ) { return $value; } if ( isset($format) ) { $options['format'] = $format; } if ( preg_match('/_regional_(.*)/', $options['format'], $regs) ) { // when such type of format is given directly to kDBBase::GetField $options['format'] = $this->language->GetDBField($regs[1]); } if ( $options['format'] == '_input_' ) { // use input format instead of output format $options['format'] = $options['input_format']; } if ( !$options['use_timezone'] ) { return adodb_gmdate($options['format'], $value); } $format = defined($options['format']) ? constant($options['format']) : $options['format']; $dt_separator = getArrayValue($options, 'date_time_separator'); if ( $dt_separator ) { $format = trim($format, $dt_separator); } return adodb_date($format, $value); } function HumanFormat($format) { $patterns = Array('/m/', '/n/', '/d/', '/j/', '/y/', '/Y/', '/h|H/', '/g|G/', '/i/', '/s/', '/a|A/'); $replace = Array( 'mm', 'm', 'dd', 'd', 'yy', 'yyyy', 'hh', 'h', 'mm', 'ss', 'AM'); $res = preg_replace($patterns, $replace, $format); return $res; } function SQLFormat($format) { $mapping = Array( '/%/' => '%%', '/(? '%p', // Lowercase Ante meridiem and Post meridiem => MySQL provides only uppercase '/(? '%p', // Uppercase Ante meridiem and Post meridiem '/(? '%d', // Day of the month, 2 digits with leading zeros '/(? '%a', // A textual representation of a day, three letters '/(? '%M', // A full textual representation of a month, such as January or March '/(? '%l', // 12-hour format of an hour without leading zeros '/(? '%k', // 24-hour format of an hour without leading zeros '/(? '%h', // 12-hour format of an hour with leading zeros '/(? '%H', // 24-hour format of an hour with leading zeros '/(? '%i', // Minutes with leading zeros '/(? 'N/A', // Whether or not the date is in daylights savings time '/(? 'N/A', // English ordinal suffix for the day of the month, 2 characters, see below '/jS/' => '%D', // MySQL can't return separate suffix, but could return date with suffix '/(? '%e', // Day of the month without leading zeros '/(? '%W', // A full textual representation of the day of the week '/(? 'N/A', // Whether it's a leap year '/(? '%m', // Numeric representation of a month, with leading zeros '/(? '%b', // A short textual representation of a month, three letters '/(? '%c', // Numeric representation of a month, without leading zeros '/(? 'N/A', // Difference to Greenwich time (GMT) in hours '/(? 'N/A', // RFC 2822 formatted date '/(? '%s', // Seconds, with leading zeros // S and jS moved before j - see above '/(? 'N/A', // Number of days in the given month '/(? 'N/A', // Timezone setting of this machine '/(? 'N/A', // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) '/(? '%w', // Numeric representation of the day of the week '/(? '%v', // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) '/(? '%Y', // A full numeric representation of a year, 4 digits '/(? '%y', // A two digit representation of a year '/(? 'N/A', // The day of the year (starting from 0) => MySQL starts from 1 '/(? 'N/A', // Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. ); $patterns = array_keys($mapping); $replacements = array_values($mapping); $res = preg_replace($patterns, $replacements, $format); return $res; } /** * Converts formatted date+time to timestamp and validates format * * @param mixed $value * @param string $field_name * @param kDBItem $object * @param string $format Format. * @return mixed * @access public */ public function Parse($value, $field_name, &$object, $format = null) { $options = $object->GetFieldOptions($field_name); $dt_separator = getArrayValue($options,'date_time_separator'); if($dt_separator) $value = trim($value, $dt_separator); if($value == '') return NULL; //return strtotime($value); if ( !isset($format) ) { $format = $options['input_format']; } if ($dt_separator) $format = trim($format, $dt_separator); $error_params = array( 'format' => $this->HumanFormat($format), 'sample' => adodb_date($format), 'value' => $value, ); $hour = 0; $minute = 0; $second = 0; $month = 1; $day = 1; $year = 1970; $patterns['n'] = '([0-9]{1,2})'; $patterns['m'] = '([0-9]{1,2})'; $patterns['d'] = '([0-9]{1,2})'; $patterns['j'] = '([0-9]{1,2})'; $patterns['Y'] = '([0-9]{4})'; $patterns['y'] = '([0-9]{2})'; $patterns['G'] = '([0-9]{1,2})'; $patterns['g'] = '([0-9]{1,2})'; $patterns['H'] = '([0-9]{2})'; $patterns['h'] = '([0-9]{2})'; $patterns['i'] = '([0-9]{2})'; $patterns['s'] = '([0-9]{2})'; $patterns['a'] = '(am|pm)'; $patterns['A'] = '(AM|PM)'; $holders_mask = '/' . preg_replace('/[a-zA-Z]{1}/i', '([a-zA-Z]{1})', preg_quote($format, '/')) . '/'; if (!preg_match($holders_mask, $format, $holders)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } $values_mask = '/^' . preg_quote($format, '/') . '$/'; foreach ($patterns as $key => $val) { $values_mask = str_replace($key, $val, $values_mask); } if (!preg_match($values_mask, $value, $values)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } for ($i = 1; $i < count($holders); $i++) { switch ($holders[$i]) { case 'n': case 'm': $month = $values[$i]; $month = preg_replace('/^0{1}/', '', $month); break; case 'd': $day = $values[$i]; $day = preg_replace('/^0{1}/', '', $day); break; case 'Y': $year = $values[$i]; break; case 'y': $year = $values[$i] >= 70 ? 1900 + $values[$i] : 2000 + $values[$i]; break; case 'H': case 'h': case 'G': case 'g': $hour = $values[$i]; $hour = preg_replace('/^0{1}/', '', $hour); break; case 'i': $minute = $values[$i]; $minute = preg_replace('/^0{1}/', '', $minute); break; case 's': $second = $values[$i]; $second = preg_replace('/^0{1}/', '', $second); break; case 'a': case 'A': if ($hour <= 12) { // if AM/PM used with 24-hour - could happen :) if ($values[$i] == 'pm' || $values[$i] == 'PM') { $hour += 12; if ($hour == 24) $hour = 12; } elseif ($values[$i] == 'am' || $values[$i] == 'AM') { if ($hour == 12) $hour = 0; } } break; } } //echo "day: $day, month: $month, year: $year, hour: $hour, minute: $minute
"; /*if (!($year >= 1970 && $year <= 2037)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; }*/ if (!($month >= 1 && $month <= 12)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } $months_days = Array ( 1 => 31,2 => 28, 3 => 31, 4 => 30,5 => 31,6 => 30, 7 => 31, 8 => 31,9 => 30,10 => 31,11 => 30,12 => 31); if ($year % 4 == 0) $months_days[2] = 29; if (!($day >=1 && $day <= $months_days[$month])) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } if (!($hour >=0 && $hour <= 23)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } if (!($minute >=0 && $minute <= 59)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } if (!($second >=0 && $second <= 59)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } if (!$options['use_timezone']) { return adodb_gmmktime($hour, $minute, $second, $month, $day, $year); } return adodb_mktime($hour, $minute, $second, $month, $day, $year); } function GetSample($field, &$options, &$object) { return $this->Format( adodb_mktime(), $field, $object, $options['input_format']); } }