跳至内容

介绍 Julia/使用日期和时间

来自 Wikibooks,开放世界中的开放书籍
Previous page
使用文本文件
介绍 Julia Next page
绘图
使用日期和时间

使用日期和时间

[编辑 | 编辑源代码]

用于处理日期和时间的函数在标准包 Dates 中提供。要使用任何时间和日期函数,您必须执行以下操作之一

  • using Dates
  • import Dates

如果您使用 import Dates 函数,则需要在每个函数前面加上明确的 Dates. 前缀,例如 Dates.dayofweek(dt),如本章所示。但是,如果您在代码中添加 using Dates 行,这会将所有导出的 Dates 函数引入 Main,并且可以使用它们而无需 Dates. 前缀。

此图显示了用于存储时间、日期和日期时间的各种类型之间的关系。

Shows the hierarchy of date and date-time types in Julia
显示了 Julia 中日期和日期时间类型的层次结构

日期、时间和日期时间

[编辑 | 编辑源代码]

有三种主要的数据类型可用

  • 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.DayDates.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 时间。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)
华夏公益教科书