Skip to content

Latest commit

 

History

History
998 lines (752 loc) · 19.7 KB

date.adoc

File metadata and controls

998 lines (752 loc) · 19.7 KB

Date! 数据类型

1. 概要

date! 值表示日历日期,它依赖于格里高利历。一个 date! 值也可以包含可选的时间和时区信息。日期的时间以 24 小时的格式表示(包括可选的时区信息)。Red 的日期也全面支持 ISO 8601 输入格式。

注意
  • date! 使用早发格里高利历以扩展其用法到 1582 年之前。在使用 date! 值表示历史上的前格里高利历日期时需要当心。

  • 支持的日期取值范围为从 1/Jan/-1638431/Dec/16383,当数学运算溢出时会进行折返。在输入时出于实践性的考虑,该取值范围减少为从 1/Jan/-999931/Dec/9999

2. 创建

date! 值可以使用字面语法创建,或者在运行时使用 make 构造函数创建,或用 to 转换到日期类型。

2.1. 字面语法

它支持多种不同的字面语法格式,包括所有的 Rebol 日期格式和大多数的 ISO 日期标准。

<date>/<time>
<date>/<time><zone>
<date>T<time>
<date>T<time>Z
<date>T<time><zone>

<date> 格式:
    <yyyy><sep><mmm><sep><dd>
    <dd><sep><mmm><sep><yyyy>
    <dd><sep><mmm><sep><yy>

当后接 T 时附加的 <date> 格式:
    <yyyy><mm><dd>
    <yyyy>-W<ww>
    <yyyy>-W<ww>-<d>
    <yyyy>-<ddd>

<mmm> 格式(月份):
    <m>
    <mon>
    <month>

<time> 格式:
    <hour>:<min>:<sec>
    <hour>:<min>:<sec><zone>
    <hhmmss>
    <hhmmss><zone>
    <hhmmss>.<dec>
    <hhmmss>.<dec><zone>
    <hhmm>
    <hhmm><zone>

<zone> 格式(时区):
    <sign><hour>
    <sign><hour>:<min15>
    <sign><hhmm>

<sec> 格式(秒):
    <ss>
    <ss>.<dec>

