134. 日期与时间

本篇讲述Drupal系统中和日期时间相关的内容

计时器:

系统提供了一个计时器程序(秒表),用于帮助计时或调式,在批处理、计划任务中都有使用,类如下:

\Drupal\Component\Utility\Timer

该计时器有个特点:在计时过程中可以反复暂停、继续开始计时,获取到的是总用时间

有三个静态方法,可在请求中全局使用,说明如下:

public static function start($name)

在内部创建一个计时器,并开始计时,参数为计时器名(任意字符串值,计时器标识符),无返回值,在调用暂停计时方法后可调用该方法继续计时

public static function read($name)

读取一个计时器所计时间总数(如有暂停,则暂停期间的时间不会计算),返回一个浮点数表示的时间,单位为毫秒(不是秒也不是微妙),精度为小数点后2位,如果之前没有设置过参数指定的计时器,那么将返回NULL

public static function stop($name)

暂停计时,暂停后可调用开始计算方法继续计时,暂停期内的时间不会被计算,返回一个数组,该数组是内部表示的计时器,有如下键:

time:所计时间的总数,毫秒为单位,精确到小数点后两位

count:一个整数,表示该计数器开启计时的次数

 

时间服务:

用于以oop方式提供系统常用的四个时间:请求时间、当前时间以及她们对应的微秒时间

服务iddatetime.time

类:Drupal\Component\Datetime\Time

获取方式:\Drupal::time()

该服务在创建测试时很有用,有4个可用方法:

public function getRequestTime()

得到当前请求的请求时间,也就是“$_SERVER['REQUEST_TIME'];”,整数时间戳,和以下值完全等同:

REQUEST_TIME:系统全局常量
$requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
$request_time = $request->server->get('REQUEST_TIME');

public function getRequestMicroTime()

得到当前请求的浮点时间,如:“1575421124.0707”精确到微秒,和以下值等同:

$_SERVER['REQUEST_TIME_FLOAT'];
$requestStack->getCurrentRequest()->server->get('REQUEST_TIME_FLOAT');
$request->server->get('REQUEST_TIME_FLOAT');

public function getCurrentTime()

当前时间,也就是函数time()

public function getCurrentMicroTime()

当前微秒时间,精确到微秒的浮点数,如“1575421778.3079”也就是函数microtime(TRUE)

 

Drupal日期时间类:

在组件中提供了如下类:

\Drupal\Component\Datetime\DateTimePlus

这是对PHP原始全局类“\DateTime”的进一步包装,提供了更多的灵活性和使用方法,示例如下:

$dateTimePlus=\Drupal\Component\Datetime\DateTimePlus::createFromTimestamp(time(),'Asia/Shanghai');
echo $dateTimePlus->render();

核心中提供了如下类:

\Drupal\Core\Datetime\DrupalDateTime

核心类继承自组件类,主要附加了时区处理和翻译功能,该类是drupal使用的主要日期时间类

 

日期辅助类:

类:\Drupal\Core\Datetime\DateHelper

该类提供了许多日期操作方面的辅助方法

public static function monthNamesUntranslated()

返回未翻译的长月名数组

public static function monthNamesAbbrUntranslated()

返回未翻译的短月名数组

public static function monthNames($required = FALSE)

返回已翻译的长月名

public static function monthNamesAbbr($required = FALSE)

返回已翻译的短月名

public static function weekDaysUntranslated()

返回未翻译周名数组

public static function weekDays($required = FALSE)

返回已翻译周名数组

public static function weekDaysAbbr($required = FALSE)

public static function weekDaysAbbr2($required = FALSE)

public static function weekDaysAbbr1($required = FALSE)

返回已翻译短周名数组

public static function weekDaysOrdered($weekdays)

依据周中设定的第一天重新排序周数组

public static function years($min = 0, $max = 0, $required = FALSE)

构造一个在指定范围的年数组

public static function days($required = FALSE, $month = NULL, $year = NULL)

根据指定的年、月构造天数组

