diff --git a/base/dates/Dates.jl b/base/dates/Dates.jl index cf5631a9f7244..b2551e94784d1 100644 --- a/base/dates/Dates.jl +++ b/base/dates/Dates.jl @@ -78,6 +78,6 @@ export Period, DatePeriod, TimePeriod, firstdayofquarter, lastdayofquarter, adjust, tonext, toprev, tofirst, tolast, # io.jl - ISODateTimeFormat, ISODateFormat, DateFormat, RFC1123Format, @dateformat_str + ISODateTimeFormat, ISODateFormat, ISOTimeFormat, DateFormat, RFC1123Format, @dateformat_str end # module diff --git a/base/dates/io.jl b/base/dates/io.jl index b31e28c35fd33..f7ce2480feffc 100644 --- a/base/dates/io.jl +++ b/base/dates/io.jl @@ -273,6 +273,8 @@ const CONVERSION_DEFAULTS = Dict{Type, Any}( Minute => Int64(0), Second => Int64(0), Millisecond => Int64(0), + Microsecond => Int64(0), + Nanosecond => Int64(0), ) # Specifies the required fields in order to parse a TimeType @@ -280,6 +282,7 @@ const CONVERSION_DEFAULTS = Dict{Type, Any}( const CONVERSION_TRANSLATIONS = Dict{Type{<:TimeType}, Tuple}( Date => (Year, Month, Day), DateTime => (Year, Month, Day, Hour, Minute, Second, Millisecond), + Time => (Hour, Minute, Second, Millisecond, Microsecond, Nanosecond), ) """ @@ -388,10 +391,12 @@ end # Standard formats const ISODateTimeFormat = DateFormat("yyyy-mm-dd\\THH:MM:SS.s") const ISODateFormat = DateFormat("yyyy-mm-dd") +const ISOTimeFormat = DateFormat("HH:MM:SS.s") const RFC1123Format = DateFormat("e, dd u yyyy HH:MM:SS") default_format(::Type{DateTime}) = ISODateTimeFormat default_format(::Type{Date}) = ISODateFormat +default_format(::Type{Time}) = ISOTimeFormat ### API @@ -400,12 +405,12 @@ const Locale = Union{DateLocale, String} """ DateTime(dt::AbstractString, format::AbstractString; locale="english") -> DateTime -Construct a `DateTime` by parsing the `dt` date string following the pattern given in -the `format` string. +Construct a `DateTime` by parsing the `dt` date time string following the +pattern given in the `format` string. -This method creates a `DateFormat` object each time it is called. If you are parsing many -date strings of the same format, consider creating a [`DateFormat`](@ref) object once and using -that as the second argument instead. +This method creates a `DateFormat` object each time it is called. If you are +parsing many date time strings of the same format, consider creating a +[`DateFormat`](@ref) object once and using that as the second argument instead. """ function DateTime(dt::AbstractString, format::AbstractString; locale::Locale=ENGLISH) parse(DateTime, dt, DateFormat(format, locale)) @@ -414,30 +419,55 @@ end """ DateTime(dt::AbstractString, df::DateFormat) -> DateTime -Construct a `DateTime` by parsing the `dt` date string following the pattern given in -the [`DateFormat`](@ref) object. Similar to -`DateTime(::AbstractString, ::AbstractString)` but more efficient when repeatedly parsing -similarly formatted date strings with a pre-created `DateFormat` object. +Construct a `DateTime` by parsing the `dt` date time string following the +pattern given in the [`DateFormat`](@ref) object. Similar to +`DateTime(::AbstractString, ::AbstractString)` but more efficient when +repeatedly parsing similarly formatted date time strings with a pre-created +`DateFormat` object. """ DateTime(dt::AbstractString, df::DateFormat=ISODateTimeFormat) = parse(DateTime, dt, df) """ - Date(dt::AbstractString, format::AbstractString; locale="english") -> Date + Date(d::AbstractString, format::AbstractString; locale="english") -> Date -Construct a `Date` object by parsing a `dt` date string following the pattern given in the -`format` string. Follows the same conventions as -`DateTime(::AbstractString, ::AbstractString)`. +Construct a `Date` by parsing the `d` date string following the pattern given +in the `format` string. + +This method creates a `DateFormat` object each time it is called. If you are +parsing many date strings of the same format, consider creating a +[`DateFormat`](@ref) object once and using that as the second argument instead. +""" +function Date(d::AbstractString, format::AbstractString; locale::Locale=ENGLISH) + parse(Date, d, DateFormat(format, locale)) +end + +""" + Date(d::AbstractString, df::DateFormat) -> Date + +Parse a date from a date string `d` using a `DateFormat` object `df`. +""" +Date(d::AbstractString, df::DateFormat=ISODateFormat) = parse(Date, d, df) + +""" + Time(t::AbstractString, format::AbstractString; locale="english") -> Time + +Construct a `Time` by parsing the `t` time string following the pattern given +in the `format` string. + +This method creates a `DateFormat` object each time it is called. If you are +parsing many time strings of the same format, consider creating a +[`DateFormat`](@ref) object once and using that as the second argument instead. """ -function Date(dt::AbstractString, format::AbstractString; locale::Locale=ENGLISH) - parse(Date, dt, DateFormat(format, locale)) +function Time(t::AbstractString, format::AbstractString; locale::Locale=ENGLISH) + parse(Time, t, DateFormat(format, locale)) end """ - Date(dt::AbstractString, df::DateFormat) -> Date + Time(t::AbstractString, df::DateFormat) -> Time -Parse a date from a date string `dt` using a `DateFormat` object `df`. +Parse a time from a time string `t` using a `DateFormat` object `df`. """ -Date(dt::AbstractString,df::DateFormat=ISODateFormat) = parse(Date, dt, df) +Time(t::AbstractString, df::DateFormat=ISOTimeFormat) = parse(Time, t, df) @generated function format(io::IO, dt::TimeType, fmt::DateFormat{<:Any,T}) where T N = nfields(T) diff --git a/test/dates/io.jl b/test/dates/io.jl index b5090eef1086a..4a34d87859637 100644 --- a/test/dates/io.jl +++ b/test/dates/io.jl @@ -445,3 +445,42 @@ end # Ensure that no overflow occurs when using Int32 literals: Int32(10)^10 @test Dates.parse_components("." * rpad(999, 10, '0'), Dates.DateFormat(".s")) == [Dates.Millisecond(999)] end + +# Time Parsing +let + time_tuple(t::Dates.Time) = ( + Dates.hour(t), Dates.minute(t), Dates.second(t), + Dates.millisecond(t), Dates.microsecond(t), Dates.nanosecond(t) + ) + + ## default ISOTimeFormat + t = Dates.Time("01") + @test time_tuple(t) == (1, 0, 0, 0, 0, 0) + t = Dates.Time("01:23") + @test time_tuple(t) == (1, 23, 0, 0, 0, 0) + t = Dates.Time("01:23:45") + @test time_tuple(t) == (1, 23, 45, 0, 0, 0) + t = Dates.Time("01:23:45.678") + @test time_tuple(t) == (1, 23, 45, 678, 0, 0) + + ## string format + t = Dates.Time("23:56:12.1", "HH:MM:SS.s") + @test time_tuple(t) == (23, 56, 12, 100, 0, 0) + + ## precomputed DateFormat + t = Dates.Time("04:09:45.012", DateFormat("HH:MM:SS.s")) + @test time_tuple(t) == (4, 9, 45, 12, 0, 0) + t = Dates.Time("21 07", DateFormat("HH MM")) + @test time_tuple(t) == (21, 7, 0, 0, 0, 0) + t = Dates.Time("4.02", DateFormat("H.MM")) + @test time_tuple(t) == (4, 2, 0, 0, 0, 0) + t = Dates.Time("1725", DateFormat("HHMM")) + @test time_tuple(t) == (17, 25, 0, 0, 0, 0) + + ## exceptions + @test_throws ArgumentError Dates.Time("24:00") # invalid hours + @test_throws ArgumentError Dates.Time("00:60") # invalid minutes + @test_throws ArgumentError Dates.Time("00:00:60") # invalid seconds + @test_throws ArgumentError Dates.Time("20:03:20", DateFormat("HH:MM")) # too much precision + @test_throws ArgumentError Dates.Time("10:33:51", DateFormat("YYYY-MM-DD HH:MM:SS")) # Time can't hold year/month/day +end