<sep>    : `-` 或 `/`
<yyyy>   : 34 位数字,代表年份(4 位数字表示 ISO 日期)
<yy>     : 2 个数字代表相对于 2000 年的年份
<m>      : 12 位数字,代表月份
<mm>     : 2 位数字,代表月份
<mon>    : 3 位字母,代表月份的开始
<month>  : 月份的英文全名
<d>      : 1 位数字,代表星期(17<dd>     : 12 位数字,表示日
<ddd>    : 3 位数字,代表年中的某一天
<ww>     : 2 位数字,代表年中的某一周
<time>   : time!<hour>   : 12 位数字,代表小时
<min>    : 12 位数字,代表分钟
<ss>     : 12 位数字,代表秒
<dec>    : 秒的小数
<sign>   : `+` 或 `-` 字符(不能省略)
<min15>  :<min> 相同但会被舍入为 15 的倍数
<hhmm>   : 4 位数字,表示小时和分钟(无分隔符)
<hhmmss> : 6 位数字,表示小时、分种和秒(无分隔符)

日期字面值可以接受许多不同的输入格式。超出范围的值(超过指定的数字位数或不符合字段的规范)将产生语法错误。当再次序列化(例如用于显示)时,仅使用以下标准格式:

<dd>-<mon>-<yyyy>
<dd>-<mon>-<yyyy>/<hour>:<min>:<sec>
<dd>-<mon>-<yyyy>/<hour>:<min>:<sec><sign><hour>:<min15>

time 和/或 zone 字段没有被设置时,它们会被省略。对于负的日期值,使用 / 分隔符取代 - 分隔符以提高可读性。

注意
  • 当使用字母指定月份时,月份以其英文名称来表示,并为大小写不敏感的。

  • 当仅用 2 位数字(yy)指定年份时:如果它 < 50,则被解释为 20yy 年,否则被解释为 19yy 年。

有效的日期输入例子:

1999-10-5
1999/10/5
5-10-1999
5/10/1999
5-October-1999
1999-9-11
11-9-1999
5/sep/2012
5-SEPTEMBER-2012

02/03/04
02/03/71

5/9/2012/6:0
5/9/2012/6:00
5/9/2012/6:00+8
5/9/2012/6:0+0430
4/Apr/2000/6:00+8:00
1999-10-2/2:00-4:30
1/1/1990/12:20:25-6

2017-07-07T08:22:23+00:00
2017-07-07T08:22:23Z
20170707T082223Z
20170707T0822Z
20170707T082223+0530

2017-W01
2017-W23-5
2017-W23-5T10:50Z
2017-001
2017-153T10:50:00-4:00

2.2. 运行时创建

make date! [<day> <month> <year>]
make date! [<year> <month> <day>]
make date! [<day> <month> <year> <time>]
make date! [<day> <month> <year> <time> <zone>]
make date! [<day> <month> <year> <hour> <minute> <second>]
make date! [<day> <month> <year> <hour> <minute> <second> <zone>]

<year>   : integer!<month>  : integer!<day>    : integer!<time>   : time!<zone>   : integer! , time!pair!<hour>   : integer!<minute> : integer!<second> : integer!
注意
  • 超出取值范围的参数值将导致错误。若要正规化的结果,请使用 to 替代 make

  • yearday 字段位置可以互换,但只适用于低年份值,当年份值 >= 100 且大于第三个字段的值时可以将年份放在第一个位置。当这个规则不满足的时候,第三个字段被认为是年份。负的年份总是指定在第三个位置。

make date! [1978 2 3]
== 3-Feb-1978

make date! [1978 2 3 5:0:0 8]
== 3-Feb-1978/5:00:00+08:00

make date! [1978 2 3 5:0:0]
== 3-Feb-1978/5:00:00

make date! [1978 2 3 5 20 30]
== 3-Feb-1978/5:20:30

make date! [1978 2 3 5 20 30 -4]
== 3-Feb-1978/5:20:30-4:00


make date! [100 12 31]
== 31-Dec-0100

; 32 不是有效的日期
make date! [100 12 32]
*** Script Error: cannot MAKE/TO date! from: [100 12 32]
*** Where: make

; 第一个字段 < 100,所以不被认为是年份
make date! [99 12 31]
*** Script Error: cannot MAKE/TO date! from: [99 12 31]
*** Where: make

3. 路径访问器

路径访问器提供了一种便捷的方式来获取和设置所有 date! 值字段。

3.1. /date

语法

<date>/date
<date>/date: <date2>

<date>  : 引用 date! 值的单词或路径表达式
<date2> : date!

描述

获取或设置日期的日期字段(不包括时间和区域),返回作为 date! 值的日期。

d:  now
== 10-Jul-2017/22:46:22-06:00
d/date
== 10-Jul-2017

d/date: 15/09/2017
== 15-Sep-2017/22:46:22-06:00

3.2. /year

语法

<date>/year
<date>/year: <year>

<date> : 引用 date! 值的单词或路径表达式
<year> : integer!

描述

获取或设置日期的年份字段,返回作为整数的年份。超出取值范围的参数值会产生一个正规化的日期。

d:  now
== 10-Jul-2017/22:46:22-06:00
d/year: 16383
== 16383
d
== 10-Jul-16383/22:46:22-06:00
d/year: 16384
== 16384
d
== 10/Jul/-16384/22:46:22-06:00     ; 注意超过 16384 年后产生溢出折返
d/year: 32767
== 32767
d
== 10/Jul/-1/22:46:22-06:00
d/year: 32768
== 32768
d
== 10-Jul-0000/22:46:22-06:00

3.3. /month

语法

<date>/month
<date>/month: <month>

<date>  : 引用 date! 值的单词或路径表达式
<month> : integer!

描述

获取或设置日期的月份字段,返回作为整数的月份。超出取值范围的参数值会产生一个正规化的日期。

d: now
== 10-Jul-2017/22:48:31-06:00
d/month: 12
== 12
d
== 10-Dec-2017/22:48:31-06:00
d/month: 13
== 13
d
== 10-Jan-2018/22:48:31-06:00   ; 注意折返到了下一年
d/month
== 1                            ; 月份现在被正规化了

3.4. /day

语法

<date>/day
<date>/day: <day>

<date> : 引用 date! 值的单词或路径表达式
<day>  : integer!

描述

获取或设置日期的日字段,返回作为整数的日。超出取值范围的参数值会产生一个正规化的日期。

 d: 1-jan-2017
== 1-Jan-2017
d/day: 32
== 32
d
== 1-Feb-2017
d/day: 0         ; 注意 0 起的作用,以使日期数学合理
== 0
d
== 31-Jan-2017

3.5. /time

语法

<date>/time
<date>/time: <time>

<date> : 引用 date! 值的单词或路径表达式
<time> : time!none!

描述

获取或设置日期的时间字段,返回作为 time! 值的时间,或者如果时间没有被设置或已经被重置(见下文),就返回 none! 值。超出取值范围的参数值会产生一个正规化的日期。

如果时间被设置为 none! 值,则 timezone 字段将被设置为零,并且不再会被显示出来。

d: now
== 10-Jul-2017/23:18:54-06:00
d/time: 1:2:3
== 1:02:03
d
== 10-Jul-2017/1:02:03-06:00
d/time: none
== 10-Jul-2017

3.6. /hour

语法

<date>/hour
<date>/hour: <hour>

<date> : 引用 date! 值的单词或路径表达式
<hour> : integer!

描述

获取或设置日期的小时字段,返回 0 到 23 之间的作为整数值的小时。超出取值范围的参数值会产生一个正规化的日期。

d: now
== 10-Jul-2017/23:19:40-06:00
d/hour: 0
== 0
d
== 10-Jul-2017/0:19:40-06:00
d/hour: 24
== 24
d
== 11-Jul-2017/0:19:40-06:00

3.7. /minute

语法

<date>/minute
<date>/minute: <minute>

<date>   : 引用 date! 值的单词或路径表达式
<minute> : integer!

描述

获取或设置日期的分钟字段,返回 0 到 59 之间的作为整数值的分钟。超出取值范围的参数值会产生一个正规化的日期。

== 10-Jul-2017/23:20:25-06:00
d/minute: 0
== 0
d
== 10-Jul-2017/23:00:25-06:00
d/minute: 60
== 60
d
== 11-Jul-2017/0:00:25-06:00

3.8. /second

语法

<date>/second
<date>/second: <second>

<date>   : 引用 date! 值的单词或路径表达式
<second> : integer!float!

描述

获取或设置日期的秒数字段,返回为 0 到 59 之间的作为 integer!float! 值的秒数。超出取值范围的参数值会产生一个正规化的日期。

d: now
== 10-Jul-2017/23:21:15-06:00
d/second: 0
== 0
d
== 10-Jul-2017/23:21:00-06:00
d/second: -1
== -1
d
== 10-Jul-2017/23:20:59-06:00
d/second: 60
== 60
d
== 10-Jul-2017/23:21:00-06:00

3.9. /zone

语法

<date>/zone
<date>/zone: <zone>

<date> : 引用 date! 值的单词或路径表达式
<zone> : time!integer!

描述

获取或设置日期的时区字段,返回在 -16:00 与 +15:00 之间的作为 time! 值的时区。使用 /zone 设置时区将仅改变该字段,时间会保持不变。超出取值范围的参数值会产生一个正规化的日期。

当用 integer! 参数设置时区时,该参数表示分钟被设置为 0 的小时数。

时区的分钟数的粒度为 15,不遵照粒度的值将被向下舍入到最靠近的 15 分钟的倍数。

d: 1/3/2017/5:30:0
d/zone: 8
== 1-Mar-2017/5:30:00+08:00

d/zone: -4:00
== 1-Mar-2017/5:30:00-04:00

3.10. /timezone

语法

<date>/timezone
<date>/timezone: <timezone>

<date>     : 引用 date! 值的单词或路径表达式
<timezone> : integer! , time!pair!

描述

获取或设置日期的时区字段,返回在 -16:00 与 +15:00 之间的作为 time! 值的时区。使用 /zone 设置时区将改变时间和区域两个字段,使新时间保持与在新区域内的旧时间相等。超出取值范围的参数值会产生一个正规化的日期。

当用 integer! 参数设置时区时,该参数表示分钟被设置为 0 的小时数。

时区的分钟数的粒度为 15,不遵照粒度的值将被向下舍入到最靠近的 15 分钟的倍数。

d: 1/3/2017/5:30:0
d/timezone: 8
== 1-Mar-2017/13:30:00+08:00

d/timezone: -4:00
== 1-Mar-2017/1:30:00-04:00
注意
  • 设置 /timezone 为 0 将会把时间设置为 UTC。

3.11. /yearday

语法

<date>/yearday
<date>/yearday: <day>

<date>    : 引用 date! 值的单词或路径表达式
<yearday> : integer!

描述

获取日期当天在当年中的天数,从 1 月 1 日开始计为 1,返回作为整数的天数。当用于设置当年中的当天天数时,该日期会被重新计算以匹配那一天。超出取值范围的参数值会产生一个正规化的日期。

注意
  • 为了与 Rebol 兼容,/yearday 的另一个别名 /julian 也是可用的。

d: 1-jan-2017
== 1-Jan-2017
d/yearday
== 1
d: 31-dec-2017
== 31-Dec-2017
d/yearday
== 365
d: 31-dec-2020
== 31-Dec-2020
d/yearday
== 366                  ; 闰年

d: 31-dec-2017
== 31-Dec-2017
d/yearday: 366
== 366
d
== 1-Jan-2018

3.12. /weekday

语法

<date>/weekday
<date>/weekday: <day>

<date>    : 引用 date! 值的单词或路径表达式
<weekday> : integer!

描述

获取星期数值,取值范围为从星期一的 1 到星期天的 7。当用于设置星期时,该日期会被重新计算以匹配相对于这周的那一天。超出取值范围的参数值会产生一个正规化的日期。

d: now
== 10-Jul-2017/23:25:35-06:00
d/weekday
== 1
d/weekday: 2
== 2
d
== 11-Jul-2017/23:25:35-06:00
d/weekday: 7
== 7
d
== 16-Jul-2017/23:25:35-06:00
d/weekday: 8
== 8
d
== 17-Jul-2017/23:25:35-06:00

3.13. /week

语法

<date>/week
<date>/week: <day>

<date> : 引用 date! 值的单词或路径表达式
<week> : integer!

描述

获取使用非正式周定义的周数(周从星期日开始,第一周从 1 月 1 日开始),取值范围为从年中第一个星期的 1 到 53。当用于设置周数时,该日期会被重新计算以匹配那周的第一天(星期日)。超出取值范围的参数值会产生一个正规化的日期。

注意
  • 非正式周定义允许年中的第一个和最后一个星期是不完整的一周,天数取值范围为从 1 天到 7 天。对于精确周计算,请使用 /isoweek 访问器。

d: now
== 10-Jul-2017/23:28:07-06:00
d/week
== 28
d/week: 29
== 29
d
== 16-Jul-2017/23:28:07-06:00
d/week: 52
== 52
d
== 24-Dec-2017/23:28:07-06:00
d/week: 53
== 53
d
== 31-Dec-2017/23:28:07-06:00
d/week: 54
== 54
d
== 7-Jan-2018/23:28:07-06:00

3.14. /isoweek

语法

<date>/isoweek
<date>/isoweek: <day>

<date>    : 引用 date! 值的单词或路径表达式
<isoweek> : integer!

描述

获取使用 ISO 8601 周定义的周数,取值范围为从年中第一个星期的 1,到 52(或者某些年是 53)。当用于设置周数时,该日期会被重新计算以匹配那周的第一天(星期一)。超出取值范围的参数值会产生一个正规化的日期。

d: now
== 10-Jul-2017/23:29:13-06:00
d/isoweek
== 28
d/isoweek: 29
== 29
d
== 17-Jul-2017/23:29:13-06:00
d/isoweek: 52
== 52
d
== 25-Dec-2017/23:29:13-06:00
d/isoweek: 53
== 53
d
== 1-Jan-2018/23:29:13-06:00

3.15. 序数访问器

除了使用单词来访问日期的字段,也可以在路径表达式中使用整数索引:

<date>/<index>

<date>  : 引用 date! 值的单词或路径表达式
<index> : 指向日期字段的 integer!

这样的序数访问器可以用于获取或设置字段。下表给出了对应的字段名称

索引 名称

1

date

2

year

3

month

4

day

5

zone

6

time

7

hour

8

minute

9

second

10

weekday

11

yearday

12

timezone

13

week

14

isoweek

3.16. 使用 pick 访问日期字段

可以不使用路径来访问日期字段,这在某些情况下更为便捷,可以对日期使用 pick

语法

pick <date> <field>

<date>  : date!<field> : integer!

整数参数代表用于日期的序数访问器,参照上面的 “序数访问器” 表格。

d: now
== 10-Jul-2017/23:35:01-06:00
names: system/catalog/accessors/date!
repeat i 14 [print [pad i 4 pad names/:i 10 pick d i]]
1    date       11-Jul-2017
2    year       2017
3    month      7
4    day        11
5    zone       8:00:00
6    time       21:43:52
7    hour       21
8    minute     43
9    second     52.0
10   weekday    2
11   yearday    192
12   timezone   8:00:00
13   week       28
14   isoweek    28

4. 转换

4.1. 新纪元时间

日期可以用 to 动作与 Unix 新纪元时间互相转换。

语法

to-integer <date>
to-date <epoch>

<date>  : date!<epoch> : 代表新纪元时间的整数值

新纪元时间,以 UTC 时区表示。如果日期参数不在 UTC 时区,它会在转换为新纪元时间之前被内部地转换到 UTC 时区。

d: 8-Jul-2017/17:49:27+08:00
to-integer d
== 1499507367

to-integer 8-Jul-2017/9:49:27
== 1499507367

to-date to-integer d
== 8-Jul-2017/9:49:27
注意

2038 年之后的新纪元时间是未定义的。

4.2. 把区块转换为日期

语法

to date! <spec>

<spec> : 用于日期字段的内含值的区块

参数区块会按照跟 make(参考 2.2 运行时创建)一样的语法被转换为 date! 值。超出取值范围的参数值会产生正规化的日期。若要从区块进行会产生错误而不会被正规化的严格转换,请使用 make

5. 比较

所有可以应用于日期的比较有:===<>><>=<=same?。此外,还支持 minmaxsort

3-Jul-2017/9:41:40+2:00 = 3-Jul-2017/5:41:40-2:00
== true

10/10/2017 < 1/1/2017
== false

max 10/10/2017 1/1/2017
== 10-Oct-2017

same? 1/1/1980 1-JAN-1980
== true

sort [1/1/2017 5/10/1999 3-Jul-2017/5:41:40-2:00 1/1/1950 1/1/1980/2:2:2]
== [1-Jan-1950 1-Jan-1980/2:02:02 5-Oct-1999 1-Jan-2017 3-Jul-2017/5:41:40-02:00]

6. 算术运算

可对日期进行的数学操作包括:

  • 加减任何日期字段中的值:结果将被正规化。

  • 用日期值跟整数值相加减:该整数会被解释为天数。

  • 用日期值跟时间值相加减:将把日期的时间增加/减少该时间。

  • 两个日期相减:结果是这两个日期相隔的有符号天数。

  • 以两个日期为参数调用 difference:结果是作为 time! 值的这两个日期之间的有符号的差。

20-Feb-1980 + 50
== 10-Apr-1980

20-Feb-1980 + 3
== 23-Feb-1980

20-Feb-1980 - 25
== 26-Jan-1980

20-Feb-1980 + 100
== 30-May-1980

28-Feb-1980 + 20:30:45
== 28-Feb-1980/20:30:45

28-Feb-1980/8:30:00 + 20:30:45
== 29-Feb-1980/5:00:45

d: 20-Feb-1980
d/day: d/day + 50
== 10-Apr-1980

d: 20-Feb-1980
d/month: d/month + 5
== 20-Jul-1980

d: 28-Feb-1980/8:30:00
d/hour: d/hour + 48
== 1-Mar-1980/8:30:00

08/07/2017/10:45:00 - 20-Feb-1980/05:30:0
== 13653

difference 08/07/2017/10:45:00 20-Feb-1980/05:30:0
327677:15:00

7. 获取当前日期

now 函数返回操作系统的当前日期和时间(包括时区)。所有的日期路径访问器都可作为修饰词用于 now,另外还有一些附加的:

  • /utc:获取 UTC 格式的日期。

  • /precise:以更高的精度获得时间(在 Windows 上为 1/60 秒,在 Unix 上为微秒)。

now
== 8-Jul-2017/18:32:25+08:00

now/year
== 2017

now/hour
== 18

now/month
== 7

now/day
== 8

now/hour
== 18

now/zone
== 8:00:00

now/utc
== 8-Jul-2017/10:32:25

8. 其他日期相关函数

8.1. random

语法

random <date>

<date> : date!

描述

使用参数日期作为上限,返回一个随机日期。如果参数日期没有时间/时区分量,则产生的日期也不会有。

random 09/07/2017
== 18-May-1972

random 09/07/2017
== 13-Aug-0981

random 09/07/2017/12:00:00+8
== 28-Feb-0341/17:57:12+04:00

random 09/07/2017/12:00:00+8
== 13-Dec-1062/5:09:12-00:30