public static function hours($format = 'H', $required = FALSE)

构造小时数组

public static function minutes($format = 'i', $required = FALSE, $increment = 1)

构造分钟数组

public static function seconds($format = 's', $required = FALSE, $increment = 1)

构造秒数组

public static function ampm($required = FALSE)

构造上下午选项数组

public static function daysInMonth($date = NULL)

返回给定时间所在月的天数

public static function daysInYear($date = NULL)

返回给定时间所在年的天数

public static function dayOfWeek($date = NULL)

返回给定时间是一周中的第几天,星期日将当做第0天,返回0,该方法不参考系统设置的星期第一天

public static function dayOfWeekName($date = NULL, $abbr = TRUE)

返回给定日期的已翻译星期名

 

时间相关函数:

drupal_get_user_timezone()

得到当前用户的时区,在用户是登录用户且系统配置为允许用户自定义时区时,如果用户有设定时区那么返回这个时区,否则返回系统默认时区

 

时间相关配置:

系统时间相关配置在以下页面设置:

/admin/config/regional/settings

数据储存于以下配置中:

$config = \Drupal::config('system.date');

说明如下:

country:
  default: '' #国家代码,中国为“CN”
first_day: 0 #一周中的第一天
timezone:
  default: '' #系统默认时区
  user:
    configurable: true #用户是否可以设置他们自己的时区
    warn: false #如果用户没有设置时区,则在登录时是否提醒设置
    default: 0 #新用户的默认时区,0为系统默认时区,1空时区,2注册时选择

 

日期格式实体

是一个配置实体,用于储存日期时间的格式化信息,当配置时间显示时提供选项支持,定义如下:

实体类型iddate_format

实体类:\Drupal\Core\Datetime\Entity\DateFormat

相关表单:

添加:Drupal\system\Form\DateFormatAddForm

编辑:Drupal\system\Form\DateFormatEditForm

删除:Drupal\system\Form\DateFormatDeleteForm

列表构建器:

Drupal\system\DateFormatListBuilder

该实体的实现比较简单,这里仅说明如下几点:

label:即列表页中的名称

id:即添加表单中的机读名称

pattern:即列表页中所示的模式,换句话说即PHP函数date的第一个参数

locked:指示在用户UI接口中是否允许更新或删除

管理该实体的后台地址为:

  /admin/config/regional/date-time

在程序中,日期格式化器服务可直接使用该实体去格式化时间,示例如下:

        $entity=\Drupal::entityManager()->getStorage('date_format')->load('html_datetime');
        echo \Drupal::service('date.formatter')->format(time(), $entity->id());

系统默认提供的格式类型如下“idlable:示例”:


Array
(
    [html_datetime] => Array
        (
            [0] => 名称:HTML 日期与时间
            [1] => 模式:Y-m-d\TH:i:sO
            [2] => 示例:2019-12-04T15:03:17+0800
        )

    [fallback] => Array
        (
            [0] => 名称:回滚日期格式
            [1] => 模式:D, m/d/Y - H:i
            [2] => 示例:周三, 12/04/2019 - 15:03
        )

    [html_date] => Array
        (
            [0] => 名称:HTML 日期
            [1] => 模式:Y-m-d
            [2] => 示例:2019-12-04
        )

    [html_month] => Array
        (
            [0] => 名称:HTML 月份
            [1] => 模式:Y-m
            [2] => 示例:2019-12
        )

    [html_time] => Array
        (
            [0] => 名称:HTML 时间
            [1] => 模式:H:i:s
            [2] => 示例:15:03:17
        )

    [html_week] => Array
        (
            [0] => 名称:HTML 周
            [1] => 模式:Y-\WW
            [2] => 示例:2019-W49
        )

    [html_year] => Array
        (
            [0] => 名称:HTML 年份
            [1] => 模式:Y
            [2] => 示例:2019
        )

    [html_yearless_date] => Array
        (
            [0] => 名称:HTML 无年份日期
            [1] => 模式:m-d
            [2] => 示例:12-04
        )

    [long] => Array
        (
            [0] => 名称:默认长日期
            [1] => 模式:l, F j, Y - H:i
            [2] => 示例:星期三, 十二月 4, 2019 - 15:03
        )

    [medium] => Array
        (
            [0] => 名称:默认中等日期
            [1] => 模式:D, m/d/Y - H:i
            [2] => 示例:周三, 12/04/2019 - 15:03
        )

    [short] => Array
        (
            [0] => 名称:默认短日期
            [1] => 模式:m/d/Y - H:i
            [2] => 示例:12/04/2019 - 15:03
        )

)

 

