Dates
模块提供了两种关于时间的数据类型: Date
和 DateTime
, 精度分别为天和毫秒, 都是抽象数据类型 TimeType
的子类型.
使用两种数据类型的原因很简单: 某些操作本身很简单, 无论是从代码上看还是逻辑上, 使用高精度的数据类型是完全没有必要的. 例如, Date
只精确到天 (也就是说, 没有小时, 分钟或者秒), 所以使用时就不需要考虑时区, 夏令时和闰秒.
Date
和 DateTime
都不过是 Int64
的简单封装, 仅有的一个成员变量 instant
实际上的类型是 UTInstant{P}
, 代表的是基于世界时的机器时间 [1]. Datetime
类型是 不考虑时区 的 (根据 Python 的讲法), 或者说是 Java 8 里面的 本地时间.
额外的时间日期操作可以通过 Timezones.jl 扩展包来获取, 其中的数据来自 Olsen Time Zone Database .
Date
和 DateTime
遵循 ISO 8601 标准. 值得注意的一点是, ISO 8601 关于公元前日期的处理比较特殊. 简单来说, 公元前的最后一天是公元前 1-12-31, 接下来第二天是公元 1-1-1, 所以是没有公元 0 年存在的. 而 ISO 标准认定, 公元前 1 年是 0 年, 所以 0000-12-21
是 0001-01-01
的前一天, -0001
是公元前 2 年, -0003
是公元前 3 年, 等等.
[1] 一般来说有两种常用的时间表示法, 一种是基于地球的自转状态 (地球转一整圈 = 1 天), 另一种基于 SI 秒 (固定的常量).
这两种表示方法是不一样的. 试想一下, 因为地球自转, 基于世界时的的秒可能是不等长的. 但总得来说, 基于世界时的 Date
和 DateTime
是一种简化的方案, 例如闰秒的情况不需要考虑. 这种表示时间的方案的正式名称为世界时
这意味着, 每一分钟有 60 秒, 每一天有 60 小时, 这样使得关于时间的计算更自然, 简单.
Date
和 DateType
可以通过整数或者 Period
构造, 通过直接传入, 或者作为与特定时间的差值:
julia> DateTime(2013) |
2013-01-01T00:00:00 |
julia> DateTime(2013,7) |
2013-07-01T00:00:00 |
julia> DateTime(2013,7,1) |
2013-07-01T00:00:00 |
julia> DateTime(2013,7,1,12) |
2013-07-01T12:00:00 |
julia> DateTime(2013,7,1,12,30) |
2013-07-01T12:30:00 |
julia> DateTime(2013,7,1,12,30,59) |
2013-07-01T12:30:59 |
julia> DateTime(2013,7,1,12,30,59,1) |
2013-07-01T12:30:59.001 |
julia> Date(2013) |
2013-01-01 |
julia> Date(2013,7) |
2013-07-01 |
julia> Date(2013,7,1) |
2013-07-01 |
julia> Date(Dates.Year(2013),Dates.Month(7),Dates.Day(1)) |
2013-07-01 |
julia> Date(Dates.Month(7),Dates.Year(2013)) |
2013-07-01 |
Date
和 DateTime
解析是通过格式化的字符串实现的. 格式化的字符串是指 分隔 的或者 固定宽度 的 "字符段" 来表示一段时间, 然后传递给 Date
或者 DateTime
的构造函数.
使用分隔的字符段方法, 需要显示指明分隔符, 所以 "y-m-d"
告诉解析器第一个和第二个字符段中间有一个 -
, 例如 "2014-07-16"
, y
, m
和 d
字符告诉解析器每个字符段的含义.
固定宽度字符段是使用固定宽度的字符串来表示时间. 所以 "yyyymmdd"
相对应的时间字符串为 "20140716"
.
同时字符表示的月份也可以被解析, 通过使用 u
和 U
, 分别是月份的简称和全称. 默认支持英文的月份名称, 所以 u
对应于 Jan
, Feb
, Mar
等等, U
对应于 January
, February
, March
等等. 然而, 同 dayname
和 monthname
一样, 本地化的输出也可以实现, 通过向 Dates.MONTHTOVALUEABBR
和 Dates.MONTHTOVALUE
字典添加 locale=>Dict{UTF8String, Int}
类型的映射.
更多的解析和格式化的例子可以参考 tests/dates/io.jl .
计算两个 Date
或者 DateTime
之间的间隔是很直观的, 考虑到他们不过是 UTInstant{Day}
和 UTInstant{Millisecond}
的简单封装. 不同点是, 计算两个 Date
的时间间隔, 返回的是 Day
, 而计算DateTime
时间间隔返回的是 Millisecond
. 同样的, 比较两个 TimeType
本质上是比较两个 Int64
julia> dt = Date(2012,2,29) |
2012-02-29 |
julia> dt2 = Date(2000,2,1) |
2000-02-01 |
julia> dump(dt) |
Date |
instant: UTInstant{Day} |
periods: Day |
value: Int64 734562 |
julia> dump(dt2) |
Date |
instant: UTInstant{Day} |
periods: Day |
value: Int64 730151 |
julia> dt > dt2 |
true |
julia> dt != dt2 |
true |
julia> dt + dt2 |
Operation not defined for TimeTypes |
julia> dt * dt2 |
Operation not defined for TimeTypes |
julia> dt / dt2 |
Operation not defined for TimeTypes |
julia> dt - dt2 |
4411 days |
julia> dt2 - dt |
-4411 days |
julia> dt = DateTime(2012,2,29) |
2012-02-29T00:00:00 |
julia> dt2 = DateTime(2000,2,1) |
2000-02-01T00:00:00 |
julia> dt - dt2 |
381110402000 milliseconds |
因为 Date
和 DateTime
类型是使用 Int64
的封装, 具体的某一部分可以通过访问函数来获得. 小写字母的获取函数返回值为整数:
julia> t = Date(2014,1,31) |
2014-01-31 |
julia> Dates.year(t) |
2014 |
julia> Dates.month(t) |
1 |
julia> Dates.week(t) |
5 |
julia> Dates.day(t) |
31 |
Period
:
julia> Dates.Year(t) |
2014 years |
julia> Dates.Day(t) |
31 days |
查询函数可以用来获得关于 TimeType
的额外信息, 例如某个日期是星期几:
julia> t = Date(2014,1,31) |
2014-01-31 |
julia> Dates.dayofweek(t) |
5 |
julia> Dates.dayname(t) |
"Friday" |
julia> Dates.dayofweekofmonth(t) |
5 # 5th Friday of January |
julia> Dates.monthname(t) |
"January" |
julia> Dates.daysinmonth(t) |
31 |
julia> Dates.isleapyear(t) |
false |
julia> Dates.dayofyear(t) |
31 |
julia> Dates.quarterofyear(t) |
1 |
julia> Dates.dayofquarter(t) |
31 |