2026a

# 日期和时间算术运算


此示例演示了如何进行日期和时间值的加减运算,以此计算未来和过去的日期以及以精确单位或日历单位计量的流逝的持续时间。日期和时间数组可以进行加、减、乘、除运算,这些运算符的使用方法与其他 Syslab 数据类型相同。但是,日期和时间存在一些特定行为。

# 为日期时间数组加上或减去一定的持续时间

创建一个日期时间标量。默认情况下,日期时间数组并未与时区关联。

using Printf
using TyBase
t1 = DateTime("31-Jan-2020 22:40:49", dateformat"dd-u-yyyy HH:MM:SS")
t1 =
2020-01-31T22:40:49

为其加上时数序列以求得未来的时间点。

t2 = t1 + Dates.Hour.(1:3)
t2 =
3-element Vector{DateTime}:
 2020-01-31T23:40:49
 2020-02-01T00:40:49
 2020-02-01T01:40:49

验证 t2 中每一对日期时间值的差是否为 1 小时。

dt = diff(t2)
Time(0) .+ dt
ans =
2-element Vector{Time}:
 01:00:00
 01:00:00

diff 返回由小时数、分钟数和秒数构成的精确持续时间。

从日期时间值减去分钟数序列以查找过去的时间点。

t2 = t1 .- Dates.Minute.(20:10:40)
t2 =
3-element Vector{DateTime}:
 2020-01-31T22:20:49
 2020-01-31T22:10:49
 2020-01-31T22:00:49

将数值数组与 datetime 数组相加。Syslab 将数值数组中的每个值视作 24 小时一天的精确天数。

t2 = t1 .+ Dates.Day.(1:3)
t2 =
3-element Vector{DateTime}:
 2020-02-01T22:40:49
 2020-02-02T22:40:49
 2020-02-03T22:40:49

# 包含时区的日期时间值加法

如要处理不同时区的日期时间值,或是要考虑夏令时更改,请使用关联了时区的日期时间数组。创建一个 datetime 标量,表示纽约时间 2014 年 3 月 8 日。

using TimeZones
t1 = ZonedDateTime(2014, 3, 8, 0, 0, 0, tz"America/New_York")
t1 = 
2014-03-08T00:00:00-05:00

通过与天数序列(每一天固定 24 小时)相加查找未来时间点。

t2 = t1 + Dates.Day.(0:2)
t2 = 
3-element Vector{ZonedDateTime}:
 2014-03-08T00:00:00-05:00
 2014-03-09T00:00:00-05:00
 2014-03-10T00:00:00-04:00

由于夏令时变化发生在 2014 年 3 月 9 日,因此 t2 中的第三个日期时间不是在午夜。

验证 t2 中每一对日期时间值的差是否为 24 小时。

dt = diff(DateTime.(t2))
dt = 
2-element Vector{Millisecond}:
 86400000 milliseconds
 86400000 milliseconds

通过将日期时间值与 years、hours、minutes 和 seconds 等函数的输出结果相加,您还可以分别为其加上以年份、小时、分钟和秒等其他单位表示的固定长度持续时间。

如要考虑夏令时更改,则您应当用日历持续时间代替持续时间。将日期时间值与日历持续时间相加减时,日历持续时间会考虑夏令时变化。

将多个日历天与 t1 相加。

t3 = t1 + Dates.Day.(0:2)
t3 = 
3-element Vector{ZonedDateTime}:
 2014-03-08T00:00:00-05:00
 2014-03-09T00:00:00-05:00
 2014-03-10T00:00:00-04:00

查看 t3 中每一对日期时间值之间的差并非始终为 24 小时,这是因为夏令时变化发生在 3 月 9 日。

dt = diff(t3)
dt = 
2-element Vector{Millisecond}:
 86400000 milliseconds
 82800000 milliseconds

# 将日历持续时间与日期时间数组相加

将多个日历月与 2014 年 1 月 31 日相加。

t1 = DateTime(2014, 1, 31)
t1 = 
2014-01-31T00:00:00
t2 = t1 + Dates.Month.(1:4)
t2 = 
4-element Vector{DateTime}:
 2014-02-28T00:00:00
 2014-03-31T00:00:00
 2014-04-30T00:00:00
 2014-05-31T00:00:00

t2 中的每个日期时间都出现在每个月的最后一天。

使用 caldatediff 函数计算 t2 中每一对日期时间值在日历天数上的差值。

function caldatediff(A, comp)
    B = map(i -> A[i] - A[i - 1], 2:length(A))
    B = convert.(comp, B)
    return B
end
dt = caldatediff(t2, Dates.Day)
dt = 
3-element Vector{Day}:
 31 days
 30 days
 31 days

dt 中连续的日期时间值对之间相差的天数并非始终相同,这是因为不同的月份由不同的天数组成。

将多个日历年与 2014 年 1 月 31 日相加。

