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

JuliaLang / julia / #38039

31 Mar 2025 08:00AM UTC coverage: 20.268% (-0.02%) from 20.292%
#38039

push

local

web-flow
`_precompilepkgs`: interactive progress display: fix unintended capture (#57932)

The variable `str` also exists in one of the enclosing closures. Use a
new variable, as was surely intended, instead of capturing and mutating
the `str`.

Improves the sysimage's resistance to invalidation.

9938 of 49034 relevant lines covered (20.27%)

98941.38 hits per line

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

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

3
"""
4
    AbstractDateToken
5

6
A token used in parsing or formatting a date time string. Each subtype must
7
define the tryparsenext and format methods.
8

9
"""
10
abstract type AbstractDateToken end
11

12
"""
13
    tryparsenext(tok::AbstractDateToken, str::String, i::Int, len::Int, locale::DateLocale)
14

15
`tryparsenext` parses for the `tok` token in `str` starting at index `i`.
16
`len` is the length of the string.  parsing can be optionally based on the
17
`locale`. If a `tryparsenext` method does not need a locale, it can leave
18
the argument out in the method definition.
19

20
If parsing succeeds, returns a tuple of 2 elements `(res, idx)`, where:
21

22
* `res` is the result of the parsing.
23
* `idx::Int`, is the index _after_ the index at which parsing ended.
24
"""
25
function tryparsenext end
26

27
"""
28
    format(io::IO, tok::AbstractDateToken, dt::TimeType, locale)
29

30
Format the `tok` token from `dt` and write it to `io`. The formatting can
31
be based on `locale`.
32

33
All subtypes of `AbstractDateToken` must define this method in order
34
to be able to print a Date / DateTime object according to a `DateFormat`
35
containing that token.
36
"""
37
format(io::IO, tok::AbstractDateToken, dt::TimeType, locale)
38

39
# fallback to tryparsenext/format methods that don't care about locale
40
@inline function tryparsenext(d::AbstractDateToken, str, i, len, locale)
×
41
    return tryparsenext(d, str, i, len)
×
42
end
43

44
function Base.string(t::Time)
×
45
    h, mi, s = hour(t), minute(t), second(t)
×
46
    hh = lpad(h, 2, "0")
×
47
    mii = lpad(mi, 2, "0")
×
48
    ss = lpad(s, 2, "0")
×
49
    nss = tons(Millisecond(t)) + tons(Microsecond(t)) + tons(Nanosecond(t))
×
50
    ns = nss == 0 ? "" : rstrip(@sprintf("%.9f", nss / 1e+9)[2:end], '0')
×
51
    return "$hh:$mii:$ss$ns"
×
52
end
53

54
Base.show(io::IO, ::MIME"text/plain", t::Time) = print(io, t)
×
55
Base.print(io::IO, t::Time) = print(io, string(t))
×
56

57
function Base.show(io::IO, t::Time)
×
58
    if get(io, :compact, false)::Bool
×
59
        print(io, t)
×
60
    else
61
        values = [
×
62
            hour(t)
63
            minute(t)
64
            second(t)
65
            millisecond(t)
66
            microsecond(t)
67
            nanosecond(t)
68
        ]
69
        index = something(findlast(!iszero, values), 1)
×
70

71
        print(io, Time, "(")
×
72
        for i in 1:index
×
73
            show(io, values[i])
×
74
            i != index && print(io, ", ")
×
75
        end
×
76
        print(io, ")")
×
77
    end
78
end
79

80
@inline function format(io, d::AbstractDateToken, dt, locale)
81
    format(io, d, dt)
147✔
82
end
83

84
# Information for parsing and formatting date time values.
85
struct DateFormat{S, T<:Tuple}
86
    tokens::T
87
    locale::DateLocale
88
end
89

90
### Token types ###
91

92
struct DatePart{letter} <: AbstractDateToken
93
    width::Int
94
    fixed::Bool
95
end
96

97
@inline min_width(d::DatePart) = d.fixed ? d.width : 1
×
98
@inline max_width(d::DatePart) = d.fixed ? d.width : 0
×
99

100
function _show_content(io::IO, d::DatePart{c}) where c
×
101
    for i = 1:d.width
×
102
        print(io, c)
×
103
    end
×
104
end
105

106
function Base.show(io::IO, d::DatePart{c}) where c
×
107
    print(io, "DatePart(")
×
108
    _show_content(io, d)
×
109
    print(io, ")")
×
110
end
111

112
### Parse tokens
113

114
for c in "yY"
115
    @eval begin
116
        @inline function tryparsenext(d::DatePart{$c}, str, i, len)
×
117
            val = tryparsenext_sign(str, i, len)
×
118
            if val !== nothing
×
119
                coefficient, i = val
×
120
            else
121
                coefficient = 1
×
122
            end
123
            # The sign character does not affect fixed length `DatePart`s
124
            val = tryparsenext_base10(str, i, len, min_width(d), max_width(d))
×
125
            val === nothing && return nothing
×
126
            y, ii = val
×
127
            return y * coefficient, ii
×
128
        end
129
    end
130
end
131

132
for c in "mdHIMS"
133
    @eval begin
134
        @inline function tryparsenext(d::DatePart{$c}, str, i, len)
×
135
            return tryparsenext_base10(str, i, len, min_width(d), max_width(d))
×
136
        end
137
    end
138
end
139

140
function tryparsenext(d::DatePart{'p'}, str, i, len)
×
141
    i+1 > len && return nothing
×
142
    c, ii = iterate(str, i)::Tuple{Char, Int}
×
143
    ap = lowercase(c)
×
144
    (ap == 'a' || ap == 'p') || return nothing
×
145
    c, ii = iterate(str, ii)::Tuple{Char, Int}
×
146
    lowercase(c) == 'm' || return nothing
×
147
    return ap == 'a' ? AM : PM, ii
×
148
end
149

150
for (tok, fn) in zip("uUeE", Any[monthabbr_to_value, monthname_to_value, dayabbr_to_value, dayname_to_value])
151
    @eval @inline function tryparsenext(d::DatePart{$tok}, str, i, len, locale)
×
152
        next = tryparsenext_word(str, i, len, locale, max_width(d))
×
153
        next === nothing && return nothing
×
154
        word, i = next
×
155
        val = $fn(word, locale)
×
156
        val == 0 && return nothing
×
157
        return val, i
×
158
    end
159
end
160

161
# 3-digit (base 10) number following a decimal point. For InexactError below.
162
struct Decimal3 end
163

164
@inline function tryparsenext(d::DatePart{'s'}, str, i, len)
×
165
    val = tryparsenext_base10(str, i, len, min_width(d), max_width(d))
×
166
    val === nothing && return nothing
×
167
    ms0, ii = val
×
168
    len = ii - i
×
169
    if len > 3
×
170
        ms, r = divrem(ms0, Int64(10) ^ (len - 3))
×
171
        r == 0 || return nothing
×
172
    else
173
        ms = ms0 * Int64(10) ^ (3 - len)
×
174
    end
175
    return ms, ii
×
176
end
177

178
### Format tokens
179

180
hour12(dt) = let h = hour(dt); h > 12 ? h - 12 : h == 0 ? 12 : h; end
×
181

182
for (c, fn) in zip("YmdHIMS", Any[year, month, day, hour, hour12, minute, second])
183
    @eval function format(io, d::DatePart{$c}, dt)
184
        print(io, string($fn(dt), base = 10, pad = d.width))
126✔
185
    end
186
end
187

188
for (tok, fn) in zip("uU", Any[monthabbr, monthname])
189
    @eval function format(io, d::DatePart{$tok}, dt, locale)
×
190
        print(io, $fn(month(dt), locale))
×
191
    end
192
end
193

194
function format(io, d::DatePart{'p'}, dt, locale)
×
195
    ampm = hour(dt) < 12 ? "AM" : "PM" # fixme: locale-specific?
×
196
    print(io, ampm)
×
197
end
198

199
for (tok, fn) in zip("eE", Any[dayabbr, dayname])
200
    @eval function format(io, ::DatePart{$tok}, dt, locale)
×
201
        print(io, $fn(dayofweek(dt), locale))
×
202
    end
203
end
204

205
@inline function format(io, d::DatePart{'y'}, dt)
×
206
    y = year(dt)
×
207
    n = d.width
×
208

209
    # the last n digits of y
210
    # will be 0 padded if y has less than n digits
211
    str = string(y, base = 10, pad = n)
×
212
    l = lastindex(str)
×
213
    if l == n
×
214
        # fast path
215
        print(io, str)
×
216
    else
217
        print(io, SubString(str, l - (n - 1), l))
×
218
    end
219
end
220

221
function format(io, d::DatePart{'s'}, dt)
21✔
222
    ms = millisecond(dt)
21✔
223
    if ms % 100 == 0
21✔
224
        str = string(div(ms, 100))
×
225
    elseif ms % 10 == 0
21✔
226
        str = string(div(ms, 10), pad = 2)
×
227
    else
228
        str = string(ms, pad = 3)
21✔
229
    end
230

231
    print(io, rpad(str, d.width, '0'))
21✔
232
end
233

234
### Delimiters
235

236
struct Delim{T, length} <: AbstractDateToken
237
    d::T
238
end
239

240
Delim(d::T) where {T<:AbstractChar} = Delim{T, 1}(d)
×
241
Delim(d::String) = Delim{String, length(d)}(d)
×
242

243
@inline function tryparsenext(d::Delim{<:AbstractChar, N}, str, i::Int, len) where N
×
244
    for j = 1:N
×
245
        i > len && return nothing
×
246
        next = iterate(str, i)
×
247
        @assert next !== nothing
×
248
        c, i = next
×
249
        c != d.d && return nothing
×
250
    end
×
251
    return true, i
×
252
end
253

254
@inline function tryparsenext(d::Delim{String, N}, str, i::Int, len) where N
×
255
    i1 = i
×
256
    i2 = firstindex(d.d)
×
257
    for j = 1:N
×
258
        if i1 > len
×
259
            return nothing
×
260
        end
261
        next1 = iterate(str, i1)
×
262
        @assert next1 !== nothing
×
263
        c1, i1 = next1
×
264
        next2 = iterate(d.d, i2)
×
265
        @assert next2 !== nothing
×
266
        c2, i2 = next2
×
267
        if c1 != c2
×
268
            return nothing
×
269
        end
270
    end
×
271
    return true, i1
×
272
end
273

274
@inline function format(io, d::Delim, dt, locale)
275
    print(io, d.d)
145✔
276
end
277

278
function _show_content(io::IO, d::Delim{<:AbstractChar, N}) where N
×
279
    if d.d in keys(CONVERSION_SPECIFIERS)
×
280
        for i = 1:N
×
281
            print(io, '\\', d.d)
×
282
        end
×
283
    else
284
        for i = 1:N
×
285
            print(io, d.d)
×
286
        end
×
287
    end
288
end
289

290
function _show_content(io::IO, d::Delim)
×
291
    for c in d.d
×
292
        if c in keys(CONVERSION_SPECIFIERS)
×
293
            print(io, '\\')
×
294
        end
295
        print(io, c)
×
296
    end
×
297
end
298

299
function Base.show(io::IO, d::Delim)
×
300
    print(io, "Delim(")
×
301
    _show_content(io, d)
×
302
    print(io, ")")
×
303
end
304

305
### DateFormat construction
306

307
abstract type DayOfWeekToken end # special addition to Period types
308

309
# Map conversion specifiers or character codes to tokens.
310
# Note: Allow addition of new character codes added by packages
311
const CONVERSION_SPECIFIERS = Dict{Char, Type}(
312
    'y' => Year,
313
    'Y' => Year,
314
    'm' => Month,
315
    'u' => Month,
316
    'U' => Month,
317
    'e' => DayOfWeekToken,
318
    'E' => DayOfWeekToken,
319
    'd' => Day,
320
    'H' => Hour,
321
    'I' => Hour,
322
    'M' => Minute,
323
    'S' => Second,
324
    's' => Millisecond,
325
    'p' => AMPM,
326
)
327

328
# Default values are needed when a conversion specifier is used in a DateFormat for parsing
329
# and we have reached the end of the input string.
330
# Note: Allow `Any` value as a default to support extensibility
331
const CONVERSION_DEFAULTS = IdDict{Type, Any}(
332
    Year => Int64(1),
333
    Month => Int64(1),
334
    DayOfWeekToken => Int64(0),
335
    Day => Int64(1),
336
    Hour => Int64(0),
337
    Minute => Int64(0),
338
    Second => Int64(0),
339
    Millisecond => Int64(0),
340
    Microsecond => Int64(0),
341
    Nanosecond => Int64(0),
342
    AMPM => TWENTYFOURHOUR,
343
)
344

345
# Specifies the required fields in order to parse a TimeType
346
# Note: Allows for addition of new TimeTypes
347
const CONVERSION_TRANSLATIONS = IdDict{Type, Any}(
348
    Date => (Year, Month, Day),
349
    DateTime => (Year, Month, Day, Hour, Minute, Second, Millisecond, AMPM),
350
    Time => (Hour, Minute, Second, Millisecond, Microsecond, Nanosecond, AMPM),
351
)
352

353
# The `DateFormat(format, locale)` method just below consumes the following Regex.
354
# Constructing this Regex is fairly expensive; doing so in the method itself can
355
# consume half or better of `DateFormat(format, locale)`'s runtime. So instead we
356
# construct and cache it outside the method body. Note, however, that when
357
# `keys(CONVERSION_SPECIFIERS)` changes, the cached Regex must be updated accordingly;
358
# hence the mutability (Ref-ness) of the cache, the helper method with which to populate
359
# the cache, the cache of the hash of `keys(CONVERSION_SPECIFIERS)` (to facilitate checking
360
# for changes), and the lock (to maintain consistency of these objects across threads when
361
# threads simultaneously modify `CONVERSION_SPECIFIERS` and construct `DateFormat`s).
362
function compute_dateformat_regex(conversion_specifiers)
×
363
    letters = String(collect(keys(conversion_specifiers)))
×
364
    return Regex("(?<!\\\\)([\\Q$letters\\E])\\1*")
×
365
end
366
const DATEFORMAT_REGEX_LOCK = ReentrantLock()
367
const DATEFORMAT_REGEX_HASH = Ref(hash(keys(CONVERSION_SPECIFIERS)))
368
const DATEFORMAT_REGEX_CACHE = Ref(compute_dateformat_regex(CONVERSION_SPECIFIERS))
369

370
"""
371
    DateFormat(format::AbstractString, locale="english")
372

373
Construct a date formatting object that can be used for parsing date strings or
374
formatting a date object as a string. The following character codes can be used to construct the `format`
375
string:
376

377
| Code       | Matches   | Comment                                                       |
378
|:-----------|:----------|:--------------------------------------------------------------|
379
| `Y`        | 1996, 96  | Returns year of 1996, 0096                                    |
380
| `y`        | 1996, 96  | Same as `Y` on `parse` but discards excess digits on `format` |
381
| `m`        | 1, 01     | Matches 1 or 2-digit months                                   |
382
| `u`        | Jan       | Matches abbreviated months according to the `locale` keyword  |
383
| `U`        | January   | Matches full month names according to the `locale` keyword    |
384
| `d`        | 1, 01     | Matches 1 or 2-digit days                                     |
385
| `H`        | 00        | Matches hours (24-hour clock)                                 |
386
| `I`        | 00        | For outputting hours with 12-hour clock                       |
387
| `M`        | 00        | Matches minutes                                               |
388
| `S`        | 00        | Matches seconds                                               |
389
| `s`        | .500      | Matches milliseconds                                          |
390
| `e`        | Mon, Tues | Matches abbreviated days of the week                          |
391
| `E`        | Monday    | Matches full name days of the week                            |
392
| `p`        | AM        | Matches AM/PM (case-insensitive)                              |
393
| `yyyymmdd` | 19960101  | Matches fixed-width year, month, and day                      |
394

395
Characters not listed above are normally treated as delimiters between date and time slots.
396
For example a `dt` string of "1996-01-15T00:00:00.0" would have a `format` string like
397
"y-m-dTH:M:S.s". If you need to use a code character as a delimiter you can escape it using
398
backslash. The date "1995y01m" would have the format "y\\ym\\m".
399

400
Note that 12:00AM corresponds 00:00 (midnight), and 12:00PM corresponds to 12:00 (noon).
401
When parsing a time with a `p` specifier, any hour (either `H` or `I`) is interpreted as
402
as a 12-hour clock, so the `I` code is mainly useful for output.
403

404
Creating a DateFormat object is expensive. Whenever possible, create it once and use it many times
405
or try the [`dateformat""`](@ref @dateformat_str) string macro. Using this macro creates the DateFormat
406
object once at macro expansion time and reuses it later. There are also several [pre-defined formatters](@ref
407
Common-Date-Formatters), listed later.
408

409
See [`DateTime`](@ref) and [`format`](@ref) for how to use a DateFormat object to parse and write Date strings
410
respectively.
411
"""
412
function DateFormat(f::AbstractString, locale::DateLocale=ENGLISH)
×
413
    tokens = AbstractDateToken[]
×
414
    prev = ()
×
415
    prev_offset = 1
×
416

417
    # To understand this block, please see the comments attached to the definitions of
418
    # DATEFORMAT_REGEX_LOCK, DATEFORMAT_REGEX_HASH, and DATEFORMAT_REGEX_CACHE.
419
    lock(DATEFORMAT_REGEX_LOCK)
×
420
    try
×
421
        dateformat_regex_hash = hash(keys(CONVERSION_SPECIFIERS))
×
422
        if dateformat_regex_hash != DATEFORMAT_REGEX_HASH[]
×
423
            DATEFORMAT_REGEX_HASH[] = dateformat_regex_hash
×
424
            DATEFORMAT_REGEX_CACHE[] = compute_dateformat_regex(CONVERSION_SPECIFIERS)
×
425
        end
426
    finally
427
        unlock(DATEFORMAT_REGEX_LOCK)
×
428
    end
429

430
    for m in eachmatch(DATEFORMAT_REGEX_CACHE[], f)
×
431
        tran = replace(f[prev_offset:prevind(f, m.offset)], r"\\(.)" => s"\1")
×
432

433
        if !isempty(prev)
×
434
            letter, width = prev
×
435
            push!(tokens, DatePart{letter}(width, isempty(tran)))
×
436
        end
437

438
        if !isempty(tran)
×
439
            push!(tokens, Delim(length(tran) == 1 ? first(tran) : tran))
×
440
        end
441

442
        letter = f[m.offset]
×
443
        width = length(m.match)
×
444

445
        prev = (letter, width)
×
446
        prev_offset = m.offset + width
×
447
    end
×
448

449
    tran = replace(f[prev_offset:lastindex(f)], r"\\(.)" => s"\1")
×
450

451
    if !isempty(prev)
×
452
        letter, width = prev
×
453
        push!(tokens, DatePart{letter}(width, false))
×
454
    end
455

456
    if !isempty(tran)
×
457
        push!(tokens, Delim(length(tran) == 1 ? first(tran) : tran))
×
458
    end
459

460
    tokens_tuple = (tokens...,)
×
461
    return DateFormat{Symbol(f),typeof(tokens_tuple)}(tokens_tuple, locale)
×
462
end
463

464
function DateFormat(f::AbstractString, locale::AbstractString)
×
465
    DateFormat(f, LOCALES[locale])
×
466
end
467

468
function Base.show(io::IO, df::DateFormat{S,T}) where {S,T}
×
469
    print(io, "dateformat\"", S, '"')
×
470
end
471
Base.Broadcast.broadcastable(x::DateFormat) = Ref(x)
×
472

473
"""
474
    dateformat"Y-m-d H:M:S"
475

476
Create a [`DateFormat`](@ref) object. Similar to `DateFormat("Y-m-d H:M:S")`
477
but creates the DateFormat object once during macro expansion.
478

479
See [`DateFormat`](@ref) for details about format specifiers.
480
"""
481
macro dateformat_str(str::String)
482
    DateFormat(str)
483
end
484

485
# Standard formats
486

487
"""
488
    Dates.ISODateTimeFormat
489

490
Describes the ISO8601 formatting for a date and time. This is the default value for `Dates.format`
491
of a `DateTime`.
492

493
# Examples
494
```jldoctest
495
julia> Dates.format(DateTime(2018, 8, 8, 12, 0, 43, 1), ISODateTimeFormat)
496
"2018-08-08T12:00:43.001"
497
```
498
"""
499
const ISODateTimeFormat = DateFormat("yyyy-mm-dd\\THH:MM:SS.s")
500
default_format(::Type{DateTime}) = ISODateTimeFormat
×
501

502
"""
503
    Dates.ISODateFormat
504

505
Describes the ISO8601 formatting for a date. This is the default value for `Dates.format` of a `Date`.
506

507
# Examples
508
```jldoctest
509
julia> Dates.format(Date(2018, 8, 8), ISODateFormat)
510
"2018-08-08"
511
```
512
"""
513
const ISODateFormat = DateFormat("yyyy-mm-dd")
514
default_format(::Type{Date}) = ISODateFormat
×
515

516
"""
517
    Dates.ISOTimeFormat
518

519
Describes the ISO8601 formatting for a time. This is the default value for `Dates.format` of a `Time`.
520

521
# Examples
522
```jldoctest
523
julia> Dates.format(Time(12, 0, 43, 1), ISOTimeFormat)
524
"12:00:43.001"
525
```
526
"""
527
const ISOTimeFormat = DateFormat("HH:MM:SS.s")
528
default_format(::Type{Time}) = ISOTimeFormat
×
529

530
"""
531
    Dates.RFC1123Format
532

533
Describes the RFC1123 formatting for a date and time.
534

535
# Examples
536
```jldoctest
537
julia> Dates.format(DateTime(2018, 8, 8, 12, 0, 43, 1), RFC1123Format)
538
"Wed, 08 Aug 2018 12:00:43"
539
```
540
"""
541
const RFC1123Format = DateFormat("e, dd u yyyy HH:MM:SS")
542

543

544
### API
545

546
const Locale = Union{DateLocale, String}
547

548
"""
549
    DateTime(dt::AbstractString, format::AbstractString; locale="english")
550

551
Construct a `DateTime` by parsing the `dt` date time string following the
552
pattern given in the `format` string (see [`DateFormat`](@ref)  for syntax).
553

554
!!! note
555
    This method creates a `DateFormat` object each time it is called. It is recommended
556
    that you create a [`DateFormat`](@ref) object instead and use that as the second
557
    argument to avoid performance loss when using the same format repeatedly.
558

559
# Examples
560
```jldoctest
561
julia> DateTime("2020-01-01", "yyyy-mm-dd")
562
2020-01-01T00:00:00
563

564
julia> a = ("2020-01-01", "2020-01-02");
565

566
julia> [DateTime(d, dateformat"yyyy-mm-dd") for d ∈ a] # preferred
567
2-element Vector{DateTime}:
568
 2020-01-01T00:00:00
569
 2020-01-02T00:00:00
570
```
571
"""
572
function DateTime(dt::AbstractString, format::AbstractString; locale::Locale=ENGLISH)
×
573
    return parse(DateTime, dt, DateFormat(format, locale))
×
574
end
575

576
"""
577
    DateTime(dt::AbstractString, df::DateFormat=ISODateTimeFormat)
578

579
Construct a `DateTime` by parsing the `dt` date time string following the
580
pattern given in the [`DateFormat`](@ref) object, or $ISODateTimeFormat if omitted.
581

582
Similar to `DateTime(::AbstractString, ::AbstractString)` but more efficient when
583
repeatedly parsing similarly formatted date time strings with a pre-created
584
`DateFormat` object.
585
"""
586
DateTime(dt::AbstractString, df::DateFormat=ISODateTimeFormat) = parse(DateTime, dt, df)
×
587

588
"""
589
    Date(d::AbstractString, format::AbstractString; locale="english")
590

591
Construct a `Date` by parsing the `d` date string following the pattern given
592
in the `format` string (see [`DateFormat`](@ref) for syntax).
593

594
!!! note
595
    This method creates a `DateFormat` object each time it is called. It is recommended
596
    that you create a [`DateFormat`](@ref) object instead and use that as the second
597
    argument to avoid performance loss when using the same format repeatedly.
598

599
# Examples
600
```jldoctest
601
julia> Date("2020-01-01", "yyyy-mm-dd")
602
2020-01-01
603

604
julia> a = ("2020-01-01", "2020-01-02");
605

606
julia> [Date(d, dateformat"yyyy-mm-dd") for d ∈ a] # preferred
607
2-element Vector{Date}:
608
 2020-01-01
609
 2020-01-02
610
```
611
"""
612
function Date(d::AbstractString, format::AbstractString; locale::Locale=ENGLISH)
×
613
    parse(Date, d, DateFormat(format, locale))
×
614
end
615

616
"""
617
    Date(d::AbstractString, df::DateFormat=ISODateFormat)
618

619
Construct a `Date` by parsing the `d` date string following the
620
pattern given in the [`DateFormat`](@ref) object, or $ISODateFormat if omitted.
621

622
Similar to `Date(::AbstractString, ::AbstractString)` but more efficient when
623
repeatedly parsing similarly formatted date strings with a pre-created
624
`DateFormat` object.
625
"""
626
Date(d::AbstractString, df::DateFormat=ISODateFormat) = parse(Date, d, df)
×
627

628
"""
629
    Time(t::AbstractString, format::AbstractString; locale="english")
630

631
Construct a `Time` by parsing the `t` time string following the pattern given
632
in the `format` string (see [`DateFormat`](@ref) for syntax).
633

634
!!! note
635
    This method creates a `DateFormat` object each time it is called. It is recommended
636
    that you create a [`DateFormat`](@ref) object instead and use that as the second
637
    argument to avoid performance loss when using the same format repeatedly.
638

639
# Examples
640
```jldoctest
641
julia> Time("12:34pm", "HH:MMp")
642
12:34:00
643

644
julia> a = ("12:34pm", "2:34am");
645

646
julia> [Time(d, dateformat"HH:MMp") for d ∈ a] # preferred
647
2-element Vector{Time}:
648
 12:34:00
649
 02:34:00
650
```
651
"""
652
function Time(t::AbstractString, format::AbstractString; locale::Locale=ENGLISH)
×
653
    parse(Time, t, DateFormat(format, locale))
×
654
end
655

656
"""
657
    Time(t::AbstractString, df::DateFormat=ISOTimeFormat)
658

659
Construct a `Time` by parsing the `t` date time string following the
660
pattern given in the [`DateFormat`](@ref) object, or $ISOTimeFormat if omitted.
661

662
Similar to `Time(::AbstractString, ::AbstractString)` but more efficient when
663
repeatedly parsing similarly formatted time strings with a pre-created
664
`DateFormat` object.
665
"""
666
Time(t::AbstractString, df::DateFormat=ISOTimeFormat) = parse(Time, t, df)
×
667

668
@generated function format(io::IO, dt::TimeType, fmt::DateFormat{<:Any,T}) where T
21✔
669
    N = fieldcount(T)
6✔
670
    quote
6✔
671
        ts = fmt.tokens
21✔
672
        loc = fmt.locale
2✔
673
        Base.@nexprs $N i -> format(io, ts[i], dt, loc)
21✔
674
    end
675
end
676

677
function format(dt::TimeType, fmt::DateFormat, bufsize=12)
21✔
678
    # preallocate to reduce resizing
679
    io = IOBuffer(Vector{UInt8}(undef, bufsize), read=true, write=true)
40✔
680
    format(io, dt, fmt)
21✔
681
    String(io.data[1:io.ptr - 1])
21✔
682
end
683

684

685
"""
686
    format(dt::TimeType, format::AbstractString; locale="english")::AbstractString
687

688
Construct a string by using a `TimeType` object and applying the provided `format`. The
689
following character codes can be used to construct the `format` string:
690

691
| Code       | Examples  | Comment                                                      |
692
|:-----------|:----------|:-------------------------------------------------------------|
693
| `y`        | 6         | Numeric year with a fixed width                              |
694
| `Y`        | 1996      | Numeric year with a minimum width                            |
695
| `m`        | 1, 12     | Numeric month with a minimum width                           |
696
| `u`        | Jan       | Month name shortened to 3-chars according to the `locale`    |
697
| `U`        | January   | Full month name according to the `locale` keyword            |
698
| `d`        | 1, 31     | Day of the month with a minimum width                        |
699
| `H`        | 0, 23     | Hour (24-hour clock) with a minimum width                    |
700
| `M`        | 0, 59     | Minute with a minimum width                                  |
701
| `S`        | 0, 59     | Second with a minimum width                                  |
702
| `s`        | 000, 500  | Millisecond with a minimum width of 3                        |
703
| `e`        | Mon, Tue  | Abbreviated days of the week                                 |
704
| `E`        | Monday    | Full day of week name                                        |
705

706
The number of sequential code characters indicate the width of the code. A format of
707
`yyyy-mm` specifies that the code `y` should have a width of four while `m` a width of two.
708
Codes that yield numeric digits have an associated mode: fixed-width or minimum-width.
709
The fixed-width mode left-pads the value with zeros when it is shorter than the specified
710
width and truncates the value when longer. Minimum-width mode works the same as fixed-width
711
except that it does not truncate values longer than the width.
712

713
When creating a `format` you can use any non-code characters as a separator. For example to
714
generate the string "1996-01-15T00:00:00" you could use `format`: "yyyy-mm-ddTHH:MM:SS".
715
Note that if you need to use a code character as a literal you can use the escape character
716
backslash. The string "1996y01m" can be produced with the format raw"yyyy\\ymm\\m".
717
"""
718
function format(dt::TimeType, f::AbstractString; locale::Locale=ENGLISH)
×
719
    format(dt, DateFormat(f, locale))
×
720
end
721

722
# show
723
function Base.print(io::IO, dt::DateTime)
×
724
    str = if millisecond(dt) == 0
2✔
725
        format(dt, dateformat"YYYY-mm-dd\THH:MM:SS", 19)
×
726
    else
727
        format(dt, dateformat"YYYY-mm-dd\THH:MM:SS.sss", 23)
4✔
728
    end
729
    print(io, str)
2✔
730
end
731

732
function Base.print(io::IO, dt::Date)
×
733
    # don't use format - bypassing IOBuffer creation
734
    # saves a bit of time here.
735
    y,m,d = yearmonthday(value(dt))
×
736
    yy = y < 0 ? @sprintf("%05i", y) : lpad(y, 4, "0")
×
737
    mm = lpad(m, 2, "0")
×
738
    dd = lpad(d, 2, "0")
×
739
    print(io, "$yy-$mm-$dd")
×
740
end
741

742
for date_type in (:Date, :DateTime)
743
    # Human readable output (i.e. "2012-01-01")
744
    @eval Base.show(io::IO, ::MIME"text/plain", dt::$date_type) = print(io, dt)
×
745
    # Parsable output (i.e. Date("2012-01-01"))
746
    @eval Base.show(io::IO, dt::$date_type) = print(io, typeof(dt), "(\"", dt, "\")")
×
747
    # Parsable output will have type info displayed, thus it is implied
748
    @eval Base.typeinfo_implicit(::Type{$date_type}) = true
×
749
end
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