日期格式化器服务:

这是一个工具型服务,用于提供日期时间相关的辅助方法,如格式化时间、计算时间间隔等

服务iddate.formatter

类:Drupal\Core\Datetime\DateFormatter

twig模板中的format_date过滤器在PHP层即调用该服务的format方法,各方法说明如下:

public function format($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL)

格式化一个时间,返回一个经过翻译的日期字符串,各参数含义如下:

$timestamp:时间戳

$type:时间格式类型,也就是前文的日期格式实体的实体id ,默认为:medium

$format:格式类型也可以被指定为“custom,此时将使用该参数指定的格式(同date函数的第一个参数),如未指定且类型为“custom”,则使用类型“fallback

$timezone:时区标识符,字符串值,默认使用系统默认时区

$langcode:语言代码,用于翻译,默认使用界面语言id

注意:该方法的格式化字符串如果包含了用户输入,安全起见返回值务必进行实体转义

 

public function formatInterval($interval, $granularity = 2, $langcode = NULL)

格式化一个时间间隔,返回翻译过的字符串,以年、月、周、天、时、分、秒表示,如:

2 3 个月 2 6 14 小时 20 分钟 50

2 6 14 小时 20 分钟 50

14 小时 20 分钟

注意:在返回的表示中,单位一定是从大到小连续的,最小间隔单位为秒,不会出现类似以下的形式:

6 20 分钟 50 (这跳过了时)

2 3 个月6 (这跳过了周)

该方法一年按365天算,一月按30天算,即不考虑闰年和大小月

参数说明如下:

$interval:时间间隔,浮点数,单位秒

$granularity:可包含的单位最大个数,默认2,合理值应小于等于7,大于等于1,但无强制要求,假设完整应返回“2 3 个月 2 6 14 小时 20 分钟 50 秒”,此时该参数设为3会导致仅返回“2 3 个月 2 周”,仅有年周月3个单位

$langcode:用于翻译的语言代码,默认为系统当前语言的代码

 

public function getSampleDateFormats($langcode = NULL, $timestamp = NULL, $timezone = NULL)

返回一个时间格式样本数组,键名为可用的时间格式字符(仅一个字符),键值为对应的格式化时间,如下:

 

Array
(
    [d] => 04
    [D] => 周三
    [j] => 4
    [l] => 星期三
    [N] => 3
    [S] => th
    [w] => 3
    [z] => 337
    [W] => 49
    [F] => 十二月
    [m] => 12
    [M] => 12月
    [n] => 12
    [t] => 31
    [L] => 0
    [o] => 2019
    [Y] => 2019
    [y] => 19
    [a] => 下午
    [A] => 下午
    [B] => 406
    [g] => 4
    [G] => 16
    [h] => 04
    [H] => 16
    [i] => 44
    [s] => 39
    [u] => 000000
    [e] => Asia/Hong_Kong
    [I] => 0
    [O] => +0800
    [P] => +08:00
    [T] => HKT
    [Z] => 28800
    [c] => 2019-12-04T16:44:39+08:00
    [r] => Wed, 04 Dec 2019 16:44:39 +0800
    [U] => 1575449079
)

 

参数如下:

$langcode:用于翻译的语言代码,默认为系统当前语言的代码

$timestamp:时间戳,用这个时间来生成样本,默认为当前时间

$timezone:时区标识符,默认为系统默认时区

 

public function formatDiff($from, $to, $options = [])