t2 = t1 + Dates.Year.(0:4)
t2 = 
5-element Vector{DateTime}:
 2014-01-31T00:00:00
 2015-01-31T00:00:00
 2016-01-31T00:00:00
 2017-01-31T00:00:00
 2018-01-31T00:00:00

使用 caldatediff 函数计算 t2 中每一对日期时间值在日历天数上的差值。

dt = caldatediff(t2, Dates.Day)
dt = 
4-element Vector{Day}:
 365 days
 365 days
 366 days
 365 days

dt 中连续的日期时间值对之间相差天数并非始终相同,这是因为 2016 年是闰年,有 366 天。

您可以使用 quarter、week 和 day 函数创建日历季度、日历周或日历天数组,然后将其与日期时间数组相加或相减。

日历持续时间相加时不适用加法交换律。当您将一个以上的 calendarDuration 数组与日期时间值相加时,Syslab 会按其在命令中出现的顺序相加。

在 2014 年 1 月 31 日基础上先加上 3 个日历月,再加上 30 个日历天。

t2 = DateTime(2014, 1, 31) + Dates.Month(3) + Dates.Day(30)
t2 = 
2014-05-30T00:00:00

# 日历持续时间算术运算

创建两个日历持续时间,然后对其求和。

d1 = Dates.Year(1) + Dates.Month(2) + Dates.Day(20)
d1 = 
1 year, 2 months, 20 days
d2 = Dates.Month(11) + Dates.Day(23)
d2 = 
11 months, 23 days
d = d1 + d2
d = 
1 year, 13 months, 43 days
d + d 
ans = 
2 years, 26 months, 86 days

# 以精确单位计算流逝时间

将一个 datetime 数组与另一个相减,按精确的小时、分钟和秒数计算流逝的时间。

求出日期时间值序列与前一天起始时间之间的精确时长。

now_day = DateTime("2020-01-31T22:40:50")
t2 = now_day + Dates.Day.(1:3)
t2 = 
3-element Vector{DateTime}:
 2020-02-01T22:40:50
 2020-02-02T22:40:50
 2020-02-03T22:40:50
t1 = DateTime("2020-01-31") - Dates.Day(1)
t1 = 
2020-01-30T00:00:00
function format_hms(dur)
    if !(typeof(dur) <: TimePeriod)
        return NaN
    end

    h = 0; m = 0; s = 0; ms = 0

    i = Millisecond(dur).value # 取毫秒值
    ms = i % 1000

    i = round(Int, (i - ms) / 1000) # 转为秒
    h = floor(Int, i / 3600)
    i = i - h * 3600
    m = floor(Int, i / 60)
    s = i - m * 60
    
    if ms == 0
        res = @sprintf("%02d:%02d:%02d", h,m,s) 
    else
        res = @sprintf("%02d:%02d:%02d.%d", h,m,s, ms)
    end
    return res
end
dt = t2 - t1
format_hms.(dt)
ans = 
3-element Vector{String}:
 "70:40:50"
 "94:40:50"
 "118:40:50"
function format_days(dur)
    if !(typeof(dur) <: TimePeriod)
        return NaN
    end

    ms = Millisecond(dur).value # 取毫秒值
    days = ms / (24*3600*1000)
    
    res = @sprintf("%f days", days)
    return res
end
format_days.(dt)
ans = 
3-element Vector{String}:
 "2.945023 days"
 "3.945023 days"
 "4.945023 days"

dt 以“时:分:秒”的格式存储持续时间。

dt = t2 - t1
format_hms.(dt)
ans = 
3-element Vector{String}:
 "70:40:50"
 "94:40:50"
 "118:40:50"

通过更改 dt 的 Format 属性,以天为单位查看流逝的持续时间。

format_days.(dt)
ans = 
3-element Vector{String}:
 "2.945023 days"
 "3.945023 days"
 "4.945023 days"

用因子 1.2 乘以 dt 以扩大持续时间的值。由于持续时间具有精确长度,您可以将其与小数值相乘和相除。

dt2 = 1.2*dt
format_days.(dt2)
ans = 
3-element Vector{String}:
 "3.534028 days"
 "4.734028 days"
 "5.934028 days"

# 以日历单位计算流逝时间

求两个日期之间流逝的日历年、月和天数。

t1 = DateTime("2020-1-31")
t1 = 
2020-01-31T00:00:00
t2 = t1 + Dates.Month.(0:2) + Dates.Day(4)
t2 = 
3-element Vector{DateTime}:
 2020-02-04T00:00:00
 2020-03-04T00:00:00
 2020-04-04T00:00:00
dt = t2-t1 
canonicalize.(Dates.CompoundPeriod.(dt))
ans = 
3-element Vector{Dates.CompoundPeriod}:
 4 days
 4 weeks, 5 days
 9 weeks, 1 day

# 另请参阅

between | diff