介绍 Julia/使用日期和时间
用于处理日期和时间的函数在标准包 Dates 中提供。要使用任何时间和日期函数,您必须执行以下操作之一
using Dates
import Dates
如果您使用 import
Dates 函数,则需要在每个函数前面加上明确的 Dates. 前缀,例如 Dates.dayofweek(dt)
,如本章所示。但是,如果您在代码中添加 using Dates
行,这会将所有导出的 Dates 函数引入 Main,并且可以使用它们而无需 Dates.
前缀。
此图显示了用于存储时间、日期和日期时间的各种类型之间的关系。
有三种主要的数据类型可用
- Dates.Time 对象表示一天中的一个精确时间点。它没有说明星期几或年份。它的精度为纳秒。
- Dates.Date 对象只表示日期:没有时区,没有夏令时问题等... 它的精度为一天。
- Dates.DateTime 对象是日期和时间组合,因此它指定了时间中的一个确切时刻。它的精度为毫秒左右。
使用这些构造函数之一来创建您想要的对象类型
julia> rightnow = Dates.Time(Dates.now()) # a Dates.Time object 16:51:56.374
julia> birthday = Dates.Date(1997,3,15) # a Dates.Date object 1997-03-15 julia> armistice = Dates.DateTime(1918,11,11,11,11,11) # a Dates.DateTime object 1918-11-11T11:11:11
Dates.today()
函数返回当前日期的 Date 对象
julia> datetoday = Dates.today() 2014-09-02
Dates.now()
函数返回当前时间点的 DateTime 对象
julia> datetimenow = Dates.now() 2014-09-02T08:20:07.437
(我们之前使用 Dates.now()
来定义 rightnow
,然后使用 Dates.Time() 将其转换为 Dates.Time。)
有时您需要 UTC(世界参考时间,没有本地夏令时调整)
julia> Dates.now(Dates.UTC) 2014-09-02T08:27:54.14
要从格式化字符串创建对象,请在 Dates 中使用 DateTime()
函数,并提供一个与格式匹配的合适格式字符串
julia> Dates.DateTime("20140529 120000", "yyyymmdd HHMMSS") 2014-05-29T12:00:00 julia> Dates.DateTime("18/05/2009 16:12", "dd/mm/yyyy HH:MM") 2009-05-18T16:12:00 julia> vacation = Dates.DateTime("2014-09-02T08:20:07") # defaults to expecting ISO8601 format 2014-09-02T08:20:07
有关更多示例,请参阅下面的 日期格式化。
获得日期/时间或日期对象后,您可以使用以下函数从该对象中提取信息。对于日期和日期时间对象,您可以获取年份、月份、日期等等
julia> Dates.year(birthday) 1997 julia> Dates.year(datetoday) 2014 julia> Dates.month(birthday) 3 julia> Dates.month(datetoday) 9 julia> Dates.day(birthday) 15 julia> Dates.day(datetoday) 2
以及,对于日期/时间对象
julia> Dates.minute(now()) 37 julia> Dates.hour(now()) 16 julia> Dates.second(now()) 8
julia> Dates.minute(rightnow) 37 julia> Dates.hour(rightnow) 16 julia> Dates.second(rightnow) 8
还有一堆其他有用的函数
julia> Dates.dayofweek(birthday) 6 julia> Dates.dayname(birthday) "Saturday" julia> Dates.yearmonth(now()) (2014,9) julia> Dates.yearmonthday(birthday) (1997,3,15) julia> Dates.isleapyear(birthday) false julia> Dates.daysofweekinmonth(datetoday) 5 julia> Dates.monthname(birthday) "March" julia> Dates.monthday(now()) (9,2) julia> Dates.dayofweekofmonth(birthday) 3
其中两个函数的名称非常相似:Dates.daysofweekinmonth()
(月份中的星期几)函数告诉您一个月中与指定日期具有相同日期名称的天数 - 本月(撰写本文时)有五个星期二。最后一个函数 dayofweekofmonth(birthday)
(月份中的星期几)告诉我们,1997 年 3 月 15 日是这个月的第三个星期六。
您还可以查找相对于某个日期的日期,例如包含该日期的第一个星期几,方法是使用下面介绍的调整函数。
您可以对日期和日期/时间对象进行运算。减去两个日期或日期时间以找到差异是最明显的一种
julia> datetoday - birthday 6380 days julia> datetimenow - armistice 3023472252000 milliseconds
您可以将其转换为 Dates.Day
或 Dates.Millisecond
或其他单位
julia> Dates.Period(datetoday - birthday) 7357 days julia> Dates.canonicalize(Dates.CompoundPeriod(datetimenow - armistice)) 5138 weeks, 5 days, 5 hours, 46 minutes, 1 second, 541 milliseconds julia> convert(Dates.Day, Dates.Period(Dates.today() - Dates.Date(2016, 1, 1))) 491 days julia> convert(Dates.Millisecond, Dates.Period(Dates.today() - Dates.Date(2016, 1, 1))) 42422400000 milliseconds
要向日期和日期/时间对象添加和减去时间段,请使用 Dates.
构造函数来指定时间段。例如,Dates.Year(20)
定义了 20 年的时间段,Dates.Month(6)
定义了 6 个月的时间段。因此,要向生日日期添加 20 年和 6 个月
julia> birthday + Dates.Year(20) + Dates.Month(6) 2017-09-15
这是从现在起 6 个月前
julia> Dates.now() - Dates.Month(6) 2014-03-02T16:43:08
类似地,对于月份、周
julia> Dates.now() - Dates.Year(2) - Dates.Month(6) 2012-03-02T16:44:03
类似地,对于周和小时。这是从现在起两周和 12 小时的日期和时间
julia> Dates.now() + Dates.Week(2) + Dates.Hour(12) 2015-09-18T20:49:16
还有
julia> daystoxmas = Dates.Date(Dates.year(Dates.now()), 12, 25) - Dates.today() 148 days
或距离圣诞节还有 148 天(撰写本文时)。
要将值检索为数字,请使用 Dates.value()
函数
julia> Dates.value(daystoxmas) 148
这适用于不同类型的日期/时间对象
julia> lastchristmas = Dates.now() - Dates.DateTime(2017, 12, 25, 0, 0, 0) 25464746504 milliseconds julia> Dates.value(lastchristmas) 25464746504
您可以创建可迭代的范围对象来定义日期范围
julia> d = Dates.Date(1980,1,1):Dates.Month(3):Dates.Date(2019,1,1) 1980-01-01:3 months:2019-01-01
此迭代器会生成每三个月的第一天。要找出这些日期中的哪些日期是工作日,您可以向 filter()
提供一个匿名函数,该函数将日期名称与给定日期名称进行比较
julia> weekdays = filter(dy -> Dates.dayname(dy) != "Saturday" && Dates.dayname(dy) != "Sunday" , d)
104 元素 Array{Date,1}
1980-01-01 1980-04-01 1980-07-01 ⋮ 2014-07-01 2014-10-01 2016-04-01 2016-07-01 2018-01-01 2018-10-01 2019-01-01
类似地,这是从现在起 3 小时间隔的时间范围,持续一年
julia> d = collect(Dates.DateTime(Dates.now()):Dates.Hour(3):Dates.DateTime(Dates.now() + Dates.Year(1))) 2929-element Array{DateTime,1}: 2015-09-04T08:30:59 2015-09-04T11:30:59 2015-09-04T14:30:59 ⋮ 2016-09-03T20:30:59 2016-09-03T23:30:59 2016-09-04T02:30:59 2016-09-04T05:30:59 2016-09-04T08:30:59
如果您必须每 30 天支付一次账单,从 2018 年 1 月 1 日开始,以下代码显示了到期日期每个月是如何向前推移的
julia> foreach(d -> println(Dates.format(d, "d u yyyy")), Dates.Date("2018-01-01"):Dates.Day(30):Dates.Date("2019-01-01")) 1 Jan 2018 31 Jan 2018 2 Mar 2018 1 Apr 2018 1 May 2018 31 May 2018 30 Jun 2018 30 Jul 2018 29 Aug 2018 28 Sep 2018 28 Oct 2018 27 Nov 2018 27 Dec 2018
要指定日期格式,请在格式字符串中使用日期格式化代码。每个字符都代表一个日期/时间元素
y Year digit eg yyyy => 2015, yy => 15 m Month digit eg m => 3 or 03 u Month name eg Jan U Month name eg January e Day of week eg Tue E Day of week eg Tuesday d Day eg 3 or 03 H Hour digit eg HH => 00 M Minute digit eg MM => 00 S Second digit eg S => 00 s Millisecond digit eg .000
您可以将这些格式字符串与 DateTime()
和 Dates.format()
等函数一起使用。例如,您可以通过识别传入字符串中的不同元素,从字符串中创建 DateTime 对象
julia> Dates.Date("Fri, 15 Jun 2018", "e, d u y") 2018-06-15
julia> Dates.DateTime("Fri, 15 Jun 2018 11:43:14", "e, d u y H:M:S") 2018-06-15T11:43:14
其他字符按字面意思使用。在第二个示例中,格式化字符匹配如下
Fri, 15 Jun 2018 11:43:14 e , d u y H: M: S
您可以向 Dates.format
提供格式字符串来格式化日期对象。在格式字符串中,您可以重复字符以控制例如年份和日期的输出方式
julia> timenow = Dates.now() 2015-07-28T11:43:14
julia> Dates.format(timenow, "e, dd u yyyy HH:MM:SS") "Tue, 28 Jul 2015 11:43:14"
创建格式化日期时,您可以将格式字符串中的某些组件加倍,以对一位数日期元素生成前导零
julia> anothertime = Dates.DateTime("Tue, 8 Jul 2015 2:3:7", "e, d u y H:M:S") 2015-07-08T02:03:07 julia> Dates.format(anothertime, "e: dd u yy, HH.MM.SS") # with leading zeros "Wed: 08 Jul 15, 02.03.07" julia> Dates.format(anothertime, "e: d u yy, H.M.S") "Wed: 8 Jul 15, 2.3.7"
要将日期字符串从一种格式转换为另一种格式,您可以使用 DateTime()
和格式字符串将字符串转换为 DateTime 对象,然后使用 DateFormat()
以不同格式输出对象
julia> formatted_date = "Tue, 28 Jul 2015 11:43:14" "Tue, 28 Jul 2015 11:43:14" julia> temp = Dates.DateTime(formatted_date, "e, dd u yyyy HH:MM:SS") 2015-07-28T11:43:14 julia> Dates.format(temp, "dd, U, yyyy HH:MM, e") "28, July, 2015 11:43, Tue"
如果您要进行大量的日期格式化(您可以将日期函数应用于字符串数组),最好预先定义一个 DateFormat 对象,然后将其用于批量转换(这更快)
julia> dformat = Dates.DateFormat("y-m-d"); julia> Dates.Date.([ # broadcast "2010-01-01", "2011-03-23", "2012-11-3", "2013-4-13", "2014-9-20", "2015-3-1" ], dformat)
6-element Array{Date,1}: 2010-01-01 2011-03-23 2012-11-03 2013-04-13 2014-09-20 2015-03-01
有一些内置格式可以使用。例如,有 Dates.ISODateTimeFormat
用于为您提供 ISO8601 格式
julia> Dates.DateTime.([ "2010-01-01", "2011-03-23", "2012-11-3", "2013-4-13", "2014-9-20", "2015-3-1" ], Dates.ISODateTimeFormat) 6-element Array{DateTime,1}: 2010-01-01T00:00:00 2011-03-23T00:00:00 2012-11-03T00:00:00 2013-04-13T00:00:00 2014-09-20T00:00:00 2015-03-01T00:00:00
这是传统的 RFC1123
julia> Dates.format(Dates.now(), Dates.RFC1123Format) "Sat, 30 Jul 2016 16:36:09"
有时您需要查找最接近另一个日期的日期 - 例如,那个星期的第一天或包含那个日期的月的最后一天。您可以使用 Dates.firstdayofweek()
和 Dates.lastdayofmonth()
等函数来完成此操作。因此,如果我们目前在周中
julia> Dates.dayname(now()) "Wednesday"
这将返回星期一
julia> Dates.firstdayofweek(now()) 2014-09-01T00:00:00
您也可以使用函数链运算符来编写它
julia> Dates.now() |> Dates.firstdayofweek |> Dates.dayname "Monday"
tofirst()
、tolast()
、tonext()
和 toprev()
方法提供了更通用的解决方案。
使用 `tonext()` 和 `toprev()`,您可以提供一个(可能是匿名的)函数,当日期被正确调整时,该函数返回 true。例如,函数
d->Dates.dayofweek(d) == Dates.Tuesday
如果日期 `d` 是星期二,则返回 true。将此与 `tonext()` 方法一起使用
julia> Dates.tonext(d->Dates.dayofweek(d) == Dates.Tuesday, birthday) 1997-03-18 # the first Tuesday after the birthday
或者您可以找到生日日期之后的下一个星期日
julia> Dates.tonext(d->Dates.dayname(d) == "Sunday", birthday) 1997-03-16 # the first Sunday after the birthday
使用 `tofirst()` 和 `tolast()`,您可以找到一个月的第一个星期日、星期四或任何其他日期。星期一是 1,星期二 2,等等。
julia> Dates.tofirst(birthday, 1) # the first Monday (1) of that month 1997-03-03
提供关键字参数 `of=Year` 以获取一年中的第一个匹配的星期几。
julia> Dates.tofirst(birthday, 1, of=Year) # the first Monday (1) of 1997 1997-01-06
您可以使用 `round()`、`floor()` 和 `ceil()`,通常用于将数字四舍五入到最近的首选值,以调整日期向前或向后,使它们具有“更圆”的值。
julia> Dates.now() 2016-09-12T17:55:11.378 julia> Dates.format(round(Dates.DateTime(Dates.now()), Dates.Minute(15)), Dates.RFC1123Format) "Mon, 12 Sep 2016 18:00:00"
The `ceil()` 函数将日期或时间调整为向前
julia> ceil(birthday, Dates.Month) 1997-04-01 julia> ceil(birthday, Dates.Year) 1998-01-01 julia> ceil(birthday, Dates.Week) 1997-03-17
能够找到一组日期中满足某些特定条件的所有日期非常有用。例如,您可以使用 `Dates.dayofweekofmonth()` 和 `Dates.dayname()` 函数计算出一个月的第二个星期日。
例如,让我们创建一个日期范围,从 2014 年 9 月 1 日到 2014 年圣诞节
julia> dr = Dates.Date(2014,9,1):Dates.Day(1):Dates.Date(2014,12,25) 2014-09-01:1 day:2014-12-25
现在,一个类似于我们之前在 `tonext()` 中使用的匿名函数,找到该范围内满足该函数的日期的选择。
julia> filter(d -> Dates.dayname(d) == "Sunday", dr) 16-element Array{Date,1}: 2014-09-07 2014-09-14 2014-09-21 2014-09-28 2014-10-05 2014-10-12 2014-10-19 2014-10-26 2014-11-02 2014-11-09 2014-11-16 2014-11-23 2014-11-30 2014-12-07 2014-12-14 2014-12-21
这些是 2014 年 9 月 1 日到 2014 年圣诞节之间的每个星期日的日期。
通过在匿名函数中组合条件,您可以构建更复杂的重复事件。这是一个列表,包含该时期内所有在奇数天且大于 20 的星期二
julia> filter(d->Dates.dayname(d) == "Tuesday" && isodd(Dates.day(d)) && Dates.day(d) > 20, dr) 4-element Array{Date,1}: 2014-09-23 2014-10-21 2014-11-25 2014-12-23
以及 2016 年 4 月至 11 月之间的每个第二个星期二
dr = Dates.Date(2015):Dates.Day(1):Dates.Date(2016);
filter(dr) do x
Dates.dayofweek(x) == Dates.Tue &&
Dates.April <= Dates.month(x) <= Dates.Nov &&
Dates.dayofweekofmonth(x) == 2
end
8-element Array{Base.Dates.Date,1}:
2015-04-14
2015-05-12
2015-06-09
2015-07-14
2015-08-11
2015-09-08
2015-10-13
2015-11-10
您有时必须处理另一种时间记录方式:Unix 时间。Unix 时间是从 1970 年(Unix 的诞生)开始经过的秒数。在 Julia 中,计数存储在一个 64 位整数中,我们永远不会看到 Unix 时间的结束。(宇宙将在 64 位 Unix 时间达到最大可能值之前很久就结束了,这将是在 2922 亿年后,即 292,277,026,596 年 12 月 4 日星期日 15:30:08)。
在 Julia 中,`time()` 函数(不带参数使用)返回当前秒的 Unix 时间值
julia> time() 1.414141581230945e9
The `strftime()` ("string format time") function,它存在于 Libc 模块中,将 Unix 时间中的秒数转换为更易读的格式
julia> Libc.strftime(86400 * 365.25 * 4) # 4 years worth of Unix seconds "Tue 1 Jan 00:00:00 1974"
您可以通过提供格式字符串来选择不同的格式,格式字符串中的日期和时间不同部分由“%”字母代码定义
julia> Libc.strftime("%A, %B %e at %T, %Y", 86400 * 365.25 * 4) "Tuesday, January 1 at 00:00:00, 1974"
The `strptime()` 函数接收格式字符串和日期字符串,并返回一个 TmStruct 表达式。然后可以通过将其传递给 `time()` 来将其转换为 Unix 时间值
julia> Libc.strptime("%A, %B %e at %T, %Y", "Tuesday, January 1 at 00:00:00, 1974") Base.Libc.TmStruct(0,0,0,1,0,74,2,0,0,0,0,0,0,0) julia> time(ans) 1.262304e8 julia> time(Libc.strptime("%Y-%m-%d","2014-10-1")) 1.4121216e9
Dates 模块还提供了一个 `unix2datetime()` 函数,该函数将 Unix 时间值转换为日期/时间对象
julia> Dates.unix2datetime(time()) 2014-10-24T09:26:29.305
`DateTime` 以毫秒的形式存储在 `instant` 字段中。使用 `Dates.value` 获取值。
julia> moment=Dates.now() 2017-02-01T12:45:46.326 julia> Dates.value(moment) 63621636346326
julia> moment.instant Base.Dates.UTInstant{Base.Dates.Millisecond}(63621636346326 milliseconds)
如果您使用更精确的 `Dates.Time` 类型,您可以访问纳秒。
julia> moment = Dates.Time(Dates.now()) 17:38:44.33
julia> Dates.value(moment) 63524330000000 julia> moment.instant 63524330000000 nanoseconds
The `@elapsed` 宏返回表达式求值所花费的秒数
function test(n)
for i in 1:n
x = sin(rand())
end
end
julia> @elapsed test(100000000) 1.309819509
The `@time` 宏会告诉您表达式求值花费了多长时间以及分配了多少内存。
julia> @time test(100000000) 2.532941 seconds (4 allocations: 160 bytes)