Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
44
core/lib/Drupal/Core/Datetime/DateFormatInterface.php
Normal file
44
core/lib/Drupal/Core/Datetime/DateFormatInterface.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\DateFormatInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Datetime;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a date format.
|
||||
*/
|
||||
interface DateFormatInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Gets the date pattern string for this format.
|
||||
*
|
||||
* @return string
|
||||
* The pattern string as expected by date().
|
||||
*/
|
||||
public function getPattern();
|
||||
|
||||
/**
|
||||
* Sets the date pattern for this format.
|
||||
*
|
||||
* @param string $pattern
|
||||
* The date pattern to use for this format.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPattern($pattern);
|
||||
|
||||
/**
|
||||
* Determines if this date format is locked.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the date format is locked, FALSE otherwise.
|
||||
*/
|
||||
public function isLocked();
|
||||
|
||||
}
|
451
core/lib/Drupal/Core/Datetime/DateFormatter.php
Normal file
451
core/lib/Drupal/Core/Datetime/DateFormatter.php
Normal file
|
@ -0,0 +1,451 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\DateFormatter.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Datetime;
|
||||
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Provides a service to handler various date related functionality.
|
||||
*
|
||||
* @ingroup i18n
|
||||
*/
|
||||
class DateFormatter {
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The list of loaded timezones.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $timezones;
|
||||
|
||||
/**
|
||||
* The date format storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $dateFormatStorage;
|
||||
|
||||
/**
|
||||
* Language manager for retrieving the default langcode when none is specified.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The configuration factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
protected $country = NULL;
|
||||
protected $dateFormats = array();
|
||||
|
||||
/**
|
||||
* Contains the different date interval units.
|
||||
*
|
||||
* This array is keyed by strings representing the unit (e.g.
|
||||
* '1 year|@count years') and with the amount of values of the unit in
|
||||
* seconds.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $units = array(
|
||||
'1 year|@count years' => 31536000,
|
||||
'1 month|@count months' => 2592000,
|
||||
'1 week|@count weeks' => 604800,
|
||||
'1 day|@count days' => 86400,
|
||||
'1 hour|@count hours' => 3600,
|
||||
'1 min|@count min' => 60,
|
||||
'1 sec|@count sec' => 1,
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructs a Date object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
|
||||
* The string translation.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, TranslationInterface $translation, ConfigFactoryInterface $config_factory, RequestStack $request_stack) {
|
||||
$this->dateFormatStorage = $entity_manager->getStorage('date_format');
|
||||
$this->languageManager = $language_manager;
|
||||
$this->stringTranslation = $translation;
|
||||
$this->configFactory = $config_factory;
|
||||
$this->requestStack = $request_stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date, using a date type or a custom date format string.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* A UNIX timestamp to format.
|
||||
* @param string $type
|
||||
* (optional) The format to use, one of:
|
||||
* - One of the built-in formats: 'short', 'medium',
|
||||
* 'long', 'html_datetime', 'html_date', 'html_time',
|
||||
* 'html_yearless_date', 'html_week', 'html_month', 'html_year'.
|
||||
* - The name of a date type defined by a date format config entity.
|
||||
* - The machine name of an administrator-defined date format.
|
||||
* - 'custom', to use $format.
|
||||
* Defaults to 'medium'.
|
||||
* @param string $format
|
||||
* (optional) If $type is 'custom', a PHP date format string suitable for
|
||||
* input to date(). Use a backslash to escape ordinary text, so it does not
|
||||
* get interpreted as date format characters.
|
||||
* @param string|null $timezone
|
||||
* (optional) Time zone identifier, as described at
|
||||
* http://php.net/manual/timezones.php Defaults to the time zone used to
|
||||
* display the page.
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code to translate to. NULL (default) means to use
|
||||
* the user interface language for the page.
|
||||
*
|
||||
* @return string
|
||||
* A translated date string in the requested format.
|
||||
*/
|
||||
public function format($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
|
||||
if (!isset($timezone)) {
|
||||
$timezone = date_default_timezone_get();
|
||||
}
|
||||
// Store DateTimeZone objects in an array rather than repeatedly
|
||||
// constructing identical objects over the life of a request.
|
||||
if (!isset($this->timezones[$timezone])) {
|
||||
$this->timezones[$timezone] = timezone_open($timezone);
|
||||
}
|
||||
|
||||
if (empty($langcode)) {
|
||||
$langcode = $this->languageManager->getCurrentLanguage()->getId();
|
||||
}
|
||||
|
||||
// Create a DrupalDateTime object from the timestamp and timezone.
|
||||
$create_settings = array(
|
||||
'langcode' => $langcode,
|
||||
'country' => $this->country(),
|
||||
);
|
||||
$date = DrupalDateTime::createFromTimestamp($timestamp, $this->timezones[$timezone], $create_settings);
|
||||
|
||||
// If we have a non-custom date format use the provided date format pattern.
|
||||
if ($date_format = $this->dateFormat($type, $langcode)) {
|
||||
$format = $date_format->getPattern();
|
||||
}
|
||||
|
||||
// Fall back to medium if a format was not found.
|
||||
if (empty($format)) {
|
||||
$format = $this->dateFormat('fallback', $langcode)->getPattern();
|
||||
}
|
||||
|
||||
// Call $date->format().
|
||||
$settings = array(
|
||||
'langcode' => $langcode,
|
||||
);
|
||||
return Xss::filter($date->format($format, $settings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a time interval with the requested granularity.
|
||||
*
|
||||
* Note that for intervals over 30 days, the output is approximate: a "month"
|
||||
* is always exactly 30 days, and a "year" is always 365 days. It is not
|
||||
* possible to make a more exact representation, given that there is only one
|
||||
* input in seconds. If you are formatting an interval between two specific
|
||||
* timestamps, use \Drupal\Core\Datetime\DateFormatter::formatDiff() instead.
|
||||
*
|
||||
* @param int $interval
|
||||
* The length of the interval in seconds.
|
||||
* @param int $granularity
|
||||
* (optional) How many different units to display in the string (2 by
|
||||
* default).
|
||||
* @param string|null $langcode
|
||||
* (optional) langcode: The language code for the language used to format
|
||||
* the date. Defaults to NULL, which results in the user interface language
|
||||
* for the page being used.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the interval.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatDiff()
|
||||
*/
|
||||
public function formatInterval($interval, $granularity = 2, $langcode = NULL) {
|
||||
$output = '';
|
||||
foreach ($this->units as $key => $value) {
|
||||
$key = explode('|', $key);
|
||||
if ($interval >= $value) {
|
||||
$output .= ($output ? ' ' : '') . $this->formatPlural(floor($interval / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
|
||||
$interval %= $value;
|
||||
$granularity--;
|
||||
}
|
||||
|
||||
if ($granularity == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $output ? $output : $this->t('0 sec', array(), array('langcode' => $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides values for all date formatting characters for a given timestamp.
|
||||
*
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code of the date format, if different from the site
|
||||
* default language.
|
||||
* @param int|null $timestamp
|
||||
* (optional) The Unix timestamp to format, defaults to current time.
|
||||
* @param string|null $timezone
|
||||
* (optional) The timezone to use, if different from the site's default
|
||||
* timezone.
|
||||
*
|
||||
* @return array
|
||||
* An array of formatted date values, indexed by the date format character.
|
||||
*
|
||||
* @see date()
|
||||
*/
|
||||
public function getSampleDateFormats($langcode = NULL, $timestamp = NULL, $timezone = NULL) {
|
||||
$timestamp = $timestamp ?: time();
|
||||
// All date format characters for the PHP date() function.
|
||||
$date_chars = str_split('dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU');
|
||||
$date_elements = array_combine($date_chars, $date_chars);
|
||||
return array_map(function ($character) use ($timestamp, $timezone, $langcode) {
|
||||
return $this->format($timestamp, 'custom', $character, $timezone, $langcode);
|
||||
}, $date_elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the time difference from the current request time to a timestamp.
|
||||
*
|
||||
* @param $timestamp
|
||||
* A UNIX timestamp to compare against the current request time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the timestamp can be
|
||||
* before the current request time. If TRUE (default) and $timestamp is
|
||||
* before the current request time, the result string will be "0 seconds".
|
||||
* If FALSE and $timestamp is before the current request time, the result
|
||||
* string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the difference between the given
|
||||
* timestamp and the current request time. This interval is always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatDiff()
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatTimeDiffSince()
|
||||
*/
|
||||
public function formatTimeDiffUntil($timestamp, $options = array()) {
|
||||
$request_time = $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
|
||||
return $this->formatDiff($request_time, $timestamp, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the time difference from a timestamp to the current request time.
|
||||
*
|
||||
* @param $timestamp
|
||||
* A UNIX timestamp to compare against the current request time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the timestamp can be
|
||||
* after the current request time. If TRUE (default) and $timestamp is
|
||||
* after the current request time, the result string will be "0 seconds".
|
||||
* If FALSE and $timestamp is after the current request time, the result
|
||||
* string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the difference between the given
|
||||
* timestamp and the current request time. This interval is always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatDiff()
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatTimeDiffUntil()
|
||||
*/
|
||||
public function formatTimeDiffSince($timestamp, $options = array()) {
|
||||
$request_time = $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
|
||||
return $this->formatDiff($timestamp, $request_time, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a time interval between two timestamps.
|
||||
*
|
||||
* @param int $from
|
||||
* A UNIX timestamp, defining the from date and time.
|
||||
* @param int $to
|
||||
* A UNIX timestamp, defining the to date and time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the $from timestamp
|
||||
* can be after the $to timestamp. If TRUE (default) and $from is after
|
||||
* $to, the result string will be "0 seconds". If FALSE and $from is
|
||||
* after $to, the result string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the interval. This interval is
|
||||
* always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatInterval()
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatTimeDiffSince()
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatTimeDiffUntil()
|
||||
*/
|
||||
public function formatDiff($from, $to, $options = array()) {
|
||||
|
||||
$options += array(
|
||||
'granularity' => 2,
|
||||
'langcode' => NULL,
|
||||
'strict' => TRUE,
|
||||
);
|
||||
|
||||
if ($options['strict'] && $from > $to) {
|
||||
return $this->t('0 seconds');
|
||||
}
|
||||
|
||||
$date_time_from = new \DateTime();
|
||||
$date_time_from->setTimestamp($from);
|
||||
|
||||
$date_time_to = new \DateTime();
|
||||
$date_time_to->setTimestamp($to);
|
||||
|
||||
$interval = $date_time_to->diff($date_time_from);
|
||||
|
||||
$granularity = $options['granularity'];
|
||||
$output = '';
|
||||
|
||||
// We loop over the keys provided by \DateInterval explicitly. Since we
|
||||
// don't take the "invert" property into account, the resulting output value
|
||||
// will always be positive.
|
||||
foreach (array('y', 'm', 'd', 'h', 'i', 's') as $value) {
|
||||
if ($interval->$value > 0) {
|
||||
// Switch over the keys to call formatPlural() explicitly with literal
|
||||
// strings for all different possibilities.
|
||||
switch ($value) {
|
||||
case 'y':
|
||||
$interval_output = $this->formatPlural($interval->y, '1 year', '@count years', array(), array('langcode' => $options['langcode']));
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
$interval_output = $this->formatPlural($interval->m, '1 month', '@count months', array(), array('langcode' => $options['langcode']));
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
// \DateInterval doesn't support weeks, so we need to calculate them
|
||||
// ourselves.
|
||||
$interval_output = '';
|
||||
$days = $interval->d;
|
||||
if ($days >= 7) {
|
||||
$weeks = floor($days / 7);
|
||||
$interval_output .= $this->formatPlural($weeks, '1 week', '@count weeks', array(), array('langcode' => $options['langcode']));
|
||||
$days -= $weeks * 7;
|
||||
$granularity--;
|
||||
}
|
||||
if ($granularity > 0 && $days > 0) {
|
||||
$interval_output .= ($interval_output ? ' ' : '') . $this->formatPlural($days, '1 day', '@count days', array(), array('langcode' => $options['langcode']));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
$interval_output = $this->formatPlural($interval->h, '1 hour', '@count hours', array(), array('langcode' => $options['langcode']));
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
$interval_output = $this->formatPlural($interval->i, '1 minute', '@count minutes', array(), array('langcode' => $options['langcode']));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
$interval_output = $this->formatPlural($interval->s, '1 second', '@count seconds', array(), array('langcode' => $options['langcode']));
|
||||
break;
|
||||
|
||||
}
|
||||
$output .= ($output ? ' ' : '') . $interval_output;
|
||||
$granularity--;
|
||||
}
|
||||
|
||||
if ($granularity <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($output)) {
|
||||
$output = $this->t('0 seconds');
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given format pattern for the given langcode.
|
||||
*
|
||||
* @param string $format
|
||||
* The machine name of the date format.
|
||||
* @param string $langcode
|
||||
* The langcode of the language to use.
|
||||
*
|
||||
* @return string|null
|
||||
* The pattern for the date format in the given language for non-custom
|
||||
* formats, NULL otherwise.
|
||||
*/
|
||||
protected function dateFormat($format, $langcode) {
|
||||
if (!isset($this->dateFormats[$format][$langcode])) {
|
||||
$original_language = $this->languageManager->getConfigOverrideLanguage();
|
||||
$this->languageManager->setConfigOverrideLanguage(new Language(array('id' => $langcode)));
|
||||
$this->dateFormats[$format][$langcode] = $this->dateFormatStorage->load($format);
|
||||
$this->languageManager->setConfigOverrideLanguage($original_language);
|
||||
}
|
||||
return $this->dateFormats[$format][$langcode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default country from config.
|
||||
*
|
||||
* @return string
|
||||
* The config setting for country.default.
|
||||
*/
|
||||
protected function country() {
|
||||
if ($this->country === NULL) {
|
||||
$this->country = \Drupal::config('system.date')->get('country.default');
|
||||
}
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
}
|
529
core/lib/Drupal/Core/Datetime/DateHelper.php
Normal file
529
core/lib/Drupal/Core/Datetime/DateHelper.php
Normal file
|
@ -0,0 +1,529 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\DateHelper.
|
||||
*
|
||||
* Lots of helpful functions for use in massaging dates, specific to the
|
||||
* Gregorian calendar system. The values include both translated and
|
||||
* untranslated values.
|
||||
*
|
||||
* Untranslated values are useful as array keys and as css identifiers, and
|
||||
* should be listed in English.
|
||||
*
|
||||
* Translated values are useful for display to the user. All values that need
|
||||
* translation should be hard-coded and wrapped in t() so the translation system
|
||||
* will be able to process them.
|
||||
*/
|
||||
namespace Drupal\Core\Datetime;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
|
||||
/**
|
||||
* Defines Gregorian Calendar date values.
|
||||
*/
|
||||
class DateHelper {
|
||||
|
||||
/**
|
||||
* Constructs an untranslated array of month names.
|
||||
*
|
||||
* @return array
|
||||
* An array of month names.
|
||||
*/
|
||||
public static function monthNamesUntranslated() {
|
||||
// Force the key to use the correct month value, rather than
|
||||
// starting with zero.
|
||||
return array(
|
||||
1 => 'January',
|
||||
2 => 'February',
|
||||
3 => 'March',
|
||||
4 => 'April',
|
||||
5 => 'May',
|
||||
6 => 'June',
|
||||
7 => 'July',
|
||||
8 => 'August',
|
||||
9 => 'September',
|
||||
10 => 'October',
|
||||
11 => 'November',
|
||||
12 => 'December',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an untranslated array of abbreviated month names.
|
||||
*
|
||||
* @return array
|
||||
* An array of month names.
|
||||
*/
|
||||
public static function monthNamesAbbrUntranslated() {
|
||||
// Force the key to use the correct month value, rather than
|
||||
// starting with zero.
|
||||
return array(
|
||||
1 => 'Jan',
|
||||
2 => 'Feb',
|
||||
3 => 'Mar',
|
||||
4 => 'Apr',
|
||||
5 => 'May',
|
||||
6 => 'Jun',
|
||||
7 => 'Jul',
|
||||
8 => 'Aug',
|
||||
9 => 'Sep',
|
||||
10 => 'Oct',
|
||||
11 => 'Nov',
|
||||
12 => 'Dec',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a translated array of month names.
|
||||
*
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of month names.
|
||||
*/
|
||||
public static function monthNames($required = FALSE) {
|
||||
// Force the key to use the correct month value, rather than
|
||||
// starting with zero.
|
||||
$monthnames = array(
|
||||
1 => t('January', array(), array('context' => 'Long month name')),
|
||||
2 => t('February', array(), array('context' => 'Long month name')),
|
||||
3 => t('March', array(), array('context' => 'Long month name')),
|
||||
4 => t('April', array(), array('context' => 'Long month name')),
|
||||
5 => t('May', array(), array('context' => 'Long month name')),
|
||||
6 => t('June', array(), array('context' => 'Long month name')),
|
||||
7 => t('July', array(), array('context' => 'Long month name')),
|
||||
8 => t('August', array(), array('context' => 'Long month name')),
|
||||
9 => t('September', array(), array('context' => 'Long month name')),
|
||||
10 => t('October', array(), array('context' => 'Long month name')),
|
||||
11 => t('November', array(), array('context' => 'Long month name')),
|
||||
12 => t('December', array(), array('context' => 'Long month name')),
|
||||
);
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $monthnames : $monthnames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a translated array of month name abbreviations
|
||||
*
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of month abbreviations.
|
||||
*/
|
||||
public static function monthNamesAbbr($required = FALSE) {
|
||||
// Force the key to use the correct month value, rather than
|
||||
// starting with zero.
|
||||
$monthnames = array(
|
||||
1 => t('Jan', array(), array('context' => 'Abbreviated month name')),
|
||||
2 => t('Feb', array(), array('context' => 'Abbreviated month name')),
|
||||
3 => t('Mar', array(), array('context' => 'Abbreviated month name')),
|
||||
4 => t('Apr', array(), array('context' => 'Abbreviated month name')),
|
||||
5 => t('May', array(), array('context' => 'Abbreviated month name')),
|
||||
6 => t('Jun', array(), array('context' => 'Abbreviated month name')),
|
||||
7 => t('Jul', array(), array('context' => 'Abbreviated month name')),
|
||||
8 => t('Aug', array(), array('context' => 'Abbreviated month name')),
|
||||
9 => t('Sep', array(), array('context' => 'Abbreviated month name')),
|
||||
10 => t('Oct', array(), array('context' => 'Abbreviated month name')),
|
||||
11 => t('Nov', array(), array('context' => 'Abbreviated month name')),
|
||||
12 => t('Dec', array(), array('context' => 'Abbreviated month name')),
|
||||
);
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $monthnames : $monthnames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an untranslated array of week days.
|
||||
*
|
||||
* @return array
|
||||
* An array of week day names
|
||||
*/
|
||||
public static function weekDaysUntranslated() {
|
||||
return array(
|
||||
'Sunday',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a translated array of week names.
|
||||
*
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of week day names
|
||||
*/
|
||||
public static function weekDays($required = FALSE) {
|
||||
$weekdays = array(
|
||||
t('Sunday'),
|
||||
t('Monday'),
|
||||
t('Tuesday'),
|
||||
t('Wednesday'),
|
||||
t('Thursday'),
|
||||
t('Friday'),
|
||||
t('Saturday'),
|
||||
);
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $weekdays : $weekdays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a translated array of week day abbreviations.
|
||||
*
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of week day abbreviations
|
||||
*/
|
||||
public static function weekDaysAbbr($required = FALSE) {
|
||||
$weekdays = array(
|
||||
t('Sun', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Mon', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Tue', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Wed', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Thu', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Fri', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Sat', array(), array('context' => 'Abbreviated weekday')),
|
||||
);
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $weekdays : $weekdays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a translated array of 2-letter week day abbreviations.
|
||||
*
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of week day 2 letter abbreviations
|
||||
*/
|
||||
public static function weekDaysAbbr2($required = FALSE) {
|
||||
$weekdays = array(
|
||||
t('Su', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Mo', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Tu', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('We', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Th', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Fr', array(), array('context' => 'Abbreviated weekday')),
|
||||
t('Sa', array(), array('context' => 'Abbreviated weekday')),
|
||||
);
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $weekdays : $weekdays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a translated array of 1-letter week day abbreviations.
|
||||
*
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of week day 1 letter abbreviations
|
||||
*/
|
||||
public static function weekDaysAbbr1($required = FALSE) {
|
||||
$weekdays = array(
|
||||
t('S', array(), array('context' => 'Abbreviated 1 letter weekday Sunday')),
|
||||
t('M', array(), array('context' => 'Abbreviated 1 letter weekday Monday')),
|
||||
t('T', array(), array('context' => 'Abbreviated 1 letter weekday Tuesday')),
|
||||
t('W', array(), array('context' => 'Abbreviated 1 letter weekday Wednesday')),
|
||||
t('T', array(), array('context' => 'Abbreviated 1 letter weekday Thursday')),
|
||||
t('F', array(), array('context' => 'Abbreviated 1 letter weekday Friday')),
|
||||
t('S', array(), array('context' => 'Abbreviated 1 letter weekday Saturday')),
|
||||
);
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $weekdays : $weekdays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders weekdays to match the first day of the week.
|
||||
*
|
||||
* @param array $weekdays
|
||||
* An array of weekdays.
|
||||
*
|
||||
* @return array
|
||||
* An array of weekdays reordered to match the first day of the week.
|
||||
*/
|
||||
public static function weekDaysOrdered($weekdays) {
|
||||
$first_day = \Drupal::config('system.date')->get('first_day');
|
||||
if ($first_day > 0) {
|
||||
for ($i = 1; $i <= $first_day; $i++) {
|
||||
$last = array_shift($weekdays);
|
||||
array_push($weekdays, $last);
|
||||
}
|
||||
}
|
||||
return $weekdays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an array of years in a specified range.
|
||||
*
|
||||
* @param int $min
|
||||
* (optional) The minimum year in the array. Defaults to zero.
|
||||
* @param int $max
|
||||
* (optional) The maximum year in the array. Defaults to zero.
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of years in the selected range.
|
||||
*/
|
||||
public static function years($min = 0, $max = 0, $required = FALSE) {
|
||||
// Ensure $min and $max are valid values.
|
||||
if (empty($min)) {
|
||||
$min = intval(date('Y', REQUEST_TIME) - 3);
|
||||
}
|
||||
if (empty($max)) {
|
||||
$max = intval(date('Y', REQUEST_TIME) + 3);
|
||||
}
|
||||
$none = array('' => '');
|
||||
$range = range($min, $max);
|
||||
$range = array_combine($range, $range);
|
||||
return !$required ? $none + $range : $range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an array of days in a month.
|
||||
*
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
* @param int $month
|
||||
* (optional) The month in which to find the number of days. Defaults to
|
||||
* NULL.
|
||||
* @param int $year
|
||||
* (optional) The year in which to find the number of days. Defaults to
|
||||
* NULL.
|
||||
*
|
||||
* @return array
|
||||
* An array of days for the selected month.
|
||||
*/
|
||||
public static function days($required = FALSE, $month = NULL, $year = NULL) {
|
||||
// If we have a month and year, find the right last day of the month.
|
||||
if (!empty($month) && !empty($year)) {
|
||||
$date = new DrupalDateTime($year . '-' . $month . '-01 00:00:00', 'UTC');
|
||||
$max = $date->format('t');
|
||||
}
|
||||
// If there is no month and year given, default to 31.
|
||||
if (empty($max)) {
|
||||
$max = 31;
|
||||
}
|
||||
$none = array('' => '');
|
||||
$range = range(1, $max);
|
||||
$range = array_combine($range, $range);
|
||||
return !$required ? $none + $range : $range;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an array of hours.
|
||||
*
|
||||
* @param string $format
|
||||
* (optional) A date format string that indicates the format to use for the
|
||||
* hours. Defaults to 'H'.
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of hours in the selected format.
|
||||
*/
|
||||
public static function hours($format = 'H', $required = FALSE) {
|
||||
$hours = array();
|
||||
if ($format == 'h' || $format == 'g') {
|
||||
$min = 1;
|
||||
$max = 12;
|
||||
}
|
||||
else {
|
||||
$min = 0;
|
||||
$max = 23;
|
||||
}
|
||||
for ($i = $min; $i <= $max; $i++) {
|
||||
$formatted = ($format == 'H' || $format == 'h') ? DrupalDateTime::datePad($i) : $i;
|
||||
$hours[$i] = $formatted;
|
||||
}
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $hours : $hours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an array of minutes.
|
||||
*
|
||||
* @param string $format
|
||||
* (optional) A date format string that indicates the format to use for the
|
||||
* minutes. Defaults to 'i'.
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
* @param int $increment
|
||||
* An integer value to increment the values. Defaults to 1.
|
||||
*
|
||||
* @return array
|
||||
* An array of minutes in the selected format.
|
||||
*/
|
||||
public static function minutes($format = 'i', $required = FALSE, $increment = 1) {
|
||||
$minutes = array();
|
||||
// Ensure $increment has a value so we don't loop endlessly.
|
||||
if (empty($increment)) {
|
||||
$increment = 1;
|
||||
}
|
||||
for ($i = 0; $i < 60; $i += $increment) {
|
||||
$formatted = $format == 'i' ? DrupalDateTime::datePad($i) : $i;
|
||||
$minutes[$i] = $formatted;
|
||||
}
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $minutes : $minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an array of seconds.
|
||||
*
|
||||
* @param string $format
|
||||
* (optional) A date format string that indicates the format to use for the
|
||||
* seconds. Defaults to 's'.
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
* @param int $increment
|
||||
* An integer value to increment the values. Defaults to 1.
|
||||
*
|
||||
* @return array
|
||||
* An array of seconds in the selected format.
|
||||
*/
|
||||
public static function seconds($format = 's', $required = FALSE, $increment = 1) {
|
||||
$seconds = array();
|
||||
// Ensure $increment has a value so we don't loop endlessly.
|
||||
if (empty($increment)) {
|
||||
$increment = 1;
|
||||
}
|
||||
for ($i = 0; $i < 60; $i += $increment) {
|
||||
$formatted = $format == 's' ? DrupalDateTime::datePad($i) : $i;
|
||||
$seconds[$i] = $formatted;
|
||||
}
|
||||
$none = array('' => '');
|
||||
return !$required ? $none + $seconds : $seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an array of AM and PM options.
|
||||
*
|
||||
* @param bool $required
|
||||
* (optional) If FALSE, the returned array will include a blank value.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of AM and PM options.
|
||||
*/
|
||||
public static function ampm($required = FALSE) {
|
||||
$none = array('' => '');
|
||||
$ampm = array(
|
||||
'am' => t('am', array(), array('context' => 'ampm')),
|
||||
'pm' => t('pm', array(), array('context' => 'ampm')),
|
||||
);
|
||||
return !$required ? $none + $ampm : $ampm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the number of days in a month for a date.
|
||||
*
|
||||
* @param mixed $date
|
||||
* (optional) A date object, timestamp, or a date string.
|
||||
* Defaults to NULL, which means to use the current date.
|
||||
*
|
||||
* @return int
|
||||
* The number of days in the month.
|
||||
*/
|
||||
public static function daysInMonth($date = NULL) {
|
||||
if (!$date instanceOf DrupalDateTime) {
|
||||
$date = new DrupalDateTime($date);
|
||||
}
|
||||
if (!$date->hasErrors()) {
|
||||
return $date->format('t');
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the number of days in a year for a date.
|
||||
*
|
||||
* @param mixed $date
|
||||
* (optional) A date object, timestamp, or a date string.
|
||||
* Defaults to NULL, which means to use the current date.
|
||||
*
|
||||
* @return int
|
||||
* The number of days in the year.
|
||||
*/
|
||||
public static function daysInYear($date = NULL) {
|
||||
if (!$date instanceOf DrupalDateTime) {
|
||||
$date = new DrupalDateTime($date);
|
||||
}
|
||||
if (!$date->hasErrors()) {
|
||||
if ($date->format('L')) {
|
||||
return 366;
|
||||
}
|
||||
else {
|
||||
return 365;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns day of week for a given date (0 = Sunday).
|
||||
*
|
||||
* @param mixed $date
|
||||
* (optional) A date object, timestamp, or a date string.
|
||||
* Defaults to NULL, which means use the current date.
|
||||
*
|
||||
* @return int
|
||||
* The number of the day in the week.
|
||||
*/
|
||||
public static function dayOfWeek($date = NULL) {
|
||||
if (!$date instanceOf DrupalDateTime) {
|
||||
$date = new DrupalDateTime($date);
|
||||
}
|
||||
if (!$date->hasErrors()) {
|
||||
return $date->format('w');
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns translated name of the day of week for a given date.
|
||||
*
|
||||
* @param mixed $date
|
||||
* (optional) A date object, timestamp, or a date string.
|
||||
* Defaults to NULL, which means use the current date.
|
||||
* @param string $abbr
|
||||
* (optional) Whether to return the abbreviated name for that day.
|
||||
* Defaults to TRUE.
|
||||
*
|
||||
* @return string
|
||||
* The name of the day in the week for that date.
|
||||
*/
|
||||
public static function dayOfWeekName($date = NULL, $abbr = TRUE) {
|
||||
if (!$date instanceOf DrupalDateTime) {
|
||||
$date = new DrupalDateTime($date);
|
||||
}
|
||||
$dow = self::dayOfWeek($date);
|
||||
$days = $abbr ? self::weekDaysAbbr() : self::weekDays();
|
||||
return $days[$dow];
|
||||
}
|
||||
|
||||
}
|
||||
|
140
core/lib/Drupal/Core/Datetime/DrupalDateTime.php
Normal file
140
core/lib/Drupal/Core/Datetime/DrupalDateTime.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\DrupalDateTime.
|
||||
*/
|
||||
namespace Drupal\Core\Datetime;
|
||||
|
||||
use Drupal\Component\Datetime\DateTimePlus;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Extends DateTimePlus().
|
||||
*
|
||||
* This class extends the basic component and adds in Drupal-specific
|
||||
* handling, like translation of the format() method.
|
||||
*
|
||||
* Static methods in base class can also be used to create DrupalDateTime objects.
|
||||
* For example:
|
||||
*
|
||||
* DrupalDateTime::createFromArray( array('year' => 2010, 'month' => 9, 'day' => 28) )
|
||||
*
|
||||
* @see \Drupal/Component/Datetime/DateTimePlus.php
|
||||
*/
|
||||
class DrupalDateTime extends DateTimePlus {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Format string translation cache.
|
||||
*
|
||||
*/
|
||||
protected $formatTranslationCache;
|
||||
|
||||
/**
|
||||
* Constructs a date object.
|
||||
*
|
||||
* @param string $time
|
||||
* A DateTime object, a date/input_time_adjusted string, a unix timestamp.
|
||||
* Defaults to 'now'.
|
||||
* @param mixed $timezone
|
||||
* PHP DateTimeZone object, string or NULL allowed.
|
||||
* Defaults to NULL.
|
||||
* @param array $settings
|
||||
* - validate_format: (optional) Boolean choice to validate the
|
||||
* created date using the input format. The format used in
|
||||
* createFromFormat() allows slightly different values than format().
|
||||
* Using an input format that works in both functions makes it
|
||||
* possible to a validation step to confirm that the date created
|
||||
* from a format string exactly matches the input. This option
|
||||
* indicates the format can be used for validation. Defaults to TRUE.
|
||||
* - langcode: (optional) Used to control the result of the format() method.
|
||||
* Defaults to NULL.
|
||||
* - debug: (optional) Boolean choice to leave debug values in the
|
||||
* date object for debugging purposes. Defaults to FALSE.
|
||||
*/
|
||||
public function __construct($time = 'now', $timezone = NULL, $settings = array()) {
|
||||
if (!isset($settings['langcode'])) {
|
||||
$settings['langcode'] = \Drupal::languageManager()->getCurrentLanguage()->getId();
|
||||
}
|
||||
|
||||
// Instantiate the parent class.
|
||||
parent::__construct($time, $timezone, $settings);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides format().
|
||||
*
|
||||
* @param string $format
|
||||
* A format string using either PHP's date().
|
||||
* @param array $settings
|
||||
* - timezone: (optional) String timezone name. Defaults to the timezone
|
||||
* of the date object.
|
||||
* - langcode: (optional) String two letter language code used to control
|
||||
* the result of the format() method. Defaults to NULL.
|
||||
*
|
||||
* @return string
|
||||
* The formatted value of the date.
|
||||
*/
|
||||
public function format($format, $settings = array()) {
|
||||
$langcode = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
|
||||
$value = '';
|
||||
// Format the date and catch errors.
|
||||
try {
|
||||
// Encode markers that should be translated. 'A' becomes
|
||||
// '\xEF\AA\xFF'. xEF and xFF are invalid UTF-8 sequences,
|
||||
// and we assume they are not in the input string.
|
||||
// Paired backslashes are isolated to prevent errors in
|
||||
// read-ahead evaluation. The read-ahead expression ensures that
|
||||
// A matches, but not \A.
|
||||
$format = preg_replace(array('/\\\\\\\\/', '/(?<!\\\\)([AaeDlMTF])/'), array("\xEF\\\\\\\\\xFF", "\xEF\\\\\$1\$1\xFF"), $format);
|
||||
|
||||
// Call date_format().
|
||||
$format = parent::format($format, $settings);
|
||||
|
||||
// Translates a formatted date string.
|
||||
$translation_callback = function($matches) use ($langcode) {
|
||||
$code = $matches[1];
|
||||
$string = $matches[2];
|
||||
if (!isset($this->formatTranslationCache[$langcode][$code][$string])) {
|
||||
$options = array('langcode' => $langcode);
|
||||
if ($code == 'F') {
|
||||
$options['context'] = 'Long month name';
|
||||
}
|
||||
|
||||
if ($code == '') {
|
||||
$this->formatTranslationCache[$langcode][$code][$string] = $string;
|
||||
}
|
||||
else {
|
||||
$this->formatTranslationCache[$langcode][$code][$string] = $this->t($string, array(), $options);
|
||||
}
|
||||
}
|
||||
return $this->formatTranslationCache[$langcode][$code][$string];
|
||||
};
|
||||
|
||||
// Translate the marked sequences.
|
||||
$value = preg_replace_callback('/\xEF([AaeDlMTF]?)(.*?)\xFF/', $translation_callback, $format);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->errors[] = $e->getMessage();
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
78
core/lib/Drupal/Core/Datetime/Element/DateElementBase.php
Normal file
78
core/lib/Drupal/Core/Datetime/Element/DateElementBase.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\Element\DateElementBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Datetime\Element;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
|
||||
/**
|
||||
* Provides a base class for date elements.
|
||||
*/
|
||||
abstract class DateElementBase extends FormElement {
|
||||
|
||||
/**
|
||||
* Specifies the start and end year to use as a date range.
|
||||
*
|
||||
* Handles a string like -3:+3 or 2001:2010 to describe a dynamic range of
|
||||
* minimum and maximum years to use in a date selector.
|
||||
*
|
||||
* Centers the range around the current year, if any, but expands it far enough
|
||||
* so it will pick up the year value in the field in case the value in the field
|
||||
* is outside the initial range.
|
||||
*
|
||||
* @param string $string
|
||||
* A min and max year string like '-3:+1' or '2000:2010' or '2000:+3'.
|
||||
* @param object $date
|
||||
* (optional) A date object to test as a default value. Defaults to NULL.
|
||||
*
|
||||
* @return array
|
||||
* A numerically indexed array, containing the minimum and maximum year
|
||||
* described by this pattern.
|
||||
*/
|
||||
protected static function datetimeRangeYears($string, $date = NULL) {
|
||||
$datetime = new DrupalDateTime();
|
||||
$this_year = $datetime->format('Y');
|
||||
list($min_year, $max_year) = explode(':', $string);
|
||||
|
||||
// Valid patterns would be -5:+5, 0:+1, 2008:2010.
|
||||
$plus_pattern = '@[\+|\-][0-9]{1,4}@';
|
||||
$year_pattern = '@^[0-9]{4}@';
|
||||
if (!preg_match($year_pattern, $min_year, $matches)) {
|
||||
if (preg_match($plus_pattern, $min_year, $matches)) {
|
||||
$min_year = $this_year + $matches[0];
|
||||
}
|
||||
else {
|
||||
$min_year = $this_year;
|
||||
}
|
||||
}
|
||||
if (!preg_match($year_pattern, $max_year, $matches)) {
|
||||
if (preg_match($plus_pattern, $max_year, $matches)) {
|
||||
$max_year = $this_year + $matches[0];
|
||||
}
|
||||
else {
|
||||
$max_year = $this_year;
|
||||
}
|
||||
}
|
||||
// We expect the $min year to be less than the $max year. Some custom values
|
||||
// for -99:+99 might not obey that.
|
||||
if ($min_year > $max_year) {
|
||||
$temp = $max_year;
|
||||
$max_year = $min_year;
|
||||
$min_year = $temp;
|
||||
}
|
||||
// If there is a current value, stretch the range to include it.
|
||||
$value_year = $date instanceOf DrupalDateTime ? $date->format('Y') : '';
|
||||
if (!empty($value_year)) {
|
||||
$min_year = min($value_year, $min_year);
|
||||
$max_year = max($value_year, $max_year);
|
||||
}
|
||||
return array($min_year, $max_year);
|
||||
}
|
||||
|
||||
}
|
||||
|
362
core/lib/Drupal/Core/Datetime/Element/Datelist.php
Normal file
362
core/lib/Drupal/Core/Datetime/Element/Datelist.php
Normal file
|
@ -0,0 +1,362 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\Element\Datelist.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Datetime\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Datetime\DateHelper;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a datelist element.
|
||||
*
|
||||
* @FormElement("datelist")
|
||||
*/
|
||||
class Datelist extends DateElementBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return array(
|
||||
'#input' => TRUE,
|
||||
'#element_validate' => array(
|
||||
array($class, 'validateDatelist'),
|
||||
),
|
||||
'#process' => array(
|
||||
array($class, 'processDatelist'),
|
||||
),
|
||||
'#theme' => 'datetime_form',
|
||||
'#theme_wrappers' => array('datetime_wrapper'),
|
||||
'#date_part_order' => array('year', 'month', 'day', 'hour', 'minute'),
|
||||
'#date_year_range' => '1900:2050',
|
||||
'#date_increment' => 1,
|
||||
'#date_date_callbacks' => array(),
|
||||
'#date_timezone' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Validates the date type to adjust 12 hour time and prevent invalid dates.
|
||||
* If the date is valid, the date is set in the form.
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
$parts = $element['#date_part_order'];
|
||||
$increment = $element['#date_increment'];
|
||||
|
||||
$date = NULL;
|
||||
if ($input !== FALSE) {
|
||||
$return = $input;
|
||||
if (isset($input['ampm'])) {
|
||||
if ($input['ampm'] == 'pm' && $input['hour'] < 12) {
|
||||
$input['hour'] += 12;
|
||||
}
|
||||
elseif ($input['ampm'] == 'am' && $input['hour'] == 12) {
|
||||
$input['hour'] -= 12;
|
||||
}
|
||||
unset($input['ampm']);
|
||||
}
|
||||
$timezone = !empty($element['#date_timezone']) ? $element['#date_timezone'] : NULL;
|
||||
$date = DrupalDateTime::createFromArray($input, $timezone);
|
||||
if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
|
||||
static::incrementRound($date, $increment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$return = array_fill_keys($parts, '');
|
||||
if (!empty($element['#default_value'])) {
|
||||
$date = $element['#default_value'];
|
||||
if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
|
||||
static::incrementRound($date, $increment);
|
||||
foreach ($parts as $part) {
|
||||
switch ($part) {
|
||||
case 'day':
|
||||
$format = 'j';
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
$format = 'n';
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
$format = 'Y';
|
||||
break;
|
||||
|
||||
case 'hour':
|
||||
$format = in_array('ampm', $element['#date_part_order']) ? 'g': 'G';
|
||||
break;
|
||||
|
||||
case 'minute':
|
||||
$format = 'i';
|
||||
break;
|
||||
|
||||
case 'second':
|
||||
$format = 's';
|
||||
break;
|
||||
|
||||
case 'ampm':
|
||||
$format = 'a';
|
||||
break;
|
||||
|
||||
default:
|
||||
$format = '';
|
||||
|
||||
}
|
||||
$return[$part] = $date->format($format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$return['object'] = $date;
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback for a datelist element.
|
||||
*
|
||||
* If the date is valid, the date object created from the user input is set in
|
||||
* the form for use by the caller. The work of compiling the user input back
|
||||
* into a date object is handled by the value callback, so we can use it here.
|
||||
* We also have the raw input available for validation testing.
|
||||
*
|
||||
* @param array $element
|
||||
* The element being processed.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array $complete_form
|
||||
* The complete form structure.
|
||||
*/
|
||||
public static function validateDatelist(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$input_exists = FALSE;
|
||||
$input = NestedArray::getValue($form_state->getValues(), $element['#parents'], $input_exists);
|
||||
if ($input_exists) {
|
||||
|
||||
// If there's empty input and the field is not required, set it to empty.
|
||||
if (empty($input['year']) && empty($input['month']) && empty($input['day']) && !$element['#required']) {
|
||||
$form_state->setValueForElement($element, NULL);
|
||||
}
|
||||
// If there's empty input and the field is required, set an error.
|
||||
elseif (empty($input['year']) && empty($input['month']) && empty($input['day']) && $element['#required']) {
|
||||
$form_state->setError($element, t('The %field date is required.'));
|
||||
}
|
||||
else {
|
||||
// If the input is valid, set it.
|
||||
$date = $input['object'];
|
||||
if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
|
||||
$form_state->setValueForElement($element, $date);
|
||||
}
|
||||
// If the input is invalid, set an error.
|
||||
else {
|
||||
$form_state->setError($element, t('The %field date is invalid.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds minutes and seconds to nearest requested value.
|
||||
*
|
||||
* @param $date
|
||||
*
|
||||
* @param $increment
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected static function incrementRound(&$date, $increment) {
|
||||
// Round minutes and seconds, if necessary.
|
||||
if ($date instanceOf DrupalDateTime && $increment > 1) {
|
||||
$day = intval(date_format($date, 'j'));
|
||||
$hour = intval(date_format($date, 'H'));
|
||||
$second = intval(round(intval(date_format($date, 's')) / $increment) * $increment);
|
||||
$minute = intval(date_format($date, 'i'));
|
||||
if ($second == 60) {
|
||||
$minute += 1;
|
||||
$second = 0;
|
||||
}
|
||||
$minute = intval(round($minute / $increment) * $increment);
|
||||
if ($minute == 60) {
|
||||
$hour += 1;
|
||||
$minute = 0;
|
||||
}
|
||||
date_time_set($date, $hour, $minute, $second);
|
||||
if ($hour == 24) {
|
||||
$day += 1;
|
||||
$year = date_format($date, 'Y');
|
||||
$month = date_format($date, 'n');
|
||||
date_date_set($date, $year, $month, $day);
|
||||
}
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
||||
}
|
433
core/lib/Drupal/Core/Datetime/Element/Datetime.php
Normal file
433
core/lib/Drupal/Core/Datetime/Element/Datetime.php
Normal file
|
@ -0,0 +1,433 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\Element\Datetime.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Datetime\Element;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Datetime\Entity\DateFormat;
|
||||
|
||||
/**
|
||||
* Provides a datetime element.
|
||||
*
|
||||
* @FormElement("datetime")
|
||||
*/
|
||||
class Datetime extends DateElementBase {
|
||||
|
||||
/**
|
||||
* @var \DateTimeInterface
|
||||
*/
|
||||
protected static $dateExample;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$date_format = '';
|
||||
$time_format = '';
|
||||
// Date formats cannot be loaded during install or update.
|
||||
if (!defined('MAINTENANCE_MODE')) {
|
||||
if ($date_format_entity = DateFormat::load('html_date')) {
|
||||
/** @var $date_format_entity \Drupal\Core\Datetime\DateFormatInterface */
|
||||
$date_format = $date_format_entity->getPattern();
|
||||
}
|
||||
if ($time_format_entity = DateFormat::load('html_time')) {
|
||||
/** @var $time_format_entity \Drupal\Core\Datetime\DateFormatInterface */
|
||||
$time_format = $time_format_entity->getPattern();
|
||||
}
|
||||
}
|
||||
|
||||
$class = get_class($this);
|
||||
return array(
|
||||
'#input' => TRUE,
|
||||
'#element_validate' => array(
|
||||
array($class, 'validateDatetime'),
|
||||
),
|
||||
'#process' => array(
|
||||
array($class, 'processDatetime'),
|
||||
array($class, 'processGroup'),
|
||||
),
|
||||
'#pre_render' => array(
|
||||
array($class, 'preRenderGroup'),
|
||||
),
|
||||
'#theme' => 'datetime_form',
|
||||
'#theme_wrappers' => array('datetime_wrapper'),
|
||||
'#date_date_format' => $date_format,
|
||||
'#date_date_element' => 'date',
|
||||
'#date_date_callbacks' => array(),
|
||||
'#date_time_format' => $time_format,
|
||||
'#date_time_element' => 'time',
|
||||
'#date_time_callbacks' => array(),
|
||||
'#date_year_range' => '1900:2050',
|
||||
'#date_increment' => 1,
|
||||
'#date_timezone' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
if ($input !== FALSE) {
|
||||
$date_input = $element['#date_date_element'] != 'none' && !empty($input['date']) ? $input['date'] : '';
|
||||
$time_input = $element['#date_time_element'] != 'none' && !empty($input['time']) ? $input['time'] : '';
|
||||
$date_format = $element['#date_date_element'] != 'none' ? static::getHtml5DateFormat($element) : '';
|
||||
$time_format = $element['#date_time_element'] != 'none' ? static::getHtml5TimeFormat($element) : '';
|
||||
$timezone = !empty($element['#date_timezone']) ? $element['#date_timezone'] : NULL;
|
||||
|
||||
// Seconds will be omitted in a post in case there's no entry.
|
||||
if (!empty($time_input) && strlen($time_input) == 5) {
|
||||
$time_input .= ':00';
|
||||
}
|
||||
|
||||
try {
|
||||
$date_time_format = trim($date_format . ' ' . $time_format);
|
||||
$date_time_input = trim($date_input . ' ' . $time_input);
|
||||
$date = DrupalDateTime::createFromFormat($date_time_format, $date_time_input, $timezone);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$date = NULL;
|
||||
}
|
||||
$input = array(
|
||||
'date' => $date_input,
|
||||
'time' => $time_input,
|
||||
'object' => $date,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$date = $element['#default_value'];
|
||||
if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
|
||||
$input = array(
|
||||
'date' => $date->format($element['#date_date_format']),
|
||||
'time' => $date->format($element['#date_time_format']),
|
||||
'object' => $date,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$input = array(
|
||||
'date' => '',
|
||||
'time' => '',
|
||||
'object' => NULL,
|
||||
);
|
||||
}
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback for a datetime element.
|
||||
*
|
||||
* If the date is valid, the date object created from the user input is set in
|
||||
* the form for use by the caller. The work of compiling the user input back
|
||||
* into a date object is handled by the value callback, so we can use it here.
|
||||
* We also have the raw input available for validation testing.
|
||||
*
|
||||
* @param array $element
|
||||
* The form element whose value is being validated.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param array $complete_form
|
||||
* The complete form structure.
|
||||
*/
|
||||
public static function validateDatetime(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$input_exists = FALSE;
|
||||
$input = NestedArray::getValue($form_state->getValues(), $element['#parents'], $input_exists);
|
||||
if ($input_exists) {
|
||||
|
||||
$title = !empty($element['#title']) ? $element['#title'] : '';
|
||||
$date_format = $element['#date_date_element'] != 'none' ? static::getHtml5DateFormat($element) : '';
|
||||
$time_format = $element['#date_time_element'] != 'none' ? static::getHtml5TimeFormat($element) : '';
|
||||
$format = trim($date_format . ' ' . $time_format);
|
||||
|
||||
// If there's empty input and the field is not required, set it to empty.
|
||||
if (empty($input['date']) && empty($input['time']) && !$element['#required']) {
|
||||
$form_state->setValueForElement($element, NULL);
|
||||
}
|
||||
// If there's empty input and the field is required, set an error. A
|
||||
// reminder of the required format in the message provides a good UX.
|
||||
elseif (empty($input['date']) && empty($input['time']) && $element['#required']) {
|
||||
$form_state->setError($element, t('The %field date is required. Please enter a date in the format %format.', array('%field' => $title, '%format' => static::formatExample($format))));
|
||||
}
|
||||
else {
|
||||
// If the date is valid, set it.
|
||||
$date = $input['object'];
|
||||
if ($date instanceof DrupalDateTime && !$date->hasErrors()) {
|
||||
$form_state->setValueForElement($element, $date);
|
||||
}
|
||||
// If the date is invalid, set an error. A reminder of the required
|
||||
// format in the message provides a good UX.
|
||||
else {
|
||||
$form_state->setError($element, t('The %field date is invalid. Please enter a date in the format %format.', array('%field' => $title, '%format' => static::formatExample($format))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an example for a date format.
|
||||
*
|
||||
* This is centralized for a consistent method of creating these examples.
|
||||
*
|
||||
* @param string $format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function formatExample($format) {
|
||||
if (!static::$dateExample) {
|
||||
static::$dateExample = new DrupalDateTime();
|
||||
}
|
||||
return static::$dateExample->format($format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the right format for a HTML5 date element.
|
||||
*
|
||||
* The format is important because these elements will not work with any other
|
||||
* format.
|
||||
*
|
||||
* @param string $element
|
||||
* The $element to assess.
|
||||
*
|
||||
* @return string
|
||||
* Returns the right format for the date element, or the original format
|
||||
* if this is not a HTML5 element.
|
||||
*/
|
||||
protected static function getHtml5DateFormat($element) {
|
||||
switch ($element['#date_date_element']) {
|
||||
case 'date':
|
||||
return DateFormat::load('html_date')->getPattern();
|
||||
|
||||
case 'datetime':
|
||||
case 'datetime-local':
|
||||
return DateFormat::load('html_datetime')->getPattern();
|
||||
|
||||
default:
|
||||
return $element['#date_date_format'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the right format for a HTML5 time element.
|
||||
*
|
||||
* The format is important because these elements will not work with any other
|
||||
* format.
|
||||
*
|
||||
* @param string $element
|
||||
* The $element to assess.
|
||||
*
|
||||
* @return string
|
||||
* Returns the right format for the time element, or the original format
|
||||
* if this is not a HTML5 element.
|
||||
*/
|
||||
protected static function getHtml5TimeFormat($element) {
|
||||
switch ($element['#date_time_element']) {
|
||||
case 'time':
|
||||
return DateFormat::load('html_time')->getPattern();
|
||||
|
||||
default:
|
||||
return $element['#date_time_format'];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
108
core/lib/Drupal/Core/Datetime/Entity/DateFormat.php
Normal file
108
core/lib/Drupal/Core/Datetime/Entity/DateFormat.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\Entity\DateFormat.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Datetime\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Datetime\DateFormatInterface;
|
||||
|
||||
/**
|
||||
* Defines the Date Format configuration entity class.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "date_format",
|
||||
* label = @Translation("Date format"),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\system\DateFormatAccessControlHandler",
|
||||
* },
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label"
|
||||
* },
|
||||
* admin_permission = "administer site configuration",
|
||||
* list_cache_tags = { "rendered" },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "locked",
|
||||
* "pattern",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class DateFormat extends ConfigEntityBase implements DateFormatInterface {
|
||||
|
||||
/**
|
||||
* The date format machine name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the date format entity.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* The date format pattern.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pattern;
|
||||
|
||||
/**
|
||||
* The locked status of this date format.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $locked = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPattern() {
|
||||
return $this->pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPattern($pattern) {
|
||||
$this->pattern = $pattern;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isLocked() {
|
||||
return (bool) $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) {
|
||||
if ($a->isLocked() == $b->isLocked()) {
|
||||
$a_label = $a->label();
|
||||
$b_label = $b->label();
|
||||
return strnatcasecmp($a_label, $b_label);
|
||||
}
|
||||
return $a->isLocked() ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return ['rendered'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\Plugin\Field\FieldWidget\TimestampDatetimeWidget.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Datetime\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Datetime\Element\Datetime;
|
||||
use Drupal\Core\Datetime\Entity\DateFormat;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'datetime timestamp' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "datetime_timestamp",
|
||||
* label = @Translation("Datetime Timestamp"),
|
||||
* field_types = {
|
||||
* "timestamp",
|
||||
* "created",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class TimestampDatetimeWidget extends WidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$date_format = DateFormat::load('html_date')->getPattern();
|
||||
$time_format = DateFormat::load('html_time')->getPattern();
|
||||
$default_value = isset($items[$delta]->value) ? DrupalDateTime::createFromTimestamp($items[$delta]->value) : '';
|
||||
$element['value'] = $element + array(
|
||||
'#type' => 'datetime',
|
||||
'#default_value' => $default_value,
|
||||
'#date_year_range' => '1902:2037',
|
||||
);
|
||||
$element['value']['#description'] = $this->t('Format: %format. Leave blank to use the time of form submission.', array('%format' => Datetime::formatExample($date_format . ' ' . $time_format)));
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
|
||||
foreach ($values as &$item) {
|
||||
// @todo The structure is different whether access is denied or not, to
|
||||
// be fixed in https://www.drupal.org/node/2326533.
|
||||
if (isset($item['value']) && $item['value'] instanceof DrupalDateTime) {
|
||||
$date = $item['value'];
|
||||
}
|
||||
else if (isset($item['value']['object']) && $item['value']['object'] instanceof DrupalDateTime) {
|
||||
$date = $item['value']['object'];
|
||||
}
|
||||
else {
|
||||
$date = new DrupalDateTime();
|
||||
}
|
||||
$item['value'] = $date->getTimestamp();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue