/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); foreach ($items as $delta => $item) { $output = ''; $iso_date = ''; if ($item->date) { /** @var \Drupal\Core\Datetime\DrupalDateTime $date */ $date = $item->date; if ($this->getFieldSetting('datetime_type') == 'date') { // A date without time will pick up the current time, use the default. datetime_date_default_time($date); } // Create the ISO date in Universal Time. $iso_date = $date->format("Y-m-d\\TH:i:s") . 'Z'; $this->setTimeZone($date); $output = $this->formatDate($date); } // Display the date using theme datetime. $elements[$delta] = array('#cache' => ['contexts' => ['timezone']], '#theme' => 'time', '#text' => $output, '#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; }
/** * {@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) { $date = $item['value']; switch ($this->getFieldSetting('datetime_type')) { case DateTimeItem::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($date); $format = DATETIME_DATE_STORAGE_FORMAT; break; default: $format = DATETIME_DATETIME_STORAGE_FORMAT; break; } // Adjust the date for storage. $date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE)); $item['value'] = $date->format($format); } } return $values; }
/** * Creates a render array from a date object with ISO date attribute. * * @param \Drupal\Core\Datetime\DrupalDateTime $date * A date object. * * @return array * A render array. */ protected function buildDateWithIsoAttribute(DrupalDateTime $date) { if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) { // A date without time will pick up the current time, use the default. datetime_date_default_time($date); } // Create the ISO date in Universal Time. $iso_date = $date->format("Y-m-d\\TH:i:s") . 'Z'; $this->setTimeZone($date); $build = ['#theme' => 'time', '#text' => $this->formatDate($date), '#html' => FALSE, '#attributes' => ['datetime' => $iso_date], '#cache' => ['contexts' => ['timezone']]]; return $build; }
/** * Tests date field functionality. */ function testDateField() { $field_name = $this->fieldStorage->getName(); // Display creation form. $this->drupalGet('entity_test/add'); $this->assertFieldByName("{$field_name}[0][value][date]", '', 'Date element found.'); $this->assertFieldByXPath('//*[@id="edit-' . $field_name . '-wrapper"]/h4[contains(@class, "form-required")]', TRUE, 'Required markup found'); $this->assertNoFieldByName("{$field_name}[0][value][time]", '', 'Time element not found.'); // Submit a valid date and ensure it is accepted. $value = '2012-12-31 00:00:00'; $date = new DrupalDateTime($value); $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)); $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->assertNoRaw($date->format($time_format)); // The expected values will use the default time. datetime_date_default_time($date); // 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); entity_get_display($this->field->entity_type, $this->field->bundle, '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, format_string('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'] = array(); entity_get_display($this->field->entity_type, $this->field->bundle, 'full')->setComponent($field_name, $this->displayOptions)->save(); $expected = $date->format(DATETIME_DATE_STORAGE_FORMAT); $this->renderTestEntity($id); $this->assertText($expected, format_string('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected))); }
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); foreach ($items as $delta => $item) { $output = ''; if (!empty($item->date)) { /** @var \Drupal\Core\Datetime\DrupalDateTime $date */ $date = $item->date; if ($this->getFieldSetting('datetime_type') == 'date') { // A date without time will pick up the current time, use the default. datetime_date_default_time($date); } $this->setTimeZone($date); $output = $this->formatDate($date); } $elements[$delta] = ['#markup' => $output, '#cache' => ['contexts' => ['timezone']]]; } return $elements; }
/** * {@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 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; }
/** * Creates a date object for use as a default value. * * This will take a default value, apply the proper timezone for display in * a widget, and set the default time for date-only fields. * * @param \Drupal\Core\Datetime\DrupalDateTime $date * The UTC default date. * @param string $timezone * The timezone to apply. * * @return \Drupal\Core\Datetime\DrupalDateTime * A date object for use as a default value in a field widget. */ protected function createDefaultValue($date, $timezone) { // 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($timezone)); return $date; }
/** * Tests date field functionality. */ function testDateField() { $field_name = $this->fieldStorage->getName(); // Display creation form. $this->drupalGet('entity_test/add'); $this->assertFieldByName("{$field_name}[0][value][date]", '', '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]", '', 'Time element not found.'); // Build up a date in the UTC timezone. $value = '2012-12-31 00:00:00'; $date = new DrupalDateTime($value, 'UTC'); // The expected values will use the default time. datetime_date_default_time($date); // 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)); $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->assertNoRaw($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_DATE_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') + $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 '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($date_format); $entity->save(); $this->displayOptions['type'] = 'datetime_time_ago'; $this->displayOptions['settings'] = array('future_format' => '@interval in the future', 'past_format' => '@interval in the past', '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($date_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))); }
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = array(); foreach ($items as $delta => $item) { $date = $item->date; $output = ''; if (!empty($item->date)) { if ($this->getFieldSetting('datetime_type') == 'date') { // A date without time will pick up the current time, use the default. datetime_date_default_time($date); } $output = $this->formatDate($date); } $elements[$delta] = array('#markup' => $output); } return $elements; }
/** * Tests date field functionality. */ public function testDateRangeField() { $field_name = $this->fieldStorage->getName(); // Loop through defined timezones to test that date-only fields work at the // extremes. foreach (static::$timezones as $timezone) { $this->setSiteTimezone($timezone); // Ensure field is set to a date-only field. $this->fieldStorage->setSetting('datetime_type', DateRangeItem::DATETIME_TYPE_DATE); $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 UTC timezone. $value = '2012-12-31 00:00:00'; $start_date = new DrupalDateTime($value, 'UTC'); $end_value = '2013-06-06 00:00:00'; $end_date = new DrupalDateTime($end_value, 'UTC'); // 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 the date doesn't change when entity is edited through the form. $entity = EntityTest::load($id); $this->assertEqual('2012-12-31', $entity->{$field_name}->value); $this->assertEqual('2013-06-06', $entity->{$field_name}->end_value); $this->drupalGet('entity_test/manage/' . $id . '/edit'); $this->drupalPostForm(NULL, [], t('Save')); $this->drupalGet('entity_test/manage/' . $id . '/edit'); $this->drupalPostForm(NULL, [], t('Save')); $this->drupalGet('entity_test/manage/' . $id . '/edit'); $this->drupalPostForm(NULL, [], t('Save')); $entity = EntityTest::load($id); $this->assertEqual('2012-12-31', $entity->{$field_name}->value); $this->assertEqual('2013-06-06', $entity->{$field_name}->end_value); // Formats that display a time component for date-only fields will display // the default time, so that is applied before calculating the expected // value. datetime_date_default_time($start_date); datetime_date_default_time($end_date); // Reset display options since these get changed below. $this->displayOptions = ['type' => 'daterange_default', 'label' => 'hidden', 'settings' => ['format_type' => 'long', 'separator' => 'THESEPARATOR'] + $this->defaultSettings]; // Verify that the default formatter works. 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', '', DATETIME_STORAGE_TIMEZONE); $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', DATETIME_STORAGE_TIMEZONE); $end_expected = $this->dateFormatter->format($end_date->getTimestamp(), 'long', '', DATETIME_STORAGE_TIMEZONE); $end_expected_iso = $this->dateFormatter->format($end_date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', DATETIME_STORAGE_TIMEZONE); $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_DATE_STORAGE_FORMAT) . ' - ' . $end_date->format(DATETIME_DATE_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))); // 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, 'UTC'); $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))); datetime_date_default_time($start_date); $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', '', DATETIME_STORAGE_TIMEZONE); $start_expected_iso = $this->dateFormatter->format($start_date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', DATETIME_STORAGE_TIMEZONE); $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->assertNoText(' THESEPARATOR ', 'Separator not found on page'); $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_DATE_STORAGE_FORMAT); $this->renderTestEntity($id); $this->assertText($expected, new FormattableMarkup('Formatted date field using plain format displayed as %expected.', array('%expected' => $expected))); $this->assertNoText(' THESEPARATOR ', 'Separator not found on page'); $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']); $this->renderTestEntity($id); $this->assertText($expected, new FormattableMarkup('Formatted date field using daterange_custom format displayed as %expected.', array('%expected' => $expected))); $this->assertNoText(' THESEPARATOR ', 'Separator not found on page'); } }
/** * {@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; }
/** * {@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; }
/** * Tests date field functionality. */ function testDateField() { $field_name = $this->fieldStorage->getName(); // Loop through defined timezones to test that date-only fields work at the // extremes. foreach (static::$timezones as $timezone) { $this->setSiteTimezone($timezone); // Display creation form. $this->drupalGet('entity_test/add'); $this->assertFieldByName("{$field_name}[0][value][date]", '', '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]", '', 'Time element not found.'); // Build up a date in the UTC timezone. Note that using this will also // mimic the user in a different timezone simply entering '2012-12-31' via // the UI. $value = '2012-12-31 00:00:00'; $date = new DrupalDateTime($value, DATETIME_STORAGE_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]" => $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($date->format($date_format)); $this->assertNoRaw($date->format($time_format)); // Verify the date doesn't change if using a timezone that is UTC+12 when // the entity is edited through the form. $entity = EntityTest::load($id); $this->assertEqual('2012-12-31', $entity->{$field_name}->value); $this->drupalGet('entity_test/manage/' . $id . '/edit'); $this->drupalPostForm(NULL, [], t('Save')); $this->drupalGet('entity_test/manage/' . $id . '/edit'); $this->drupalPostForm(NULL, [], t('Save')); $this->drupalGet('entity_test/manage/' . $id . '/edit'); $this->drupalPostForm(NULL, [], t('Save')); $entity = EntityTest::load($id); $this->assertEqual('2012-12-31', $entity->{$field_name}->value); // Reset display options since these get changed below. $this->displayOptions = array('type' => 'datetime_default', 'label' => 'hidden', 'settings' => array('format_type' => 'medium') + $this->defaultSettings); // Verify that the date is output according to the formatter settings. $options = array('format_type' => array('short', 'medium', 'long')); // Formats that display a time component for date-only fields will display // the default time, so that is applied before calculating the expected // value. datetime_date_default_time($date); 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. Since this is a date-only // field, it is expected to display the time as 00:00:00. $expected = format_date($date->getTimestamp(), $new_value, '', DATETIME_STORAGE_TIMEZONE); $expected_iso = format_date($date->getTimestamp(), 'custom', 'Y-m-d\\TH:i:s\\Z', DATETIME_STORAGE_TIMEZONE); $this->renderTestEntity($id); $this->assertFieldByXPath('//time[@datetime="' . $expected_iso . '"]', $expected, SafeMarkup::format('Formatted date field using %value format displayed as %expected with %expected_iso attribute.', array('%value' => $new_value, '%expected' => $expected, '%expected_iso' => $expected_iso))); 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_DATE_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') + $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 '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 = EntityTest::load($id); $field_name = $this->fieldStorage->getName(); $date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC'); $entity->{$field_name}->value = $date->format($date_format); $entity->save(); $this->displayOptions['type'] = 'datetime_time_ago'; $this->displayOptions['settings'] = array('future_format' => '@interval in the future', 'past_format' => '@interval in the past', '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' => $this->dateFormatter->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 = EntityTest::load($id); $field_name = $this->fieldStorage->getName(); $date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC'); $entity->{$field_name}->value = $date->format($date_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' => $this->dateFormatter->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))); } }