返回两个时间点之间的间隔,最小间隔单位为秒,和formatInterval方法一样,但该方法还可以以对象方式返回,此时返回以下类对象:

\Drupal\Core\Datetime\FormattedDateDiff

该对象除了包含时间间隔字符串外,还包含最大缓存时间,用于显示时失效缓存,缓存时间应以字符串中最后一个单位的1单位来算,比如返回“23小时”,那么缓存时间就应该是1小时,以便在间隔为“22小时”时刷新页面。

参数$from$to都是时间戳,分别表示起点和终点,$options为选项数组,可选的有如下键:

granularity:见formatInterval方法的第二个参数

langcode:用于翻译的语言代码

strict:布尔值,是否采用严格模式,如为true(默认值),则在$from大于$to时将返回0秒,否则将计算差值,返回不分正负

return_as_object:布尔值,是否以对象方式返回,默认FALSE

 

public function formatTimeDiffUntil($timestamp, $options = [])

返回从当前请求的请求时间到给定时间戳时间之间的间隔,详见formatDiff方法

 

formatTimeDiffSince($timestamp, $options = [])

返回给定时间戳到当前请求的请求时间之间的间隔,详见formatDiff方法

 

简单日期表单元素:

元素类型:date

类:Drupal\Core\Render\Element\Date

提供一个日期输入表单元素,即type"date"input元素,用户可用日期选择器控件datepicker进行输入

示例如下:

        $form['yunke_date'] = [
            '#type'             => 'date',
            '#title'            => '日期元素',
            '#min'              => '2010-02-05',
            '#max'              => '2020-02-05',
            '#step'             => 5,
            '#default_value'    => '2019-03-05',
            '#date_date_format' => 'Y-m-d',
        ];

在提交处理器中:

        $date = $form_state->getValue($form['yunke_date']['#parents']);

得到的$date是一个字符串表示的日期,如“2019-02-15

注意该日期元素不涉及时区,也不涉及时分秒,如需这些请见下文其他两个日期元素类型

各选项解释如下:

#min

前端可选择的最小日期

#max

前端可选择的最大日期

#step

日期增加的步长

#date_date_format

用于表示日期的格式

 

日期时间表单元素:

元素类型:datetime

类:\Drupal\Core\Datetime\Element\Datetime

选项及默认值如下:

        $form['yunke_datetime'] = [
            '#type'    => 'datetime',
            '#date_date_format' => 'Y-m-d',
            '#date_date_element' => 'date',
            '#date_date_callbacks' => [],
            '#date_time_format' => 'H:i:s',
            '#date_time_element' => 'time',
            '#date_time_callbacks' => [],
            '#date_year_range' => '1900:2050',
            '#date_increment' => 1,
            '#date_timezone' => date_default_timezone_get(),
            '#default_value'=>NULL,
        ];

在提交处理器中:

$date=$form_state->getValue($form['yunke_datetime']['#parents']);

得到的$date值是一个日期时间对象,即以下类的实例:

Drupal\Core\Datetime\DrupalDateTime

如不要求必选,没填时也可能为NULL

 

该元素各主要选项及其含义如下:

#date_date_format

日期格式,见date函数第一个参数,默认为格式实体html_date的模式,即“Y-m-d”,如果显示使用H5控件元素,那么格式必须指定为对应格式,否则控件不工作

#date_date_element

指示显示该日期的控件类型,可选值如下:

date:标准H5日期输入控件,也就是type"date"input元素,弹框点击方式输入

datetime:标准H5日期时间输入控件,也就是type" datetime "input元素,须手动输入

datetime-local:标准H5日期时间输入控件,也就是type" datetime-local"input元素,须手动输入

text:纯文本方式输入,也就是type" text"input元素

none:不使用也不显示日期输入控件

#date_date_callbacks

可选,日期回调数组,每一个元素均是一个回调,接收参数如下:

callback($element, $form_state, $date);

第一个参数为本日期时间元素,其“date”子健为日期元素,最后一个参数为日期时间对象,在客户端不支持H5时,可以用回调来添加一个日历控件

