• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37806

13 Jun 2024 02:10AM UTC coverage: 87.473% (+0.05%) from 87.422%
#37806

push

local

web-flow
[TOML] remove Dates hack, replace with explicit usage (#54755)

This hack will cease functioning soon (#54739), so it must be removed so
that packages can stop relying on it.

As an aid, now provide some basic conversion and printing support for
the Base.TOML variants of this as well such that users can round-trip
the contents (if they are well-formed as a date/time).

9 of 21 new or added lines in 4 files covered. (42.86%)

78 existing lines in 8 files now uncovered.

77134 of 88180 relevant lines covered (87.47%)

16006262.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.61
/stdlib/Dates/src/types.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
abstract type AbstractTime end
4

5
"""
6
    Period
7
    Year
8
    Quarter
9
    Month
10
    Week
11
    Day
12
    Hour
13
    Minute
14
    Second
15
    Millisecond
16
    Microsecond
17
    Nanosecond
18

19
`Period` types represent discrete, human representations of time.
20
"""
21
abstract type Period     <: AbstractTime end
22

23
"""
24
    DatePeriod
25
    Year
26
    Quarter
27
    Month
28
    Week
29
    Day
30

31
Intervals of time greater than or equal to a day.
32
Conventional comparisons between `DatePeriod`s are not all valid.
33
(eg `Week(1) == Day(7)`, but `Year(1) != Day(365)`)
34
"""
35
abstract type DatePeriod <: Period end
36

37
"""
38
    TimePeriod
39
    Hour
40
    Minute
41
    Second
42
    Millisecond
43
    Microsecond
44
    Nanosecond
45

46
Intervals of time less than a day.
47
Conversions between all `TimePeriod`s are permissible.
48
(eg `Hour(1) == Minute(60) == Second(3600)`)
49
"""
50
abstract type TimePeriod <: Period end
51

52
for T in (:Year, :Quarter, :Month, :Week, :Day)
53
    @eval struct $T <: DatePeriod
54
        value::Int64
55
        $T(v::Number) = new(v)
6,467,343✔
56
    end
57
end
58
for T in (:Hour, :Minute, :Second, :Millisecond, :Microsecond, :Nanosecond)
59
    @eval struct $T <: TimePeriod
60
        value::Int64
61
        $T(v::Number) = new(v)
1,855,085✔
62
    end
63
end
64

65
"""
66
    Year(v)
67
    Quarter(v)
68
    Month(v)
69
    Week(v)
70
    Day(v)
71
    Hour(v)
72
    Minute(v)
73
    Second(v)
74
    Millisecond(v)
75
    Microsecond(v)
76
    Nanosecond(v)
77

78
Construct a `Period` type with the given `v` value. Input must be losslessly convertible
79
to an [`Int64`](@ref).
80
"""
81
Period(v)
82

83
"""
84
    Instant
85

86
`Instant` types represent integer-based, machine representations of time as continuous
87
timelines starting from an epoch.
88
"""
89
abstract type Instant <: AbstractTime end
90

91
"""
92
    UTInstant{T}
93

94
The `UTInstant` represents a machine timeline based on UT time (1 day = one revolution of
95
the earth). The `T` is a `Period` parameter that indicates the resolution or precision of
96
the instant.
97
"""
98
struct UTInstant{P<:Period} <: Instant
99
    periods::P
4,162,042✔
100
end
101

102
# Convenience default constructors
103
UTM(x) = UTInstant(Millisecond(x))
1,090,463✔
104
UTD(x) = UTInstant(Day(x))
3,071,573✔
105

106
# Calendar types provide rules for interpreting instant
107
# timelines in human-readable form.
108
abstract type Calendar <: AbstractTime end
109

110
# ISOCalendar implements the ISO 8601 standard (en.wikipedia.org/wiki/ISO_8601)
111
# Notably based on the proleptic Gregorian calendar
112
# ISOCalendar provides interpretation rules for UTInstants to civil date and time parts
113
struct ISOCalendar <: Calendar end
114

115
"""
116
    TimeZone
117

118
Geographic zone generally based on longitude determining what the time is at a certain location.
119
Some time zones observe daylight savings (eg EST -> EDT).
120
For implementations and more support, see the [`TimeZones.jl`](https://github.com/JuliaTime/TimeZones.jl) package
121
"""
122
abstract type TimeZone end
123

124
"""
125
    UTC
126

127
`UTC`, or Coordinated Universal Time, is the [`TimeZone`](@ref) from which all others are measured.
128
It is associated with the time at 0° longitude. It is not adjusted for daylight savings.
129
"""
130
struct UTC <: TimeZone end
1✔
131

132
"""
133
    TimeType
134

135
`TimeType` types wrap `Instant` machine instances to provide human representations of the
136
machine instant. `Time`, `DateTime` and `Date` are subtypes of `TimeType`.
137
"""
138
abstract type TimeType <: AbstractTime end
139

140
abstract type AbstractDateTime <: TimeType end
141

142
"""
143
    DateTime
144

145
`DateTime` represents a point in time according to the proleptic Gregorian calendar.
146
The finest resolution of the time is millisecond (i.e., microseconds or
147
nanoseconds cannot be represented by this type). The type supports fixed-point
148
arithmetic, and thus is prone to underflowing (and overflowing). A notable
149
consequence is rounding when adding a `Microsecond` or a `Nanosecond`:
150

151
```jldoctest
152
julia> dt = DateTime(2023, 8, 19, 17, 45, 32, 900)
153
2023-08-19T17:45:32.900
154

155
julia> dt + Millisecond(1)
156
2023-08-19T17:45:32.901
157

158
julia> dt + Microsecond(1000) # 1000us == 1ms
159
2023-08-19T17:45:32.901
160

161
julia> dt + Microsecond(999) # 999us rounded to 1000us
162
2023-08-19T17:45:32.901
163

164
julia> dt + Microsecond(1499) # 1499 rounded to 1000us
165
2023-08-19T17:45:32.901
166
```
167
"""
168
struct DateTime <: AbstractDateTime
169
    instant::UTInstant{Millisecond}
170
    DateTime(instant::UTInstant{Millisecond}) = new(instant)
1,090,464✔
171
end
172

173
"""
174
    Date
175

176
`Date` wraps a `UTInstant{Day}` and interprets it according to the proleptic Gregorian calendar.
177
"""
178
struct Date <: TimeType
179
    instant::UTInstant{Day}
180
    Date(instant::UTInstant{Day}) = new(instant)
3,071,574✔
181
end
182

183
"""
184
    Time
185

186
`Time` wraps a `Nanosecond` and represents a specific moment in a 24-hour day.
187
"""
188
struct Time <: TimeType
189
    instant::Nanosecond
190
    Time(instant::Nanosecond) = new(mod(instant, 86400000000000))
37,886✔
191
end
192

193
# Convert y,m,d to # of Rata Die days
194
# Works by shifting the beginning of the year to March 1,
195
# so a leap day is the very last day of the year
196
const SHIFTEDMONTHDAYS = (306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275)
197
function totaldays(y, m, d)
191✔
198
    # If we're in Jan/Feb, shift the given year back one
199
    z = m < 3 ? y - 1 : y
4,439,636✔
200
    mdays = SHIFTEDMONTHDAYS[m]
4,439,636✔
201
    # days + month_days + year_days
202
    return d + mdays + 365z + fld(z, 4) - fld(z, 100) + fld(z, 400) - 306
4,439,636✔
203
end
204

205
# If the year is divisible by 4, except for every 100 years, except for every 400 years
206
isleapyear(y) = (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0))
265,962✔
207

208
# Number of days in month
209
const DAYSINMONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
210
daysinmonth(y,m) = DAYSINMONTH[m] + (m == 2 && isleapyear(y))
6,068,189✔
211

212
### UTILITIES ###
213

214
# These are necessary because the type constructors for TimeType subtypes can
215
# throw, and we want to be able to use tryparse without requiring a try/catch.
216
# This is made easier by providing a helper function that checks arguments, so
217
# we can validate arguments in tryparse.
218

219
"""
220
    validargs(::Type{<:TimeType}, args...) -> Union{ArgumentError, Nothing}
221

222
Determine whether the given arguments constitute valid inputs for the given type.
223
Returns either an `ArgumentError`, or [`nothing`](@ref) in case of success.
224
"""
225
function validargs end
226

227
# Julia uses 24-hour clocks internally, but user input can be AM/PM with 12pm == noon and 12am == midnight.
228
@enum AMPM AM PM TWENTYFOURHOUR
229
function adjusthour(h::Int64, ampm::AMPM)
108✔
230
    ampm == TWENTYFOURHOUR && return h
914,336✔
231
    ampm == PM && h < 12 && return h + 12
24✔
232
    ampm == AM && h == 12 && return Int64(0)
20✔
233
    return h
12✔
234
end
235

236
### CONSTRUCTORS ###
237
# Core constructors
238
"""
239
    DateTime(y, [m, d, h, mi, s, ms]) -> DateTime
240

241
Construct a `DateTime` type by parts. Arguments must be convertible to [`Int64`](@ref).
242
"""
243
function DateTime(y::Int64, m::Int64=1, d::Int64=1,
914,263✔
244
                  h::Int64=0, mi::Int64=0, s::Int64=0, ms::Int64=0, ampm::AMPM=TWENTYFOURHOUR)
245
    err = validargs(DateTime, y, m, d, h, mi, s, ms, ampm)
1,907,329✔
246
    err === nothing || throw(err)
953,746✔
247
    h = adjusthour(h, ampm)
953,722✔
248
    rata = ms + 1000 * (s + 60mi + 3600h + 86400 * totaldays(y, m, d))
953,710✔
249
    return DateTime(UTM(rata))
953,710✔
250
end
251

252
function validargs(::Type{DateTime}, y::Int64, m::Int64, d::Int64,
913,590✔
253
                   h::Int64, mi::Int64, s::Int64, ms::Int64, ampm::AMPM=TWENTYFOURHOUR)
254
    0 < m < 13 || return ArgumentError("Month: $m out of range (1:12)")
953,732✔
255
    0 < d < daysinmonth(y, m) + 1 || return ArgumentError("Day: $d out of range (1:$(daysinmonth(y, m)))")
953,729✔
256
    if ampm == TWENTYFOURHOUR # 24-hour clock
953,723✔
257
        -1 < h < 24 || (h == 24 && mi==s==ms==0) ||
953,719✔
258
            return ArgumentError("Hour: $h out of range (0:23)")
259
    else
260
        0 < h < 13 || return ArgumentError("Hour: $h out of range (1:12)")
12✔
261
    end
262
    -1 < mi < 60 || return ArgumentError("Minute: $mi out of range (0:59)")
953,719✔
263
    -1 < s < 60 || return ArgumentError("Second: $s out of range (0:59)")
953,717✔
264
    -1 < ms < 1000 || return ArgumentError("Millisecond: $ms out of range (0:999)")
953,715✔
265
    return nothing
953,711✔
266
end
267

268
DateTime(dt::Base.Libc.TmStruct) = DateTime(1900 + dt.year, 1 + dt.month, dt.mday, dt.hour, dt.min, dt.sec)
1✔
269

270
"""
271
    Date(y, [m, d]) -> Date
272

273
Construct a `Date` type by parts. Arguments must be convertible to [`Int64`](@ref).
274
"""
275
function Date(y::Int64, m::Int64=1, d::Int64=1)
546✔
276
    err = validargs(Date, y, m, d)
2,758,833✔
277
    err === nothing || throw(err)
2,758,663✔
278
    return Date(UTD(totaldays(y, m, d)))
2,758,647✔
279
end
280

281
function validargs(::Type{Date}, y::Int64, m::Int64, d::Int64)
2,758,506✔
282
    0 < m < 13 || return ArgumentError("Month: $m out of range (1:12)")
2,758,660✔
283
    0 < d < daysinmonth(y, m) + 1 || return ArgumentError("Day: $d out of range (1:$(daysinmonth(y, m)))")
2,758,657✔
284
    return nothing
2,758,647✔
285
end
286

287
Date(dt::Base.Libc.TmStruct) = Date(1900 + dt.year, 1 + dt.month, dt.mday)
1✔
288

289
"""
290
    Time(h, [mi, s, ms, us, ns]) -> Time
291

292
Construct a `Time` type by parts. Arguments must be convertible to [`Int64`](@ref).
293
"""
294
function Time(h::Int64, mi::Int64=0, s::Int64=0, ms::Int64=0, us::Int64=0, ns::Int64=0, ampm::AMPM=TWENTYFOURHOUR)
105✔
295
    err = validargs(Time, h, mi, s, ms, us, ns, ampm)
1,388✔
296
    err === nothing || throw(err)
735✔
297
    h = adjusthour(h, ampm)
711✔
298
    return Time(Nanosecond(ns + 1000us + 1000000ms + 1000000000s + 60000000000mi + 3600000000000h))
699✔
299
end
300

301
function validargs(::Type{Time}, h::Int64, mi::Int64, s::Int64, ms::Int64, us::Int64, ns::Int64, ampm::AMPM=TWENTYFOURHOUR)
675✔
302
    if ampm == TWENTYFOURHOUR # 24-hour clock
717✔
303
        -1 < h < 24 || return ArgumentError("Hour: $h out of range (0:23)")
705✔
304
    else
305
        0 < h < 13 || return ArgumentError("Hour: $h out of range (1:12)")
15✔
306
    end
307
    -1 < mi < 60 || return ArgumentError("Minute: $mi out of range (0:59)")
714✔
308
    -1 < s < 60 || return ArgumentError("Second: $s out of range (0:59)")
711✔
309
    -1 < ms < 1000 || return ArgumentError("Millisecond: $ms out of range (0:999)")
707✔
310
    -1 < us < 1000 || return ArgumentError("Microsecond: $us out of range (0:999)")
705✔
311
    -1 < ns < 1000 || return ArgumentError("Nanosecond: $ns out of range (0:999)")
703✔
312
    return nothing
699✔
313
end
314

315
Time(dt::Base.Libc.TmStruct) = Time(dt.hour, dt.min, dt.sec)
7✔
316

317
# Convenience constructors from Periods
318
function DateTime(y::Year, m::Month=Month(1), d::Day=Day(1),
7✔
319
                  h::Hour=Hour(0), mi::Minute=Minute(0),
320
                  s::Second=Second(0), ms::Millisecond=Millisecond(0))
321
    return DateTime(value(y), value(m), value(d),
31✔
322
                    value(h), value(mi), value(s), value(ms))
323
end
324

325
Date(y::Year, m::Month=Month(1), d::Day=Day(1)) = Date(value(y), value(m), value(d))
11✔
326

327
function Time(h::Hour, mi::Minute=Minute(0), s::Second=Second(0),
6✔
328
              ms::Millisecond=Millisecond(0),
329
              us::Microsecond=Microsecond(0), ns::Nanosecond=Nanosecond(0))
330
    return Time(value(h), value(mi), value(s), value(ms), value(us), value(ns))
39✔
331
end
332

333
# To allow any order/combination of Periods
334

335
"""
336
    DateTime(periods::Period...) -> DateTime
337

338
Construct a `DateTime` type by `Period` type parts. Arguments may be in any order. DateTime
339
parts not provided will default to the value of `Dates.default(period)`.
340
"""
341
function DateTime(period::Period, periods::Period...)
3✔
342
    y = Year(1); m = Month(1); d = Day(1)
3✔
343
    h = Hour(0); mi = Minute(0); s = Second(0); ms = Millisecond(0)
3✔
344
    for p in (period, periods...)
3✔
345
        isa(p, Year) && (y = p::Year)
12✔
346
        isa(p, Month) && (m = p::Month)
12✔
347
        isa(p, Day) && (d = p::Day)
12✔
348
        isa(p, Hour) && (h = p::Hour)
12✔
349
        isa(p, Minute) && (mi = p::Minute)
12✔
350
        isa(p, Second) && (s = p::Second)
12✔
351
        isa(p, Millisecond) && (ms = p::Millisecond)
12✔
352
    end
15✔
353
    return DateTime(y, m, d, h, mi, s, ms)
3✔
354
end
355

356
"""
357
    Date(period::Period...) -> Date
358

359
Construct a `Date` type by `Period` type parts. Arguments may be in any order. `Date` parts
360
not provided will default to the value of `Dates.default(period)`.
361
"""
362
function Date(period::Period, periods::Period...)
5✔
363
    y = Year(1); m = Month(1); d = Day(1)
5✔
364
    for p in (period, periods...)
5✔
365
        isa(p, Year) && (y = p::Year)
10✔
366
        isa(p, Month) && (m = p::Month)
10✔
367
        isa(p, Day) && (d = p::Day)
10✔
368
    end
14✔
369
    return Date(y, m, d)
5✔
370
end
371

372
"""
373
    Time(period::TimePeriod...) -> Time
374

375
Construct a `Time` type by `Period` type parts. Arguments may be in any order. `Time` parts
376
not provided will default to the value of `Dates.default(period)`.
377
"""
378
function Time(period::TimePeriod, periods::TimePeriod...)
3✔
379
    h = Hour(0); mi = Minute(0); s = Second(0)
3✔
380
    ms = Millisecond(0); us = Microsecond(0); ns = Nanosecond(0)
3✔
381
    for p in (period, periods...)
3✔
382
        isa(p, Hour) && (h = p::Hour)
15✔
383
        isa(p, Minute) && (mi = p::Minute)
15✔
384
        isa(p, Second) && (s = p::Second)
15✔
385
        isa(p, Millisecond) && (ms = p::Millisecond)
15✔
386
        isa(p, Microsecond) && (us = p::Microsecond)
15✔
387
        isa(p, Nanosecond) && (ns = p::Nanosecond)
15✔
388
    end
18✔
389
    return Time(h, mi, s, ms, us, ns)
3✔
390
end
391

392
# Convenience constructor for DateTime from Date and Time
393
"""
394
    DateTime(d::Date, t::Time)
395

396
Construct a `DateTime` type by `Date` and `Time`.
397
Non-zero microseconds or nanoseconds in the `Time` type will result in an
398
`InexactError`.
399

400
!!! compat "Julia 1.1"
401
    This function requires at least Julia 1.1.
402

403
```jldoctest
404
julia> d = Date(2018, 1, 1)
405
2018-01-01
406

407
julia> t = Time(8, 15, 42)
408
08:15:42
409

410
julia> DateTime(d, t)
411
2018-01-01T08:15:42
412
```
413
"""
414
function DateTime(dt::Date, t::Time)
9✔
415
    (microsecond(t) > 0 || nanosecond(t) > 0) && throw(InexactError(:DateTime, DateTime, t))
9✔
416
    y, m, d = yearmonthday(dt)
7✔
417
    return DateTime(y, m, d, hour(t), minute(t), second(t), millisecond(t))
7✔
418
end
419

420
# Fallback constructors
421
DateTime(y, m=1, d=1, h=0, mi=0, s=0, ms=0, ampm::AMPM=TWENTYFOURHOUR) = DateTime(Int64(y), Int64(m), Int64(d), Int64(h), Int64(mi), Int64(s), Int64(ms), ampm)
66✔
422
Date(y, m=1, d=1) = Date(Int64(y), Int64(m), Int64(d))
30✔
423
Time(h, mi=0, s=0, ms=0, us=0, ns=0, ampm::AMPM=TWENTYFOURHOUR) = Time(Int64(h), Int64(mi), Int64(s), Int64(ms), Int64(us), Int64(ns), ampm)
14✔
424

425
# Traits, Equality
426
Base.isfinite(::Union{Type{T}, T}) where {T<:TimeType} = true
×
427
calendar(dt::DateTime) = ISOCalendar
×
428
calendar(dt::Date) = ISOCalendar
×
429

430
"""
431
    eps(::Type{DateTime}) -> Millisecond
432
    eps(::Type{Date}) -> Day
433
    eps(::Type{Time}) -> Nanosecond
434
    eps(::TimeType) -> Period
435

436
Return the smallest unit value supported by the `TimeType`.
437

438
# Examples
439
```jldoctest
440
julia> eps(DateTime)
441
1 millisecond
442

443
julia> eps(Date)
444
1 day
445

446
julia> eps(Time)
447
1 nanosecond
448
```
449
"""
450
Base.eps(::Union{Type{DateTime}, Type{Date}, Type{Time}, TimeType})
451

452
Base.eps(::Type{DateTime}) = Millisecond(1)
2✔
453
Base.eps(::Type{Date}) = Day(1)
2✔
454
Base.eps(::Type{Time}) = Nanosecond(1)
2✔
455
Base.eps(::T) where T <: TimeType = eps(T)::Period
3✔
456

457
# zero returns dt::T - dt::T
458
Base.zero(::Type{DateTime}) = Millisecond(0)
7✔
459
Base.zero(::Type{Date}) = Day(0)
7✔
460
Base.zero(::Type{Time}) = Nanosecond(0)
7✔
461
Base.zero(::T) where T <: TimeType = zero(T)::Period
18✔
462

463

464
Base.typemax(::Union{DateTime, Type{DateTime}}) = DateTime(146138512, 12, 31, 23, 59, 59)
27✔
465
Base.typemin(::Union{DateTime, Type{DateTime}}) = DateTime(-146138511, 1, 1, 0, 0, 0)
25✔
466
Base.typemax(::Union{Date, Type{Date}}) = Date(252522163911149, 12, 31)
20✔
467
Base.typemin(::Union{Date, Type{Date}}) = Date(-252522163911150, 1, 1)
19✔
468
Base.typemax(::Union{Time, Type{Time}}) = Time(23, 59, 59, 999, 999, 999)
15✔
469
Base.typemin(::Union{Time, Type{Time}}) = Time(0)
15✔
470
# Date-DateTime promotion, isless, ==
471
Base.promote_rule(::Type{Date}, x::Type{DateTime}) = DateTime
×
472
Base.isless(x::T, y::T) where {T<:TimeType} = isless(value(x), value(y))
1,057,139,145✔
473
Base.isless(x::TimeType, y::TimeType) = isless(promote(x, y)...)
5✔
474
(==)(x::T, y::T) where {T<:TimeType} = (==)(value(x), value(y))
1,055,570,681✔
475
(==)(x::TimeType, y::TimeType) = (===)(promote(x, y)...)
10✔
476
Base.min(x::AbstractTime) = x
5✔
477
Base.max(x::AbstractTime) = x
5✔
478
Base.minmax(x::AbstractTime) = (x, x)
5✔
479
Base.hash(x::Time, h::UInt) =
250✔
480
    hash(hour(x), hash(minute(x), hash(second(x),
481
        hash(millisecond(x), hash(microsecond(x), hash(nanosecond(x), h))))))
482

483
Base.sleep(duration::Period) = sleep(seconds(duration))
×
484

485
function Base.Timer(delay::Period; interval::Period=Second(0))
×
486
    Timer(seconds(delay), interval=seconds(interval))
×
487
end
488

489
function Base.timedwait(testcb, timeout::Period; pollint::Period=Millisecond(100))
1✔
490
    timedwait(testcb, seconds(timeout), pollint=seconds(pollint))
1✔
491
end
492

493
Base.OrderStyle(::Type{<:AbstractTime}) = Base.Ordered()
×
494
Base.ArithmeticStyle(::Type{<:AbstractTime}) = Base.ArithmeticWraps()
×
495

496
# minimal Base.TOML support
NEW
497
Date(d::Base.TOML.Date) = Date(d.year, d.month, d.day)
×
NEW
498
Time(t::Base.TOML.Time) = Time(t.hour, t.minute, t.second, t.ms)
×
NEW
499
DateTime(dt::Base.TOML.DateTime) = DateTime(Date(dt.date), Time(dt.time))
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc