public function serveCachedPage() { $cache_enabled = $this->getCacheMode(); // If there is no session cookie and cache is enabled (or forced), try // to serve a cached page. if (!isset($_COOKIE[session_name()]) && $cache_enabled) { global $user; // Make sure there is a user object because its timestamp will be // checked, hook_boot might check for anonymous user etc. $user = drupal_anonymous_user(); // Get the page from the cache. $cache = drupal_page_get_cache(); // If there is a cached page, display it. if (is_object($cache)) { header('X-Drupal-Cache: HIT'); // Restore the metadata cached with the page. $_GET['q'] = $cache->data['path']; drupal_set_title($cache->data['title'], PASS_THROUGH); date_default_timezone_set(drupal_get_user_timezone()); // If the skipping of the bootstrap hooks is not enforced, call // hook_boot. if (variable_get('page_cache_invoke_hooks', TRUE)) { bootstrap_invoke_all('boot'); } drupal_serve_page_from_cache($cache); // If the skipping of the bootstrap hooks is not enforced, call // hook_exit. if (variable_get('page_cache_invoke_hooks', TRUE)) { bootstrap_invoke_all('exit'); } // We are done. exit; } else { header('X-Drupal-Cache: MISS'); } } }
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $element['start_date'] = array('#type' => 'date', '#title' => t('Start date'), '#default_value' => $items[$delta]->start_date, '#date_increment' => 1, '#date_timezone' => drupal_get_user_timezone()); $element['end_date'] = array('#type' => 'date', '#title' => t('End date'), '#default_value' => $items[$delta]->end_date, '#date_increment' => 1, '#date_timezone' => drupal_get_user_timezone()); $element['state'] = array('#type' => 'select', '#title' => t('State'), '#options' => array('paid-leave' => 'paid-leave', 'unpaid-leave' => 'unpaid-leave'), '#default_value' => $items[$delta]->state); return $element; }
/** * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { // We are nesting some sub-elements inside the parent, so we need a wrapper. // We also need to add another #title attribute at the top level for ease in // identifying this item in error messages. We do not want to display this // title because the actual title display is handled at a higher level by // the Field module. $element['#theme_wrappers'][] = 'datetime_wrapper'; $element['#attributes']['class'][] = 'container-inline'; $element['value'] = array('#type' => 'datetime', '#default_value' => NULL, '#date_increment' => 1, '#date_timezone' => drupal_get_user_timezone(), '#required' => $element['#required']); if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) { // A date-only field should have no timezone conversion performed, so // use the same timezone as for storage. $element['value']['#date_timezone'] = DATETIME_STORAGE_TIMEZONE; } if ($items[$delta]->date) { $date = $items[$delta]->date; // The date was created and verified during field_load(), so it is safe to // use without further inspection. if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) { // A date without time will pick up the current time, use the default // time. datetime_date_default_time($date); } $date->setTimezone(new \DateTimeZone($element['value']['#date_timezone'])); $element['value']['#default_value'] = $date; } return $element; }
/** * Overrides prepareTimezone(). * * Override basic component timezone handling to use Drupal's * knowledge of the preferred user timezone. */ protected function prepareTimezone($timezone) { if (empty($timezone)) { // Fallback to user or system default timezone. $timezone = drupal_get_user_timezone(); } return parent::prepareTimezone($timezone); }
/** * {@inheritdoc} */ public function refreshUser(UserInterface $user) { if (!$user instanceof User) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); } $GLOBALS['user'] = $user->getDrupalUser(); date_default_timezone_set(drupal_get_user_timezone()); return $this->loadUserByUsername($user->getUsername()); }
/** * {@inheritdoc} */ public function setAccount(AccountInterface $account) { // If the passed account is already proxied, use the actual account instead // to prevent loops. if ($account instanceof static) { $account = $account->getAccount(); } $this->account = $account; date_default_timezone_set(drupal_get_user_timezone()); }
/** * Getter for the start timestamp. * * @param int $index * The index of the field value to be retrieved. Defaults to 0. * * @return int * The start date as a UNIX timestamp. */ protected function getStartTimeStamp($index = 0) { $value = $this->fieldItemList->getValue()[$index]['value']; $field_def = $this->fieldItemList->getFieldDefinition(); $field_type = $field_def->getFieldStorageDefinition()->getType(); if ($field_type == 'datetime') { /** @var \Drupal/datetime\Plugin\FieldType\DateTimeItem $field */ $field = $this->fieldItemList->get($index); // Set User's Timezone $field->date->setTimezone(timezone_open(drupal_get_user_timezone())); // Format to timestamp. return $field->date->format('U'); } return (int) $value; }
/** * Authenticates user on request. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The request event. * * @see \Drupal\Core\Authentication\AuthenticationProviderInterface::authenticate() */ public function onKernelRequestAuthenticate(GetResponseEvent $event) { if ($event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) { $request = $event->getRequest(); if ($this->authenticationProvider->applies($request)) { $account = $this->authenticationProvider->authenticate($request); if ($account) { $this->accountProxy->setAccount($account); return; } } // No account has been set explicitly, initialize the timezone here. date_default_timezone_set(drupal_get_user_timezone()); } }
/** * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) { // We are nesting some sub-elements inside the parent, so we need a wrapper. // We also need to add another #title attribute at the top level for ease in // identifying this item in error messages. We do not want to display this // title because the actual title display is handled at a higher level by // the Field module. $element['#theme_wrappers'][] = 'datetime_wrapper'; $element['#attributes']['class'][] = 'container-inline'; $element['#element_validate'][] = 'datetime_datetime_widget_validate'; // Identify the type of date and time elements to use. switch ($this->getFieldSetting('datetime_type')) { case DateTimeItem::DATETIME_TYPE_DATE: $date_type = 'date'; $time_type = 'none'; $date_format = $this->dateStorage->load('html_date')->getPattern(); $time_format = ''; $element_format = $date_format; $storage_format = DATETIME_DATE_STORAGE_FORMAT; break; default: $date_type = 'date'; $time_type = 'time'; $date_format = $this->dateStorage->load('html_date')->getPattern(); $time_format = $this->dateStorage->load('html_time')->getPattern(); $element_format = $date_format . ' ' . $time_format; $storage_format = DATETIME_DATETIME_STORAGE_FORMAT; break; } $element['value'] = array('#type' => 'datetime', '#default_value' => NULL, '#date_increment' => 1, '#date_date_format' => $date_format, '#date_date_element' => $date_type, '#date_date_callbacks' => array(), '#date_time_format' => $time_format, '#date_time_element' => $time_type, '#date_time_callbacks' => array(), '#date_timezone' => drupal_get_user_timezone(), '#required' => $element['#required']); // Set the storage and widget options so the validation can use them. The // validator will not have access to the field definition. $element['value']['#date_element_format'] = $element_format; $element['value']['#date_storage_format'] = $storage_format; if ($items[$delta]->date) { $date = $items[$delta]->date; // The date was created and verified during field_load(), so it is safe to // use without further inspection. $date->setTimezone(new \DateTimeZone($element['value']['#date_timezone'])); if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) { // A date without time will pick up the current time, use the default // time. datetime_date_default_time($date); } $element['value']['#default_value'] = $date; } return $element; }
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items) { $elements = array(); foreach ($items as $delta => $item) { $output = ''; if (!empty($item->date)) { // The date was created and verified during field_load(), so it is safe // to use without further inspection. $date = $item->date; $date->setTimeZone(timezone_open(drupal_get_user_timezone())); $format = DATETIME_DATETIME_STORAGE_FORMAT; if ($this->getFieldSetting('datetime_type') == 'date') { // A date without time will pick up the current time, use the default. datetime_date_default_time($date); $format = DATETIME_DATE_STORAGE_FORMAT; } $output = $date->format($format); } $elements[$delta] = array('#markup' => $output); } return $elements; }
/** * {@inheritdoc} */ public static function processDefaultValue($default_value, FieldableEntityInterface $entity, FieldDefinitionInterface $definition) { $default_value = parent::processDefaultValue($default_value, $entity, $definition); if (isset($default_value[0]['default_date_type'])) { if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) { // A default date only value should be in the format used for date // storage but in the user's local timezone. $date = new DrupalDateTime($default_value[0]['default_date'], drupal_get_user_timezone()); $format = DATETIME_DATE_STORAGE_FORMAT; } else { // A default date+time value should be in the format and timezone used // for date storage. $date = new DrupalDateTime($default_value[0]['default_date'], DATETIME_STORAGE_TIMEZONE); $format = DATETIME_DATETIME_STORAGE_FORMAT; } $value = $date->format($format); // We only provide a default value for the first item, as do all fields. // Otherwise, there is no way to clear out unwanted values on multiple value // fields. $default_value = array(array('value' => $value, 'date' => $date)); } return $default_value; }
/** * Expands a datetime element type into date and/or time elements. * * All form elements are designed to have sane defaults so any or all can be * omitted. Both the date and time components are configurable so they can be * output as HTML5 datetime elements or not, as desired. * * Examples of possible configurations include: * HTML5 date and time: * #date_date_element = 'date'; * #date_time_element = 'time'; * HTML5 datetime: * #date_date_element = 'datetime'; * #date_time_element = 'none'; * HTML5 time only: * #date_date_element = 'none'; * #date_time_element = 'time' * Non-HTML5: * #date_date_element = 'text'; * #date_time_element = 'text'; * * Required settings: * - #default_value: A DrupalDateTime object, adjusted to the proper local * timezone. Converting a date stored in the database from UTC to the local * zone and converting it back to UTC before storing it is not handled here. * This element accepts a date as the default value, and then converts the * user input strings back into a new date object on submission. No timezone * adjustment is performed. * Optional properties include: * - #date_date_format: A date format string that describes the format that * should be displayed to the end user for the date. When using HTML5 * elements the format MUST use the appropriate HTML5 format for that * element, no other format will work. See the format_date() function for a * list of the possible formats and HTML5 standards for the HTML5 * requirements. Defaults to the right HTML5 format for the chosen element * if a HTML5 element is used, otherwise defaults to * entity_load('date_format', 'html_date')->getPattern(). * - #date_date_element: The date element. Options are: * - datetime: Use the HTML5 datetime element type. * - datetime-local: Use the HTML5 datetime-local element type. * - date: Use the HTML5 date element type. * - text: No HTML5 element, use a normal text field. * - none: Do not display a date element. * - #date_date_callbacks: Array of optional callbacks for the date element. * Can be used to add a jQuery datepicker. * - #date_time_element: The time element. Options are: * - time: Use a HTML5 time element type. * - text: No HTML5 element, use a normal text field. * - none: Do not display a time element. * - #date_time_format: A date format string that describes the format that * should be displayed to the end user for the time. When using HTML5 * elements the format MUST use the appropriate HTML5 format for that * element, no other format will work. See the format_date() function for * a list of the possible formats and HTML5 standards for the HTML5 * requirements. Defaults to the right HTML5 format for the chosen element * if a HTML5 element is used, otherwise defaults to * entity_load('date_format', 'html_time')->getPattern(). * - #date_time_callbacks: An array of optional callbacks for the time * element. Can be used to add a jQuery timepicker or an 'All day' checkbox. * - #date_year_range: A description of the range of years to allow, like * '1900:2050', '-3:+3' or '2000:+3', where the first value describes the * earliest year and the second the latest year in the range. A year * in either position means that specific year. A +/- value describes a * dynamic value that is that many years earlier or later than the current * year at the time the form is displayed. Used in jQueryUI datepicker year * range and HTML5 min/max date settings. Defaults to '1900:2050'. * - #date_increment: The increment to use for minutes and seconds, i.e. * '15' would show only :00, :15, :30 and :45. Used for HTML5 step values and * jQueryUI datepicker settings. Defaults to 1 to show every minute. * - #date_timezone: The local timezone to use when creating dates. Generally * this should be left empty and it will be set correctly for the user using * the form. Useful if the default value is empty to designate a desired * timezone for dates created in form processing. If a default date is * provided, this value will be ignored, the timezone in the default date * takes precedence. Defaults to the value returned by * drupal_get_user_timezone(). * * Example usage: * @code * $form = array( * '#type' => 'datetime', * '#default_value' => new DrupalDateTime('2000-01-01 00:00:00'), * '#date_date_element' => 'date', * '#date_time_element' => 'none', * '#date_year_range' => '2010:+3', * ); * @endcode * * @param array $element * The form element whose value is being processed. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. * @param array $complete_form * The complete form structure. * * @return array * The form element whose value has been processed. */ public static function processDatetime(&$element, FormStateInterface $form_state, &$complete_form) { $format_settings = array(); // The value callback has populated the #value array. $date = !empty($element['#value']['object']) ? $element['#value']['object'] : NULL; // Set a fallback timezone. if ($date instanceof DrupalDateTime) { $element['#date_timezone'] = $date->getTimezone()->getName(); } elseif (empty($element['#timezone'])) { $element['#date_timezone'] = drupal_get_user_timezone(); } $element['#tree'] = TRUE; if ($element['#date_date_element'] != 'none') { $date_format = $element['#date_date_element'] != 'none' ? static::getHtml5DateFormat($element) : ''; $date_value = !empty($date) ? $date->format($date_format, $format_settings) : $element['#value']['date']; // Creating format examples on every individual date item is messy, and // placeholders are invalid for HTML5 date and datetime, so an example // format is appended to the title to appear in tooltips. $extra_attributes = array('title' => t('Date (e.g. @format)', array('@format' => static::formatExample($date_format))), 'type' => $element['#date_date_element']); // Adds the HTML5 date attributes. if ($date instanceof DrupalDateTime && !$date->hasErrors()) { $html5_min = clone $date; $range = static::datetimeRangeYears($element['#date_year_range'], $date); $html5_min->setDate($range[0], 1, 1)->setTime(0, 0, 0); $html5_max = clone $date; $html5_max->setDate($range[1], 12, 31)->setTime(23, 59, 59); $extra_attributes += array('min' => $html5_min->format($date_format, $format_settings), 'max' => $html5_max->format($date_format, $format_settings)); } $element['date'] = array('#type' => 'date', '#title' => t('Date'), '#title_display' => 'invisible', '#value' => $date_value, '#attributes' => $element['#attributes'] + $extra_attributes, '#required' => $element['#required'], '#size' => max(12, strlen($element['#value']['date'])), '#error_no_message' => TRUE, '#date_date_format' => $element['#date_date_format']); // Allows custom callbacks to alter the element. if (!empty($element['#date_date_callbacks'])) { foreach ($element['#date_date_callbacks'] as $callback) { if (function_exists($callback)) { $callback($element, $form_state, $date); } } } } if ($element['#date_time_element'] != 'none') { $time_format = $element['#date_time_element'] != 'none' ? static::getHtml5TimeFormat($element) : ''; $time_value = !empty($date) ? $date->format($time_format, $format_settings) : $element['#value']['time']; // Adds the HTML5 attributes. $extra_attributes = array('title' => t('Time (e.g. @format)', array('@format' => static::formatExample($time_format))), 'type' => $element['#date_time_element'], 'step' => $element['#date_increment']); $element['time'] = array('#type' => 'date', '#title' => t('Time'), '#title_display' => 'invisible', '#value' => $time_value, '#attributes' => $element['#attributes'] + $extra_attributes, '#required' => $element['#required'], '#size' => 12, '#error_no_message' => TRUE); // Allows custom callbacks to alter the element. if (!empty($element['#date_time_callbacks'])) { foreach ($element['#date_time_callbacks'] as $callback) { if (function_exists($callback)) { $callback($element, $form_state, $date); } } } } return $element; }
/** * Sets the proper time zone on a DrupalDateTime object for the current user. * * A DrupalDateTime object loaded from the database will have the UTC time * zone applied to it. This method will apply the time zone for the current * user, based on system and user settings. * * @see drupal_get_user_timezone() * * @param \Drupal\Core\Datetime\DrupalDateTime $date * A DrupalDateTime object. */ protected function setTimeZone(DrupalDateTime $date) { $date->setTimeZone(timezone_open(drupal_get_user_timezone())); }
/** * Expands a date element into an array of individual elements. * * Required settings: * - #default_value: A DrupalDateTime object, adjusted to the proper local * timezone. Converting a date stored in the database from UTC to the local * zone and converting it back to UTC before storing it is not handled here. * This element accepts a date as the default value, and then converts the * user input strings back into a new date object on submission. No timezone * adjustment is performed. * Optional properties include: * - #date_part_order: Array of date parts indicating the parts and order * that should be used in the selector, optionally including 'ampm' for * 12 hour time. Default is array('year', 'month', 'day', 'hour', 'minute'). * - #date_text_parts: Array of date parts that should be presented as * text fields instead of drop-down selectors. Default is an empty array. * - #date_date_callbacks: Array of optional callbacks for the date element. * - #date_year_range: A description of the range of years to allow, like * '1900:2050', '-3:+3' or '2000:+3', where the first value describes the * earliest year and the second the latest year in the range. A year * in either position means that specific year. A +/- value describes a * dynamic value that is that many years earlier or later than the current * year at the time the form is displayed. Defaults to '1900:2050'. * - #date_increment: The increment to use for minutes and seconds, i.e. * '15' would show only :00, :15, :30 and :45. Defaults to 1 to show every * minute. * - #date_timezone: The local timezone to use when creating dates. Generally * this should be left empty and it will be set correctly for the user using * the form. Useful if the default value is empty to designate a desired * timezone for dates created in form processing. If a default date is * provided, this value will be ignored, the timezone in the default date * takes precedence. Defaults to the value returned by * drupal_get_user_timezone(). * * Example usage: * @code * $form = array( * '#type' => 'datelist', * '#default_value' => new DrupalDateTime('2000-01-01 00:00:00'), * '#date_part_order' => array('month', 'day', 'year', 'hour', 'minute', 'ampm'), * '#date_text_parts' => array('year'), * '#date_year_range' => '2010:2020', * '#date_increment' => 15, * ); * @endcode * * @param array $element * The form element whose value is being processed. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. * @param array $complete_form * The complete form structure. * * @return array */ public static function processDatelist(&$element, FormStateInterface $form_state, &$complete_form) { // Load translated date part labels from the appropriate calendar plugin. $date_helper = new DateHelper(); // The value callback has populated the #value array. $date = !empty($element['#value']['object']) ? $element['#value']['object'] : NULL; // Set a fallback timezone. if ($date instanceof DrupalDateTime) { $element['#date_timezone'] = $date->getTimezone()->getName(); } elseif (!empty($element['#timezone'])) { $element['#date_timezone'] = $element['#date_timezone']; } else { $element['#date_timezone'] = drupal_get_user_timezone(); } $element['#tree'] = TRUE; // Determine the order of the date elements. $order = !empty($element['#date_part_order']) ? $element['#date_part_order'] : array('year', 'month', 'day'); $text_parts = !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(); // Output multi-selector for date. foreach ($order as $part) { switch ($part) { case 'day': $options = $date_helper->days($element['#required']); $format = 'j'; $title = t('Day'); break; case 'month': $options = $date_helper->monthNamesAbbr($element['#required']); $format = 'n'; $title = t('Month'); break; case 'year': $range = static::datetimeRangeYears($element['#date_year_range'], $date); $options = $date_helper->years($range[0], $range[1], $element['#required']); $format = 'Y'; $title = t('Year'); break; case 'hour': $format = in_array('ampm', $element['#date_part_order']) ? 'g' : 'G'; $options = $date_helper->hours($format, $element['#required']); $title = t('Hour'); break; case 'minute': $format = 'i'; $options = $date_helper->minutes($format, $element['#required'], $element['#date_increment']); $title = t('Minute'); break; case 'second': $format = 's'; $options = $date_helper->seconds($format, $element['#required'], $element['#date_increment']); $title = t('Second'); break; case 'ampm': $format = 'a'; $options = $date_helper->ampm($element['#required']); $title = t('AM/PM'); break; default: $format = ''; $options = array(); $title = ''; } $default = !empty($element['#value'][$part]) ? $element['#value'][$part] : ''; $value = $date instanceof DrupalDateTime && !$date->hasErrors() ? $date->format($format) : $default; if (!empty($value) && $part != 'ampm') { $value = intval($value); } $element['#attributes']['title'] = $title; $element[$part] = array('#type' => in_array($part, $text_parts) ? 'textfield' : 'select', '#title' => $title, '#title_display' => 'invisible', '#value' => $value, '#attributes' => $element['#attributes'], '#options' => $options, '#required' => $element['#required'], '#error_no_message' => TRUE); } // Allows custom callbacks to alter the element. if (!empty($element['#date_date_callbacks'])) { foreach ($element['#date_date_callbacks'] as $callback) { if (function_exists($callback)) { $callback($element, $form_state, $date); } } } return $element; }
/** * Sets the proper time zone on a DrupalDateTime object for the current user. * * A DrupalDateTime object loaded from the database will have the UTC time * zone applied to it. This method will apply the time zone for the current * user, based on system and user settings. * * @see drupal_get_user_timezone() * * @param \Drupal\Core\Datetime\DrupalDateTime $date * A DrupalDateTime object. */ protected function setTimeZone(DrupalDateTime $date) { if ($this->getFieldSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) { // A date without time has no timezone conversion. $timezone = DATETIME_STORAGE_TIMEZONE; } else { $timezone = drupal_get_user_timezone(); } $date->setTimeZone(timezone_open($timezone)); }
<article id="node-<?php print $node->nid; ?> " class="<?php print $classes; ?> clearfix"<?php print $attributes; ?> > <?php if ($node->field_mt_event_date) { $timezone = drupal_get_user_timezone(); $event_start_date_field = $node->field_mt_event_date['und'][0]['value']; $event_start_timestamp = strtotime($event_start_date_field); $event_start_month = format_date($event_start_timestamp, 'custom', 'M', $timezone); $event_start_day = format_date($event_start_timestamp, 'custom', 'd', $timezone); $event_start_h = format_date($event_start_timestamp, 'custom', 'H', $timezone); $event_start_m = format_date($event_start_timestamp, 'custom', 'i', $timezone); $event_end_date_field = $node->field_mt_event_date['und'][0]['value2']; $event_end_timestamp = strtotime($event_end_date_field); $event_end_month = format_date($event_end_timestamp, 'custom', 'M', $timezone); $event_end_day = format_date($event_end_timestamp, 'custom', 'd', $timezone); $event_end_h = format_date($event_end_timestamp, 'custom', 'H', $timezone); $event_end_m = format_date($event_end_timestamp, 'custom', 'i', $timezone); } if ($node->field_mt_event_location) { $event_place = $node->field_mt_event_location['und'][0]['value']; } ?> <div class="event-content-wrapper clearfix">
/** * Listener bootstraps Drupal to DRUPAL_BOOTSTRAP_PAGE_HEADER * * @param GetResponseEvent $event */ public function onKernelRequestAfterSession(GetResponseEvent $event) { if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) { if (empty($GLOBALS['user'])) { $GLOBALS['user'] = drupal_anonymous_user(); date_default_timezone_set(drupal_get_user_timezone()); } // This is basically noop. drupal_bootstrap(DRUPAL_BOOTSTRAP_PAGE_HEADER); } }
/** * {@inheritdoc} */ public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { // The widget form element type has transformed the value to a // DrupalDateTime object at this point. We need to convert it back to the // storage timezone and format. foreach ($values as &$item) { if (!empty($item['value']) && $item['value'] instanceof DrupalDateTime) { /** @var \Drupal\Core\Datetime\DrupalDateTime $start_date */ $start_date = $item['value']; switch ($this->getFieldSetting('datetime_type')) { case DateRangeItem::DATETIME_TYPE_DATE: // If this is a date-only field, set it to the default time so the // timezone conversion can be reversed. datetime_date_default_time($start_date); $format = DATETIME_DATE_STORAGE_FORMAT; break; case DateRangeItem::DATETIME_TYPE_ALLDAY: // All day fields start at midnight on the starting date, but are // stored like datetime fields, so we need to adjust the time. // This function is called twice, so to prevent a double conversion // we need to explicitly set the timezone. $start_date->setTimeZone(timezone_open(drupal_get_user_timezone())); $start_date->setTime(0, 0, 0); $format = DATETIME_DATETIME_STORAGE_FORMAT; break; default: $format = DATETIME_DATETIME_STORAGE_FORMAT; break; } // Adjust the date for storage. $start_date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE)); $item['value'] = $start_date->format($format); } if (!empty($item['end_value']) && $item['end_value'] instanceof DrupalDateTime) { /** @var \Drupal\Core\Datetime\DrupalDateTime $end_date */ $end_date = $item['end_value']; switch ($this->getFieldSetting('datetime_type')) { case DateRangeItem::DATETIME_TYPE_DATE: // If this is a date-only field, set it to the default time so the // timezone conversion can be reversed. datetime_date_default_time($end_date); $format = DATETIME_DATE_STORAGE_FORMAT; break; case DateRangeItem::DATETIME_TYPE_ALLDAY: // All day fields end at midnight on the end date, but are // stored like datetime fields, so we need to adjust the time. // This function is called twice, so to prevent a double conversion // we need to explicitly set the timezone. $end_date->setTimeZone(timezone_open(drupal_get_user_timezone())); $end_date->setTime(23, 59, 59); $format = DATETIME_DATETIME_STORAGE_FORMAT; break; default: $format = DATETIME_DATETIME_STORAGE_FORMAT; break; } // Adjust the date for storage. $end_date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE)); $item['end_value'] = $end_date->format($format); } } return $values; }
/** * Tests date and time field. */ function testDatetimeField() { $field_name = $this->fieldStorage->getName(); // Change the field to a datetime field. $this->fieldStorage->setSetting('datetime_type', 'datetime'); $this->fieldStorage->save(); // Display creation form. $this->drupalGet('entity_test/add'); $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Date element found.'); $this->assertFieldByName("{$field_name}[0][value][time]", '', 'Time element found.'); // Build up a date in the UTC timezone. $value = '2012-12-31 00:00:00'; $date = new DrupalDateTime($value, 'UTC'); // Update the timezone to the system default. $date->setTimezone(timezone_open(drupal_get_user_timezone())); // Submit a valid date and ensure it is accepted. $date_format = entity_load('date_format', 'html_date')->getPattern(); $time_format = entity_load('date_format', 'html_time')->getPattern(); $edit = array("{$field_name}[0][value][date]" => $date->format($date_format), "{$field_name}[0][value][time]" => $date->format($time_format)); $this->drupalPostForm(NULL, $edit, t('Save')); preg_match('|entity_test/manage/(\\d+)|', $this->url, $match); $id = $match[1]; $this->assertText(t('entity_test @id has been created.', array('@id' => $id))); $this->assertRaw($date->format($date_format)); $this->assertRaw($date->format($time_format)); // Verify that the date is output according to the formatter settings. $options = array('format_type' => array('short', 'medium', 'long')); foreach ($options as $setting => $values) { foreach ($values as $new_value) { // Update the entity display settings. $this->displayOptions['settings'] = array($setting => $new_value) + $this->defaultSettings; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $this->renderTestEntity($id); switch ($setting) { case 'format_type': // Verify that a date is displayed. $expected = format_date($date->getTimestamp(), $new_value); $this->renderTestEntity($id); $this->assertText($expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected.', array('%value' => $new_value, '%expected' => $expected))); break; } } } // Verify that the plain formatter works. $this->displayOptions['type'] = 'datetime_plain'; $this->displayOptions['settings'] = $this->defaultSettings; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $date->format(DATETIME_DATETIME_STORAGE_FORMAT); $this->renderTestEntity($id); $this->assertText($expected, SafeMarkup::format('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected))); // Verify that the 'datetime_custom' formatter works. $this->displayOptions['type'] = 'datetime_custom'; $this->displayOptions['settings'] = array('date_format' => 'm/d/Y g:i:s A') + $this->defaultSettings; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $date->format($this->displayOptions['settings']['date_format']); $this->renderTestEntity($id); $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_custom format displayed as %expected.', array('%expected' => $expected))); // Verify that the 'timezone_override' setting works. $this->displayOptions['type'] = 'datetime_custom'; $this->displayOptions['settings'] = array('date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York') + $this->defaultSettings; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $date->format($this->displayOptions['settings']['date_format'], array('timezone' => 'America/New_York')); $this->renderTestEntity($id); $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_custom format displayed as %expected.', array('%expected' => $expected))); // Verify that the 'datetime_time_ago' formatter works for intervals in the // past. First update the test entity so that the date difference always // has the same interval. Since the database always stores UTC, and the // interval will use this, force the test date to use UTC and not the local // or user timezome. $timestamp = REQUEST_TIME - 87654321; $entity = entity_load('entity_test', $id); $field_name = $this->fieldStorage->getName(); $date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC'); $entity->{$field_name}->value = $date->format(DATETIME_DATETIME_STORAGE_FORMAT); $entity->save(); $this->displayOptions['type'] = 'datetime_time_ago'; $this->displayOptions['settings'] = array('future_format' => '@interval from now', 'past_format' => '@interval earlier', 'granularity' => 3); entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = SafeMarkup::format($this->displayOptions['settings']['past_format'], ['@interval' => \Drupal::service('date.formatter')->formatTimeDiffSince($timestamp, ['granularity' => $this->displayOptions['settings']['granularity']])]); $this->renderTestEntity($id); $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected))); // Verify that the 'datetime_time_ago' formatter works for intervals in the // future. First update the test entity so that the date difference always // has the same interval. Since the database always stores UTC, and the // interval will use this, force the test date to use UTC and not the local // or user timezome. $timestamp = REQUEST_TIME + 87654321; $entity = entity_load('entity_test', $id); $field_name = $this->fieldStorage->getName(); $date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC'); $entity->{$field_name}->value = $date->format(DATETIME_DATETIME_STORAGE_FORMAT); $entity->save(); entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = SafeMarkup::format($this->displayOptions['settings']['future_format'], ['@interval' => \Drupal::service('date.formatter')->formatTimeDiffUntil($timestamp, ['granularity' => $this->displayOptions['settings']['granularity']])]); $this->renderTestEntity($id); $this->assertText($expected, SafeMarkup::format('Formatted date field using datetime_time_ago format displayed as %expected.', array('%expected' => $expected))); }
/** * Return a date object for the ical date, adjusted to its local timezone. * * @param array $ical_date * An array of ical date information created in the ical import. * @param string $to_tz * The timezone to convert the date's value to. * * @return object * A timezone-adjusted date object. */ public static function ical_date($ical_date, $to_tz = FALSE) { // If the ical date has no timezone, must assume it is stateless // so treat it as a local date. if (empty($ical_date['datetime'])) { return NULL; } elseif (empty($ical_date['tz'])) { $from_tz = drupal_get_user_timezone(); } else { $from_tz = $ical_date['tz']; } if (strlen($ical_date['datetime']) < 11) { $ical_date['datetime'] .= ' 00:00:00'; } $date = new DrupalDateTime($ical_date['datetime'], new \DateTimeZone($from_tz)); if ($to_tz && $ical_date['tz'] != '' && $to_tz != $ical_date['tz']) { date_timezone_set($date, timezone_open($to_tz)); } return $date; }
/** * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $date_order = $this->getSetting('date_order'); $time_type = $this->getSetting('time_type'); $increment = $this->getSetting('increment'); // We're nesting some sub-elements inside the parent, so we // need a wrapper. We also need to add another #title attribute // at the top level for ease in identifying this item in error // messages. We don't want to display this title because the // actual title display is handled at a higher level by the Field // module. $element['#theme_wrappers'][] = 'datetime_wrapper'; $element['#attributes']['class'][] = 'container-inline'; $element['#element_validate'][] = 'datetime_datelist_widget_validate'; // Identify the type of date and time elements to use. switch ($this->getFieldSetting('datetime_type')) { case DateTimeItem::DATETIME_TYPE_DATE: $storage_format = DATETIME_DATE_STORAGE_FORMAT; break; default: $storage_format = DATETIME_DATETIME_STORAGE_FORMAT; break; } // Set up the date part order array. switch ($date_order) { case 'YMD': $date_part_order = array('year', 'month', 'day'); break; case 'MDY': $date_part_order = array('month', 'day', 'year'); break; case 'DMY': $date_part_order = array('day', 'month', 'year'); break; } switch ($time_type) { case '24': $date_part_order = array_merge($date_part_order, array('hour', 'minute')); break; case '12': $date_part_order = array_merge($date_part_order, array('hour', 'minute', 'ampm')); break; case 'none': break; } $element['value'] = array('#type' => 'datelist', '#default_value' => NULL, '#date_increment' => $increment, '#date_part_order' => $date_part_order, '#date_timezone' => drupal_get_user_timezone(), '#required' => $element['#required']); // Set the storage and widget options so the validation can use them. The // validator will not have access to the field definition. $element['value']['#date_storage_format'] = $storage_format; if ($items[$delta]->date) { $date = $items[$delta]->date; // The date was created and verified during field_load(), so it is safe to // use without further inspection. $date->setTimezone(new \DateTimeZone($element['value']['#date_timezone'])); if ($this->getFieldSetting('datetime_type') == 'date') { // A date without time will pick up the current time, use the default // time. datetime_date_default_time($date); } $element['value']['#default_value'] = $date; } return $element; }
/** * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement(). * * The widget builds out a complex date element in the following way: * * - A field is pulled out of the database which is comprised of one or * more collections of start/end dates. * * - The dates in this field are all converted from the UTC values stored * in the database back to the local time. This is done in #process * to avoid making this change to dates that are not being processed, * like those hidden with #access. * * - If values are empty, the field settings rules are used to determine * if the default_values should be empty, now, the same, or use strtotime. * * - Each start/end combination is created using the date_combo element type * defined by the date module. If the timezone is date-specific, a * timezone selector is added to the first combo element. * * - The date combo element creates two individual date elements, one each * for the start and end field, using the appropriate individual Date API * date elements, like selects, textfields, or popups. * * - In the individual element validation, the data supplied by the user is * used to update the individual date values. * * - In the combo date validation, the timezone is updated, if necessary, * then the user input date values are used with that timezone to create * date objects, which are used update combo date timezone and offset values. * * - In the field's submission processing, the new date values, which are in * the local timezone, are converted back to their UTC values and stored. * */ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) { $field = $this->field; $instance = $this->instance; $field_name = $field['field_name']; $entity_type = $instance['entity_type']; // If this is a new entity, populate the field with the right default values. // This happens early so even fields later hidden with #access get those values. // We should only add default values to new entities, to avoid over-writing // a value that has already been set. This means we can't just check to see // if $items is empty, because it might have been set that way on purpose. // @see date_field_widget_properties_alter() where we flagged if this is a new entity. // We check !isset($items[$delta]['value']) because entity translation may create // a new translation entity for an existing entity and we don't want to clobber // values that were already set in that case. // @see http://drupal.org/node/1478848. $is_default = TRUE; $info = entity_get_info($entity_type); $id = $info['entity keys']['id']; if (!empty($form->{$id}) && !empty($form->{$id}['#value'])) { $is_default = FALSE; } // @TODO Repeating dates should probably be made into their own field type and completely separated out. // That will have to wait for a new branch since it may break other things, including other modules // that have an expectation of what the date field types are. // Since repeating dates cannot use the default Add more button, we have to handle our own behaviors here. // Return only the first multiple value for repeating dates, then clean up the 'Add more' bits in #after_build. // The repeating values will be re-generated when the repeat widget form is validated. // At this point we can't tell if this form element is going to be hidden by #access, and we're going to // lose all but the first value by doing this, so store the original values in case we need to replace them later. if (!empty($field['settings']['repeat'])) { if ($delta == 0) { $form['#after_build'] = array('date_repeat_after_build'); $form_state['storage']['repeat_fields'][$field_name] = array_merge($form['#parents'], array($field_name)); $form_state['storage']['date_items'][$field_name][$langcode] = $items; } else { return; } } module_load_include('inc', 'date_api', 'date_api_elements'); $timezone = date_get_timezone($field['settings']['tz_handling'], isset($items[0]['timezone']) ? $items[0]['timezone'] : drupal_get_user_timezone()); // TODO see if there's a way to keep the timezone element from ever being // nested as array('timezone' => 'timezone' => value)). After struggling // with this a while, I can find no way to get it displayed in the form // correctly and get it to use the timezone element without ending up // with nesting. if (is_array($timezone)) { $timezone = $timezone['timezone']; } $element += array('#type' => 'date_combo', '#theme_wrappers' => array('date_combo'), '#weight' => $delta, '#default_value' => isset($items[$delta]) ? $items[$delta] : '', '#date_timezone' => $timezone, '#element_validate' => array('date_combo_validate'), '#date_is_default' => $is_default, '#date_items' => isset($items[$delta]) ? $items[$delta] : ''); $element['#title'] = $instance['label']; if ($field['settings']['tz_handling'] == 'date') { $element['timezone'] = array('#type' => 'date_timezone', '#theme_wrappers' => array('date_timezone'), '#delta' => $delta, '#default_value' => $timezone, '#weight' => $instance['widget']['weight'] + 1, '#attributes' => array('class' => array('date-no-float')), '#date_label_position' => $instance['widget']['settings']['label_position']); } return $element; }
/** * Use the parsed values from the ISO argument to determine the * min and max date for this period. */ function arg_range($arg) { // Parse the argument to get its parts. $parts = $this->arg_parts($arg); // Build a range from a period-only argument (assumes the min date is now.) if (empty($parts[0]['date']) && !empty($parts[0]['period']) && empty($parts[1])) { $min_date = new DrupalDateTime(); $max_date = clone $min_date; foreach ($parts[0]['period'] as $part => $value) { date_modify($max_date, "+{$value} {$part}"); } date_modify($max_date, '-1 second'); return array($min_date, $max_date); } // Build a range from a period to period argument. if (empty($parts[0]['date']) && !empty($parts[0]['period']) && !empty($parts[1]['period'])) { $min_date = new DrupalDateTime(); $max_date = clone $min_date; foreach ($parts[0]['period'] as $part => $value) { date_modify($min_date, "+{$value} {$part}"); } date_modify($min_date, '-1 second'); foreach ($parts[1]['period'] as $part => $value) { date_modify($max_date, "+{$value} {$part}"); } date_modify($max_date, '-1 second'); return array($min_date, $max_date); } if (!empty($parts[0]['date'])) { $value = $this->complete_date($parts[0]['date'], 'min'); $min_date = new DrupalDateTime($value, drupal_get_user_timezone(), DATE_FORMAT_DATETIME); // Build a range from a single date-only argument. if (empty($parts[1]) || empty($parts[1]['date']) && empty($parts[1]['period'])) { $value = $this->complete_date($parts[0]['date'], 'max'); $max_date = new DrupalDateTime($value, drupal_get_user_timezone(), DATE_FORMAT_DATETIME); return array($min_date, $max_date); } elseif (!empty($parts[1]['period'])) { foreach ($parts[1]['period'] as $part => $value) { $max_date = clone $min_date; date_modify($max_date, "+{$value} {$part}"); } date_modify($max_date, '-1 second'); return array($min_date, $max_date); } } // Build a range from start date and end date. if (!empty($parts[1]['date'])) { $value = $this->complete_date($parts[1]['date'], 'max'); $max_date = new DrupalDateTime($value, drupal_get_user_timezone(), DATE_FORMAT_DATETIME); if (isset($min_date)) { return array($min_date, $max_date); } } // Build a range from period + end date. if (!empty($parts[0]['period'])) { $min_date = new DrupalDateTime(); foreach ($parts[0]['period'] as $part => $value) { date_modify($min_date, "{$value} {$part}"); } return array($min_date, $max_date); } // Intercept invalid info and fall back to the current date. $now = new DrupalDateTime(); return array($now, $now); }
/** * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement(). * * The widget builds out a complex date element in the following way: * * - A field is pulled out of the database which is comprised of one or * more collections of start/end dates. * * - The dates in this field are all converted from the UTC values stored * in the database back to the local time. This is done in #process * to avoid making this change to dates that are not being processed, * like those hidden with #access. * * - If values are empty, the field settings rules are used to determine * if the default_values should be empty, now, the same, or use strtotime. * * - Each start/end combination is created using the date_combo element type * defined by the date module. If the timezone is date-specific, a * timezone selector is added to the first combo element. * * - The date combo element creates two individual date elements, one each * for the start and end field, using the appropriate individual Date API * date elements, like selects, textfields, or popups. * * - In the individual element validation, the data supplied by the user is * used to update the individual date values. * * - In the combo date validation, the timezone is updated, if necessary, * then the user input date values are used with that timezone to create * date objects, which are used update combo date timezone and offset values. * * - In the field's submission processing, the new date values, which are in * the local timezone, are converted back to their UTC values and stored. * */ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) { $field = $this->field; $instance = $this->instance; $field_name = $field['field_name']; $entity_type = $instance['entity_type']; // @TODO Repeating dates should probably be made into their own field type and completely separated out. // That will have to wait for a new branch since it may break other things, including other modules // that have an expectation of what the date field types are. // Since repeating dates cannot use the default Add more button, we have to handle our own behaviors here. // Return only the first multiple value for repeating dates, then clean up the 'Add more' bits in #after_build. // The repeating values will be re-generated when the repeat widget form is validated. // At this point we can't tell if this form element is going to be hidden by #access, and we're going to // lose all but the first value by doing this, so store the original values in case we need to replace them later. if (!empty($field['settings']['repeat'])) { if ($delta == 0) { $form['#after_build'] = array('date_repeat_after_build'); $form_state['storage']['repeat_fields'][$field_name] = array_merge($form['#parents'], array($field_name)); $form_state['storage']['date_items'][$field_name][$langcode] = $items; } else { return; } } module_load_include('inc', 'date_api', 'date_api_elements'); $timezone = date_get_timezone($field['settings']['tz_handling'], isset($items[0]['timezone']) ? $items[0]['timezone'] : drupal_get_user_timezone()); // TODO see if there's a way to keep the timezone element from ever being // nested as array('timezone' => 'timezone' => value)). After struggling // with this a while, I can find no way to get it displayed in the form // correctly and get it to use the timezone element without ending up // with nesting. if (is_array($timezone)) { $timezone = $timezone['timezone']; } $element += array('#weight' => $delta, '#default_value' => isset($items[$delta]) ? $items[$delta] : '', '#date_timezone' => $timezone, '#element_validate' => array('date_combo_validate'), '#date_is_default' => $is_default, '#date_increment' => $this->getSetting('increment'), '#date_year_range' => $this->getSetting('year_range'), '#required' => $element['#required'], '#date_items' => isset($items[$delta]) ? $items[$delta] : ''); $element['#title'] = $instance['label']; if ($field['settings']['tz_handling'] == 'date') { $element['timezone'] = array('#type' => 'date_timezone', '#theme_wrappers' => array('date_timezone'), '#delta' => $delta, '#default_value' => $timezone, '#weight' => $instance['widget']['weight'] + 1, '#attributes' => array('class' => array('date-no-float')), '#date_label_position' => $instance['widget']['settings']['label_position']); } return $element; }
/** * Set the database to the current user timezone, * * @return string * The current timezone as returned by drupal_get_user_timezone(). */ public function setupTimezone() { return drupal_get_user_timezone(); }
/** * Refresh various information of the object right after session state * change. */ protected function refreshAfterSessionChange() { $this->updateUser(); // FIXME: This should not live in user session, but as a reaction of // session creation or regeneration in user module. date_default_timezone_set(drupal_get_user_timezone()); }
/** * {@inheritdoc} */ public function setupTimezone() { $timezone = drupal_get_user_timezone(); // set up the database timezone $db_type = Database::getConnection()->databaseType(); if (in_array($db_type, array('mysql', 'pgsql'))) { $offset = '+00:00'; static $already_set = FALSE; if (!$already_set) { if ($db_type == 'pgsql') { Database::getConnection()->query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE"); } elseif ($db_type == 'mysql') { Database::getConnection()->query("SET @@session.time_zone = '$offset'"); } $already_set = TRUE; } } return $timezone; }
/** * Tests all-day field. */ public function testAlldayRangeField() { $field_name = $this->fieldStorage->getName(); // Ensure field is set to a all-day field. $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_ALLDAY); $this->fieldStorage->save(); // Display creation form. $this->drupalGet('entity_test/add'); $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Start date element found.'); $this->assertFieldByName("{$field_name}[0][end_value][date]", '', 'End date element found.'); $this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]/h4[contains(@class, "js-form-required")]', TRUE, 'Required markup found'); $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Start time element not found.'); $this->assertNoFieldByName("{$field_name}[0][end_value][time]", '', 'End time element not found.'); // Build up dates in the proper timezone. $value = '2012-12-31 00:00:00'; $start_date = new DrupalDateTime($value, timezone_open(drupal_get_user_timezone())); $end_value = '2013-06-06 23:59:59'; $end_date = new DrupalDateTime($end_value, timezone_open(drupal_get_user_timezone())); // Submit a valid date and ensure it is accepted. $date_format = DateFormat::load('html_date')->getPattern(); $time_format = DateFormat::load('html_time')->getPattern(); $edit = array("{$field_name}[0][value][date]" => $start_date->format($date_format), "{$field_name}[0][end_value][date]" => $end_date->format($date_format)); $this->drupalPostForm(NULL, $edit, t('Save')); preg_match('|entity_test/manage/(\\d+)|', $this->url, $match); $id = $match[1]; $this->assertText(t('entity_test @id has been created.', array('@id' => $id))); $this->assertRaw($start_date->format($date_format)); $this->assertNoRaw($start_date->format($time_format)); $this->assertRaw($end_date->format($date_format)); $this->assertNoRaw($end_date->format($time_format)); // Verify that the default formatter works. $this->displayOptions['settings'] = ['format_type' => 'long', 'separator' => 'THESEPARATOR'] + $this->defaultSettings; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $start_expected = $this->dateFormatter->format($start_date->getTimestamp(), 'long'); $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', 'UTC'); $end_expected = $this->dateFormatter->format($end_date->getTimestamp(), 'long'); $end_expected_iso = $this->dateFormatter->format($end_date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', 'UTC'); $this->renderTestEntity($id); $this->assertFieldByXPath('//time[@datetime="' . $start_expected_iso . '"]', $start_expected, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso])); $this->assertFieldByXPath('//time[@datetime="' . $end_expected_iso . '"]', $end_expected, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $end_expected, '%expected_iso' => $end_expected_iso])); $this->assertText(' THESEPARATOR ', 'Found proper separator'); // Verify that the plain formatter works. $this->displayOptions['type'] = 'daterange_plain'; $this->displayOptions['settings'] = $this->defaultSettings; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $start_date->format(DATETIME_DATETIME_STORAGE_FORMAT) . ' - ' . $end_date->format(DATETIME_DATETIME_STORAGE_FORMAT); $this->renderTestEntity($id); $this->assertText($expected, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected))); // Verify that the custom formatter works. $this->displayOptions['type'] = 'daterange_custom'; $this->displayOptions['settings'] = array('date_format' => 'm/d/Y') + $this->defaultSettings; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' - ' . $end_date->format($this->displayOptions['settings']['date_format']); $this->renderTestEntity($id); $this->assertText($expected, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); // Verify that the 'timezone_override' setting works. $this->displayOptions['type'] = 'daterange_custom'; $this->displayOptions['settings'] = ['date_format' => 'm/d/Y g:i:s A', 'timezone_override' => 'America/New_York'] + $this->defaultSettings; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $start_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']); $expected .= ' - ' . $end_date->format($this->displayOptions['settings']['date_format'], ['timezone' => 'America/New_York']); $this->renderTestEntity($id); $this->assertText($expected, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); // Test formatters when start date and end date are the same $this->drupalGet('entity_test/add'); $value = '2012-12-31 00:00:00'; $start_date = new DrupalDateTime($value, timezone_open(drupal_get_user_timezone())); $end_value = '2012-12-31 23:59:59'; $end_date = new DrupalDateTime($end_value, timezone_open(drupal_get_user_timezone())); $date_format = DateFormat::load('html_date')->getPattern(); $time_format = DateFormat::load('html_time')->getPattern(); $edit = array("{$field_name}[0][value][date]" => $start_date->format($date_format), "{$field_name}[0][end_value][date]" => $start_date->format($date_format)); $this->drupalPostForm(NULL, $edit, t('Save')); preg_match('|entity_test/manage/(\\d+)|', $this->url, $match); $id = $match[1]; $this->assertText(t('entity_test @id has been created.', array('@id' => $id))); $this->displayOptions = ['type' => 'daterange_default', 'label' => 'hidden', 'settings' => ['format_type' => 'long', 'separator' => 'THESEPARATOR'] + $this->defaultSettings]; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $start_expected = $this->dateFormatter->format($start_date->getTimestamp(), 'long'); $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', 'UTC'); $end_expected = $this->dateFormatter->format($end_date->getTimestamp(), 'long'); $end_expected_iso = $this->dateFormatter->format($end_date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', 'UTC'); $this->renderTestEntity($id); $this->assertFieldByXPath('//time[@datetime="' . $start_expected_iso . '"]', $start_expected, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $start_expected, '%expected_iso' => $start_expected_iso])); $this->assertFieldByXPath('//time[@datetime="' . $end_expected_iso . '"]', $end_expected, new FormattableMarkup('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', ['%value' => 'long', '%expected' => $end_expected, '%expected_iso' => $end_expected_iso])); $this->assertText(' THESEPARATOR ', 'Found proper separator'); $this->displayOptions['type'] = 'daterange_plain'; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $start_date->format(DATETIME_DATETIME_STORAGE_FORMAT) . ' THESEPARATOR ' . $end_date->format(DATETIME_DATETIME_STORAGE_FORMAT); $this->renderTestEntity($id); $this->assertText($expected, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected))); $this->assertText(' THESEPARATOR ', 'Found proper separator'); $this->displayOptions['type'] = 'daterange_custom'; $this->displayOptions['settings']['date_format'] = 'm/d/Y'; entity_get_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $start_date->format($this->displayOptions['settings']['date_format']) . ' THESEPARATOR ' . $end_date->format($this->displayOptions['settings']['date_format']); $this->renderTestEntity($id); $this->assertText($expected, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); $this->assertText(' THESEPARATOR ', 'Found proper separator'); }
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items) { $elements = array(); foreach ($items as $delta => $item) { $formatted_date = ''; $iso_date = ''; if ($item->date) { $date = $item->date; // Create the ISO date in Universal Time. $iso_date = $date->format("Y-m-d\\TH:i:s") . 'Z'; // The formatted output will be in local time. $date->setTimeZone(timezone_open(drupal_get_user_timezone())); if ($this->getFieldSetting('datetime_type') == 'date') { // A date without time will pick up the current time, use the default. datetime_date_default_time($date); } $formatted_date = $this->dateFormat($date); } // Display the date using theme datetime. $elements[$delta] = array('#theme' => 'datetime', '#text' => $formatted_date, '#html' => FALSE, '#attributes' => array('datetime' => $iso_date)); if (!empty($item->_attributes)) { $elements[$delta]['#attributes'] += $item->_attributes; // Unset field item attributes since they have been included in the // formatter output and should not be rendered in the field template. unset($item->_attributes); } } return $elements; }
/** * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement(). * * The widget builds out a complex date element in the following way: * * - A field is pulled out of the database which is comprised of one or * more collections of start/end dates. * * - The dates in this field are all converted from the UTC values stored * in the database back to the local time. This is done in #process * to avoid making this change to dates that are not being processed, * like those hidden with #access. * * - If values are empty, the field settings rules are used to determine * if the default_values should be empty, now, the same, or use strtotime. * * - Each start/end combination is created using the date_combo element type * defined by the date module. If the timezone is date-specific, a * timezone selector is added to the first combo element. * * - The date combo element creates two individual date elements, one each * for the start and end field, using the appropriate individual Date API * date elements, like selects, textfields, or popups. * * - In the individual element validation, the data supplied by the user is * used to update the individual date values. * * - In the combo date validation, the timezone is updated, if necessary, * then the user input date values are used with that timezone to create * date objects, which are used update combo date timezone and offset values. * * - In the field's submission processing, the new date values, which are in * the local timezone, are converted back to their UTC values and stored. * */ public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) { $field = $this->field; $instance = $this->instance; $field_name = $field['field_name']; $entity_type = $instance['entity_type']; module_load_include('inc', 'date_api', 'date_api_elements'); $timezone = date_get_timezone($field['settings']['tz_handling'], isset($items[0]['timezone']) ? $items[0]['timezone'] : drupal_get_user_timezone()); $element += array('#weight' => $delta, '#default_value' => isset($items[$delta]) ? $items[$delta] : '', '#date_timezone' => $timezone, '#element_validate' => array('date_combo_validate'), '#required' => $element['#required'], '#date_items' => isset($items[$delta]) ? $items[$delta] : ''); $element['#title'] = $instance['label']; if ($field['settings']['tz_handling'] == 'date') { $element['timezone'] = array('#type' => 'date_timezone', '#theme_wrappers' => array('date_timezone'), '#delta' => $delta, '#default_value' => $timezone, '#weight' => $instance['widget']['weight'] + 1, '#attributes' => array('class' => array('date-no-float')), '#date_label_position' => $instance['widget']['settings']['label_position']); } return $element; }