From 978037bf606ea7942eb98243b330a29cd35691d5 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 12 Jun 2019 13:22:46 -0400 Subject: [PATCH 01/11] support AM/PM in date parsing/printing --- stdlib/Dates/src/io.jl | 36 +++++++++++++++++++++++++++++++----- stdlib/Dates/src/types.jl | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 6d89438970403..7a61df8d461f2 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -111,7 +111,7 @@ end ### Parse tokens -for c in "yYmdHMS" +for c in "yYmdHIMS" @eval begin @inline function tryparsenext(d::DatePart{$c}, str, i, len) return tryparsenext_base10(str, i, len, min_width(d), max_width(d)) @@ -119,6 +119,16 @@ for c in "yYmdHMS" end end +function tryparsenext(d::DatePart{'p'}, str, i, len) + i+1 > len && return nothing + c, ii = iterate(str, i)::Tuple{Char, Int} + ap = lowercase(c) + (ap == 'a' || ap == 'p') || return nothing + c, ii = iterate(str, ii)::Tuple{Char, Int} + lowercase(c) == 'm' || return nothing + return ap == 'a' ? AM : PM, ii +end + for (tok, fn) in zip("uUeE", [monthabbr_to_value, monthname_to_value, dayabbr_to_value, dayname_to_value]) @eval @inline function tryparsenext(d::DatePart{$tok}, str, i, len, locale) next = tryparsenext_word(str, i, len, locale, max_width(d)) @@ -149,7 +159,9 @@ end ### Format tokens -for (c, fn) in zip("YmdHMS", [year, month, day, hour, minute, second]) +hour12(dt) = let h = hour(dt); h > 12 ? h - 12 : h == 0 ? 12 : h; end + +for (c, fn) in zip("YmdHIMS", [year, month, day, hour, hour12, minute, second]) @eval function format(io, d::DatePart{$c}, dt) print(io, string($fn(dt), base = 10, pad = d.width)) end @@ -161,6 +173,11 @@ for (tok, fn) in zip("uU", [monthabbr, monthname]) end end +function format(io, d::DatePart{'p'}, dt, locale) + ampm = hour(dt) < 12 ? "AM" : "PM" # fixme: locale-specific? + print(io, ampm) +end + for (tok, fn) in zip("eE", [dayabbr, dayname]) @eval function format(io, ::DatePart{$tok}, dt, locale) print(io, $fn(dayofweek(dt), locale)) @@ -283,9 +300,11 @@ const CONVERSION_SPECIFIERS = Dict{Char, Type}( 'E' => DayOfWeekToken, 'd' => Day, 'H' => Hour, + 'I' => Hour, 'M' => Minute, 'S' => Second, 's' => Millisecond, + 'p' => AMPM, ) # Default values are needed when a conversion specifier is used in a DateFormat for parsing @@ -302,14 +321,15 @@ const CONVERSION_DEFAULTS = IdDict{Type, Any}( Millisecond => Int64(0), Microsecond => Int64(0), Nanosecond => Int64(0), + AMPM => TWENTYFOUR, ) # Specifies the required fields in order to parse a TimeType # Note: Allows for addition of new TimeTypes const CONVERSION_TRANSLATIONS = IdDict{Type, Any}( Date => (Year, Month, Day), - DateTime => (Year, Month, Day, Hour, Minute, Second, Millisecond), - Time => (Hour, Minute, Second, Millisecond, Microsecond, Nanosecond), + DateTime => (Year, Month, Day, Hour, Minute, Second, Millisecond, AMPM), + Time => (Hour, Minute, Second, Millisecond, Microsecond, Nanosecond, AMPM), ) """ @@ -327,12 +347,14 @@ string: | `u` | Jan | Matches abbreviated months according to the `locale` keyword | | `U` | January | Matches full month names according to the `locale` keyword | | `d` | 1, 01 | Matches 1 or 2-digit days | -| `H` | 00 | Matches hours | +| `H` | 00 | Matches hours (24-hour clock) | +| `I` | 00 | For outputting hours with 12-hour clock | | `M` | 00 | Matches minutes | | `S` | 00 | Matches seconds | | `s` | .500 | Matches milliseconds | | `e` | Mon, Tues | Matches abbreviated days of the week | | `E` | Monday | Matches full name days of the week | +| `p` | AM | Matches AM/PM according to the `locale` keyword | | `yyyymmdd` | 19960101 | Matches fixed-width year, month, and day | Characters not listed above are normally treated as delimiters between date and time slots. @@ -340,6 +362,10 @@ For example a `dt` string of "1996-01-15T00:00:00.0" would have a `format` strin "y-m-dTH:M:S.s". If you need to use a code character as a delimiter you can escape it using backslash. The date "1995y01m" would have the format "y\\ym\\m". +Note that 12:00AM corresponds 00:00 (midnight), and 12:00PM corresponds to 12:00 (noon). +When parsing a time with a `p` specifier, any hour (either `H` or `I`) is interpreted as +as a 12-hour clock, so the `I` code is mainly useful for output. + Creating a DateFormat object is expensive. Whenever possible, create it once and use it many times or try the `dateformat""` string macro. Using this macro creates the DateFormat object once at macro expansion time and reuses it later. see [`@dateformat_str`](@ref). diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index ee3255a116313..75aedab2bbff2 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -170,6 +170,24 @@ or [`nothing`](@ref) if no message is provided. For use by `validargs`. argerror(msg::String) = ArgumentError(msg) argerror() = nothing +# Julia uses 24-hour clocks internally, but user input can be AM/PM with 12pm == noon and 12am == midnight. +@enum AMPM AM PM TWENTYFOURHOUR +function validhour(h::Int64, mi::Int64, s::Int64, ms::Int64, ampm::AMPM) + if ampm == TWENTYFOURHOUR # 24-hour clock + -1 < h < 24 || (h == 24 && mi==s==ms==0) || + return argerror("Hour: $h out of range (0:23)") + else + 0 < h < 13 || return argerror("Hour: $h out of range (1:12)") + end + return argerror() +end +function adjusthour(h::Int64, ampm::AMPM) + ampm == TWENTYFOURHOUR && return h + ampm == PM && h < 12 && return h + 12 + ampm == AM && h == 12 && return Int64(0) + return h +end + ### CONSTRUCTORS ### # Core constructors """ @@ -178,23 +196,22 @@ argerror() = nothing Construct a `DateTime` type by parts. Arguments must be convertible to [`Int64`](@ref). """ function DateTime(y::Int64, m::Int64=1, d::Int64=1, - h::Int64=0, mi::Int64=0, s::Int64=0, ms::Int64=0) - err = validargs(DateTime, y, m, d, h, mi, s, ms) + h::Int64=0, mi::Int64=0, s::Int64=0, ms::Int64=0, ampm::AMPM=TWENTYFOURHOUR) + err = validargs(DateTime, y, m, d, h, mi, s, ms, ampm) err === nothing || throw(err) + h = adjusthour(h, ampm) rata = ms + 1000 * (s + 60mi + 3600h + 86400 * totaldays(y, m, d)) return DateTime(UTM(rata)) end function validargs(::Type{DateTime}, y::Int64, m::Int64, d::Int64, - h::Int64, mi::Int64, s::Int64, ms::Int64) + h::Int64, mi::Int64, s::Int64, ms::Int64, ampm::AMPM=TWENTYFOURHOUR) 0 < m < 13 || return argerror("Month: $m out of range (1:12)") 0 < d < daysinmonth(y, m) + 1 || return argerror("Day: $d out of range (1:$(daysinmonth(y, m)))") - -1 < h < 24 || (h == 24 && mi==s==ms==0) || - return argerror("Hour: $h out of range (0:23)") -1 < mi < 60 || return argerror("Minute: $mi out of range (0:59)") -1 < s < 60 || return argerror("Second: $s out of range (0:59)") -1 < ms < 1000 || return argerror("Millisecond: $ms out of range (0:999)") - return argerror() + return validhour(h, mi, s, ms, ampm) end DateTime(dt::Base.Libc.TmStruct) = DateTime(1900 + dt.year, 1 + dt.month, dt.mday, dt.hour, dt.min, dt.sec) @@ -223,20 +240,20 @@ Date(dt::Base.Libc.TmStruct) = Date(1900 + dt.year, 1 + dt.month, dt.mday) Construct a `Time` type by parts. Arguments must be convertible to [`Int64`](@ref). """ -function Time(h::Int64, mi::Int64=0, s::Int64=0, ms::Int64=0, us::Int64=0, ns::Int64=0) - err = validargs(Time, h, mi, s, ms, us, ns) +function Time(h::Int64, mi::Int64=0, s::Int64=0, ms::Int64=0, us::Int64=0, ns::Int64=0, ampm::AMPM=TWENTYFOURHOUR) + err = validargs(Time, h, mi, s, ms, us, ns, ampm) err === nothing || throw(err) + h = adjusthour(h, ampm) return Time(Nanosecond(ns + 1000us + 1000000ms + 1000000000s + 60000000000mi + 3600000000000h)) end -function validargs(::Type{Time}, h::Int64, mi::Int64, s::Int64, ms::Int64, us::Int64, ns::Int64) - -1 < h < 24 || return argerror("Hour: $h out of range (0:23)") +function validargs(::Type{Time}, h::Int64, mi::Int64, s::Int64, ms::Int64, us::Int64, ns::Int64, ampm::AMPM=TWENTYFOURHOUR) -1 < mi < 60 || return argerror("Minute: $mi out of range (0:59)") -1 < s < 60 || return argerror("Second: $s out of range (0:59)") -1 < ms < 1000 || return argerror("Millisecond: $ms out of range (0:999)") -1 < us < 1000 || return argerror("Microsecond: $us out of range (0:999)") -1 < ns < 1000 || return argerror("Nanosecond: $ns out of range (0:999)") - return argerror() + return validhour(h, mi, s, ms, ampm) end Time(dt::Base.Libc.TmStruct) = Time(dt.hour, dt.min, dt.sec) From ea8fcc2040aac19a90572384e1861962968b8e91 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 14:13:59 -0400 Subject: [PATCH 02/11] whoops --- stdlib/Dates/src/io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 7a61df8d461f2..7a978a2699580 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -321,7 +321,7 @@ const CONVERSION_DEFAULTS = IdDict{Type, Any}( Millisecond => Int64(0), Microsecond => Int64(0), Nanosecond => Int64(0), - AMPM => TWENTYFOUR, + AMPM => TWENTYFOURHOUR, ) # Specifies the required fields in order to parse a TimeType From 97d89cf09610190f091778c77cd25eef9766149b Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 15:00:32 -0400 Subject: [PATCH 03/11] fix validation --- stdlib/Dates/src/types.jl | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 75aedab2bbff2..791e9b32e66c6 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -172,15 +172,6 @@ argerror() = nothing # Julia uses 24-hour clocks internally, but user input can be AM/PM with 12pm == noon and 12am == midnight. @enum AMPM AM PM TWENTYFOURHOUR -function validhour(h::Int64, mi::Int64, s::Int64, ms::Int64, ampm::AMPM) - if ampm == TWENTYFOURHOUR # 24-hour clock - -1 < h < 24 || (h == 24 && mi==s==ms==0) || - return argerror("Hour: $h out of range (0:23)") - else - 0 < h < 13 || return argerror("Hour: $h out of range (1:12)") - end - return argerror() -end function adjusthour(h::Int64, ampm::AMPM) ampm == TWENTYFOURHOUR && return h ampm == PM && h < 12 && return h + 12 @@ -208,10 +199,16 @@ function validargs(::Type{DateTime}, y::Int64, m::Int64, d::Int64, h::Int64, mi::Int64, s::Int64, ms::Int64, ampm::AMPM=TWENTYFOURHOUR) 0 < m < 13 || return argerror("Month: $m out of range (1:12)") 0 < d < daysinmonth(y, m) + 1 || return argerror("Day: $d out of range (1:$(daysinmonth(y, m)))") + if ampm == TWENTYFOURHOUR # 24-hour clock + -1 < h < 24 || (h == 24 && mi==s==ms==0) || + return argerror("Hour: $h out of range (0:23)") + else + 0 < h < 13 || return argerror("Hour: $h out of range (1:12)") + end -1 < mi < 60 || return argerror("Minute: $mi out of range (0:59)") -1 < s < 60 || return argerror("Second: $s out of range (0:59)") -1 < ms < 1000 || return argerror("Millisecond: $ms out of range (0:999)") - return validhour(h, mi, s, ms, ampm) + return argerror() end DateTime(dt::Base.Libc.TmStruct) = DateTime(1900 + dt.year, 1 + dt.month, dt.mday, dt.hour, dt.min, dt.sec) @@ -248,12 +245,17 @@ function Time(h::Int64, mi::Int64=0, s::Int64=0, ms::Int64=0, us::Int64=0, ns::I end function validargs(::Type{Time}, h::Int64, mi::Int64, s::Int64, ms::Int64, us::Int64, ns::Int64, ampm::AMPM=TWENTYFOURHOUR) + if ampm == TWENTYFOURHOUR # 24-hour clock + -1 < h < 24 || return argerror("Hour: $h out of range (0:23)") + else + 0 < h < 13 || return argerror("Hour: $h out of range (1:12)") + end -1 < mi < 60 || return argerror("Minute: $mi out of range (0:59)") -1 < s < 60 || return argerror("Second: $s out of range (0:59)") -1 < ms < 1000 || return argerror("Millisecond: $ms out of range (0:999)") -1 < us < 1000 || return argerror("Microsecond: $us out of range (0:999)") -1 < ns < 1000 || return argerror("Nanosecond: $ns out of range (0:999)") - return validhour(h, mi, s, ms, ampm) + return argerror() end Time(dt::Base.Libc.TmStruct) = Time(dt.hour, dt.min, dt.sec) From 99b564a98c9741b989fc3e2d17af0486dd956de0 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 15:18:14 -0400 Subject: [PATCH 04/11] tests --- stdlib/Dates/test/io.jl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index 9ff2c21c7c5d9..f6514e0c0826a 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -566,4 +566,24 @@ end @test_throws ArgumentError DateTime(2018, 1, 1, 24, 0, 0, 1) end +@testset "AM/PM" begin + for (t12,t24) in (("12:00am","00:00"), ("12:07am","00:07"), ("01:24AM","01:24"), + ("12:00pm","12:00"), ("12:15pm","12:15"), ("11:59PM","23:59")) + d = DateTime("2018-01-01T$t24:00") + t = Time("$t24:00") + for HH in ("HH","II") + @test DateTime("2018-01-01 $t12","yyyy-mm-dd $HH:MMp") == d + @test Time("$t12","$HH:MMp") == t + end + @test uppercase(t12) == Dates.format(t, "II:MMp") == + Dates.format(d, "II:MMp") + end + for bad in ("13:24pm", "2pm", "12:24p.m.", "12:24 pm", "12:24pµ") + @show bad + @eval @test_throws ArgumentError Time($bad, "II:MMp") + end + # if am/pm is missing, defaults to 24-hour clock + @eval Time("13:24", "II:MMp") == Time("13:24", "HH:MM") +end + end From 7990b2c731d2480df62af1b2af6648c4bbc1a9a7 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 15:19:52 -0400 Subject: [PATCH 05/11] news --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 643eb29aa1526..96185d9abbcf5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -50,6 +50,7 @@ Standard library changes #### Dates +* `DateTime` and `Time` formatting/parsing now supports 12-hour clocks with AM/PM via `I` and `p` codes, similar to `strftime` ([#32308]). * Fixed `repr` such that it displays `Time` as it would be entered in Julia ([#32103]). #### Sockets From 1add0a727accb26fe1cc58e476a80b8b4f8e9cbb Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 15:22:50 -0400 Subject: [PATCH 06/11] rm debugging code, add strptime comparison --- stdlib/Dates/test/io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index f6514e0c0826a..f66e8a1296386 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -575,11 +575,11 @@ end @test DateTime("2018-01-01 $t12","yyyy-mm-dd $HH:MMp") == d @test Time("$t12","$HH:MMp") == t end + @test Time(Libc.strptime("%I:%M%p", t12)) == t @test uppercase(t12) == Dates.format(t, "II:MMp") == Dates.format(d, "II:MMp") end for bad in ("13:24pm", "2pm", "12:24p.m.", "12:24 pm", "12:24pµ") - @show bad @eval @test_throws ArgumentError Time($bad, "II:MMp") end # if am/pm is missing, defaults to 24-hour clock From 60f64cf9c594c786674a55e18bd0a330ab0c7cbb Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 15:25:36 -0400 Subject: [PATCH 07/11] another test --- stdlib/Dates/test/io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index f66e8a1296386..ecd8dcfeee906 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -579,7 +579,7 @@ end @test uppercase(t12) == Dates.format(t, "II:MMp") == Dates.format(d, "II:MMp") end - for bad in ("13:24pm", "2pm", "12:24p.m.", "12:24 pm", "12:24pµ") + for bad in ("00:24am", "00:24pm", "13:24pm", "2pm", "12:24p.m.", "12:24 pm", "12:24pµ") @eval @test_throws ArgumentError Time($bad, "II:MMp") end # if am/pm is missing, defaults to 24-hour clock From 277089c2c54357d2589b4e7fbb1772fe3028757c Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 15:31:19 -0400 Subject: [PATCH 08/11] compare to strftime --- stdlib/Dates/test/io.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index ecd8dcfeee906..b0c6ea9b0cbd9 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -575,9 +575,11 @@ end @test DateTime("2018-01-01 $t12","yyyy-mm-dd $HH:MMp") == d @test Time("$t12","$HH:MMp") == t end - @test Time(Libc.strptime("%I:%M%p", t12)) == t + tmstruct = Libc.strptime("%I:%M%p", t12) + @test Time(tmstruct) == t @test uppercase(t12) == Dates.format(t, "II:MMp") == - Dates.format(d, "II:MMp") + Dates.format(d, "II:MMp") == + uppercase(Libc.strftime("%I:%M%p", tmstruct)) end for bad in ("00:24am", "00:24pm", "13:24pm", "2pm", "12:24p.m.", "12:24 pm", "12:24pµ") @eval @test_throws ArgumentError Time($bad, "II:MMp") From fb63dda22638a0d571f317414fd0d86b98ae2782 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 15:32:24 -0400 Subject: [PATCH 09/11] compare to strftime --- stdlib/Dates/test/io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index b0c6ea9b0cbd9..ea6996a5a6d1b 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -579,7 +579,7 @@ end @test Time(tmstruct) == t @test uppercase(t12) == Dates.format(t, "II:MMp") == Dates.format(d, "II:MMp") == - uppercase(Libc.strftime("%I:%M%p", tmstruct)) + Libc.strftime("%I:%M%p", tmstruct) end for bad in ("00:24am", "00:24pm", "13:24pm", "2pm", "12:24p.m.", "12:24 pm", "12:24pµ") @eval @test_throws ArgumentError Time($bad, "II:MMp") From 21ff2c8f5ea48dde5df0b4932b02e658b141f42b Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 15:33:02 -0400 Subject: [PATCH 10/11] note that AM/PM parsing is case-insensitive --- stdlib/Dates/src/io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 7a978a2699580..6b8231f116823 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -354,7 +354,7 @@ string: | `s` | .500 | Matches milliseconds | | `e` | Mon, Tues | Matches abbreviated days of the week | | `E` | Monday | Matches full name days of the week | -| `p` | AM | Matches AM/PM according to the `locale` keyword | +| `p` | AM | Matches AM/PM (case-insensitive) | | `yyyymmdd` | 19960101 | Matches fixed-width year, month, and day | Characters not listed above are normally treated as delimiters between date and time slots. From e192792555c0b4deb7af69bf49ad0f16a23c7872 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 13 Jun 2019 23:41:10 -0400 Subject: [PATCH 11/11] add ampm to generic constructor --- stdlib/Dates/src/types.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 791e9b32e66c6..5a9b6496c4beb 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -364,9 +364,9 @@ function DateTime(dt::Date, t::Time) end # Fallback constructors -DateTime(y, m=1, d=1, h=0, mi=0, s=0, ms=0) = DateTime(Int64(y), Int64(m), Int64(d), Int64(h), Int64(mi), Int64(s), Int64(ms)) +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) Date(y, m=1, d=1) = Date(Int64(y), Int64(m), Int64(d)) -Time(h, mi=0, s=0, ms=0, us=0, ns=0) = Time(Int64(h), Int64(mi), Int64(s), Int64(ms), Int64(us), Int64(ns)) +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) # Traits, Equality Base.isfinite(::Union{Type{T}, T}) where {T<:TimeType} = true