#date_time_format

时间格式,见date函数第一个参数,默认为格式实体html_time的模式,即“H:i:s”,如果使用H5元素,那么格式必须指定为对应格式,否则控件不工作

#date_time_element

指示时间控件类型,可选值如下:

time:标准H5日期输入控件,也就是type" time"input元素,可通过方向键或滑动输入

text:纯文本方式输入,也就是type" text"input元素

none:不显示也不使用时间输入控件

#date_time_callbacks

可选,时间回调数组,每一个元素均是一个回调,接收参数如下:

callback($element, $form_state, $date);

第一个参数为本日期时间元素,其“time”子健为时间元素,最后一个参数为日期时间对象,回调须是一个函数,即function_exists($callback)需要为true

#date_year_range

可选,表示可选择的年份范围,一个冒号分隔的年份标识,年份标识可以是相对的也可以是绝对的,用4位数字表示绝对年份,用“+-”加一个14位的数字表示相对年份(相对于当前年份),默认为1900:2050,年份大小和在冒号前后的位置无关,如“2011:2019”也可以写为“2019:2011”,但如果设定了默认值,则会强行拉长范围以将默认值包含在内,更多示例如:

1900:2050(绝对时间表示的范围,1900年到2050年,也可写为2050:1900

-3:+3(当前年份的前后三年,如当前为2019,那么其为2016:2022

2000:+3(如果当前年份是2019年,那么表示2000-2022年)

范围均包含起始结、束年份,如2010:2019,可选择日期将是2010-01-012019-12-31

注意:该项仅用于前端验证,且仅在有默认值和采用日期控件时才有效,后端对该范围不做验证

#date_increment

可选,用于typetimeinput元素的step值,调整时间元素部分增加或减少的步长值,整数,单位秒,默认为1秒,如果是60的倍数,那么秒组件不被受影响,该选项的效果依赖浏览器,实测chrom支持的很好,但火狐、微软无效

#date_timezone

时区标识符,字符串值,默认为date_default_timezone_get(),用于显示时间所使用的时区,也隐含意味着客户端在使用该时区,因此应是系统中用户设置的时区,即用户账户信息中使用的时区

#default_value

默认值,须用\Drupal\Core\Datetime\DrupalDateTime的实例来表示

BUG提示(Drupal8.8:在该方法中:

\Drupal\Core\Datetime\Element\Datetime::getInfo

由于返回值会被缓存,所以不能在该方法中赋值默认时区,而应该在valueCallback方法中判断没有默认值后才赋值,云客发现已经有开发者报告了该问题,详见:

https://www.drupal.org/project/drupal/issues/3087606

 

下拉选择型日期表单元素:

元素类型iddatelist

类:\Drupal\Core\Datetime\Element\Datelist

该类型元素用下拉列表的方式指定一个日期时间,从年精确到分钟

选项及默认值如下:

        $form['yunke_datelist'] = [
            '#type'    => 'datelist',
            '#date_part_order' => ['year', 'month', 'day', 'hour', 'minute'],
            '#date_text_parts'=>[],
            '#date_year_range' => '1900:2050',
            '#date_increment' => 1,
            '#date_date_callbacks' => [],
            '#date_timezone' => date_default_timezone_get(),
        ];

在提交处理器中:

        $date=$form_state->getValue($form['yunke_datelist']['#parents']);

得到的$date值是一个日期时间对象,即以下类的实例:

Drupal\Core\Datetime\DrupalDateTime

如不要求必选,没填时也可能为NULL

以下选项及其含义同datetime元素类型选项完全相同(见前文):

#date_year_range

#date_date_callbacks

#date_timezone

其他主要选项解释如下:

#date_part_order

一个数组,指定了要出现在控件中的时间组件及其出现顺序,可用的时间组件如下:

'year', 'month', 'day', 'ampm', 'hour', 'minute'

没有秒,如果有ampm那么小时将用12小时制,否则用24小时制

#date_text_parts

默认为空数组,可包含的元素同#date_part_order选项一样,表示如果某个时间组件出现在该选项数组中,那么该组件将不以下拉框形式出现,而是以文本框

#date_increment

datetime元素类型选项完全相同,但表示分钟的步进

 

日期时间字段:

字段类型iddatetime

列表类:\Drupal\datetime\Plugin\Field\FieldType\DateTimeFieldItemList

条目类:Drupal\datetime\Plugin\Field\FieldType\DateTimeItem

字段默认控件:Drupal\datetime\Plugin\Field\FieldWidget\DateTimeDefaultWidget

可用字段控件:Drupal\datetime\Plugin\Field\FieldWidget\DateTimeDatelistWidget

该字段在储存时间时,并没有将其转化为时间戳,而是以时间字符串方式进行储存,但储存的字符串已经被转化成标准UTC时区,如果直接查看数据库,中国用户会发现少了8小时,如“2019-12-09T02:15:30”,实际上表示的北京时间为“2019-12-09T10:15:30”,因此储存的值可以视为是一个绝对时间值,和时间戳无异,只是以字符串方式储存,这导致以该字段作为实体查询的条件时,不能以时间方式进行大小于比较

 

时间戳字段:

字段类型idtimestamp

条目类:Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem

字段默认控件:\Drupal\Core\Datetime\Plugin\Field\FieldWidget\TimestampDatetimeWidget

该字段以时间戳方式储存时间

BUG提示(Drupal8.8:该字段默认控件实现有问题,在字段默认值设置中,留空表示字段值为表单提交时间,但实际上默认值留空时,字段采用了字段设置表单提交的时间,云客发现已有开发者提交了该问题,详见:

https://www.drupal.org/project/drupal/issues/2978467

https://www.drupal.org/project/drupal/issues/2978727

 

创建与修改时间字段:

创建时间字段:

字段类型idcreated

条目类:Drupal\Core\Field\Plugin\Field\FieldType\CreatedItem

修改时间字段:

字段类型idchanged

条目类:Drupal\Core\Field\Plugin\Field\FieldType\ChangedItem

列表类:\Drupal\Core\Field\ChangedFieldItemList

以上两个字段没有UI接口,仅在程序中使用,她们均储存时间戳

 

时间范围字段:

字段类型iddaterange

条目类:Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem

列表类:\Drupal\datetime_range\Plugin\Field\FieldType\DateRangeFieldItemList

该字段由核心模块datetime_range提供,该模块默认没有启用,该字段用于储存一个日期范围,以字符串方式的绝对时间值(UTC时区)进行储存

 

用户时区设置:

Drupal在时间的储存上使用的是绝对时间值(时间戳或UTC时区表示的时间字符串),这些值和时区无关;站点可以配置用户是否可以配置他们自己的时区,管理地址如下:

/admin/config/regional/settings

如果允许用户配置,那么在全站时间显示时将以用户配置的时区为准,在用户录入信息时,页面时间输入控件默认采用用户自己设定的时区,但并无显式给出时区信息,此时如果时区设置不当,真实时间将会出现偏差,在有些对时间特别敏感的站点上可能会出问题,因此是否允许用户设置自己的时区很重要。

比如一个位于中国的网站,系统默认时区设置为Asia/Shanghai,即表示网站默认使用北京时间,在中国可能很多网站用户对时区概念不是很了解,甚至不知道为什么要多此一举去设置时区,如果允许用户自己设置时区,就可能会出现用户明明在中国,却胡乱选择设置,假设被设置为了“UTC”,那么此时网站给该用户显示的时间全部会慢8小时,用户在录入信息的时候,比如添加文章,在时间控件上可能又强行以北京时间去设置,此时系统储存的真实时间将相差8小时。

因此如果你的网站只是面对区域用户,那么不应允许用户设置自己的时区,如果是面向全球用户,那么必须允许用户能设置自己的时区

 

 

本书共161小节。


目前全部收费内容共295.00元。购买全部

评论 (0)