diff --git a/Project.toml b/Project.toml index 7abff29..6d485ee 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "UnixTimes" uuid = "ab1a18e7-b408-4913-896c-624bb82ed7f4" authors = ["Christian Rorvik "] -version = "1.7.2" +version = "1.8.0" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/src/UnixTimes.jl b/src/UnixTimes.jl index 9bfa05a..a417fb0 100644 --- a/src/UnixTimes.jl +++ b/src/UnixTimes.jl @@ -63,14 +63,34 @@ UnixTime(x::Date, y::Time) = const DATEFORMAT = dateformat"yyyy-mm-ddTHH:MM:SS.sss" function UnixTime(s::AbstractString) - try - @assert length(s) == 29 - dt = DateTime(chop(s; tail=6), DATEFORMAT) - ns = Nanosecond(parse(Int, last(s, 6))) - UnixTime(dt) + ns - catch - throw(ArgumentError("Invalid UnixTime string")) + # Find the decimal point if it exists + dot_idx = findfirst('.', s) + + # No fractional seconds, parse as DateTime + isnothing(dot_idx) && return UnixTime(DateTime(s, DATEFORMAT)) + + # Split into datetime and fractional parts + dt_part = @view s[1:(dot_idx - 1)] + frac_part = @view s[(dot_idx + 1):end] + + # Parse the datetime part + dt = DateTime(dt_part, DATEFORMAT) + + # Parse fractional seconds (can be 1-9 digits) + if isempty(frac_part) || !all(isdigit, frac_part) + throw(ArgumentError("Invalid fractional seconds in UnixTime string")) end + + # Convert fractional seconds to nanoseconds + # Pad or truncate to 9 digits (nanosecond precision) + frac_digits = length(frac_part) + ns_value = if frac_digits <= 9 + parse(Int, frac_part) * 10^(9 - frac_digits) + else + parse(Int, frac_part[1:9]) + end + + return UnixTime(dt) + Nanosecond(ns_value) end function Base.convert(::Type{UnixTime}, x::DateTime) @@ -78,6 +98,9 @@ function Base.convert(::Type{UnixTime}, x::DateTime) UnixTime(Dates.UTInstant(Nanosecond(instant_ns))) end +# Promotion rules for comparing UnixTime with DateTime +Base.promote_rule(::Type{UnixTime}, ::Type{DateTime}) = UnixTime + function Base.show(io::IO, x::UnixTime) xdt = convert(DateTime, x) print(io, Dates.format(xdt, DATEFORMAT)) diff --git a/test/runtests.jl b/test/runtests.jl index ef499a8..94fbfa4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -48,9 +48,22 @@ end x = UnixTime(2020, 1, 2, 3, 4, 5, 6, 7, 8) @test string(x) == "2020-01-02T03:04:05.006007008" @test UnixTime(string(x)) == x - @test_throws ArgumentError UnixTime("2025-01-28T15:49:36.5901397023") - @test_throws ArgumentError UnixTime("2025-01-28T15:49:36.59013970") - @test_throws ArgumentError UnixTime(repeat("a", 29)) + + # Test various string formats + @test UnixTime("2020-01-02T03:04:05") == UnixTime(2020, 1, 2, 3, 4, 5) + @test UnixTime("2020-01-02T03:04:05.1") == UnixTime(2020, 1, 2, 3, 4, 5, 100) + @test UnixTime("2020-01-02T03:04:05.12") == UnixTime(2020, 1, 2, 3, 4, 5, 120) + @test UnixTime("2020-01-02T03:04:05.123") == UnixTime(2020, 1, 2, 3, 4, 5, 123) + @test UnixTime("2020-01-02T03:04:05.1234") == UnixTime(2020, 1, 2, 3, 4, 5, 123, 400) + @test UnixTime("2020-01-02T03:04:05.123456") == UnixTime(2020, 1, 2, 3, 4, 5, 123, 456) + @test UnixTime("2020-01-02T03:04:05.123456789") == UnixTime(2020, 1, 2, 3, 4, 5, 123, 456, 789) + + # Test truncation of extra digits + @test UnixTime("2020-01-02T03:04:05.1234567890") == UnixTime(2020, 1, 2, 3, 4, 5, 123, 456, 789) + + # Test invalid formats + @test_throws ArgumentError UnixTime("2020-01-02T03:04:05.") + @test_throws ArgumentError UnixTime("2020-01-02T03:04:05.abc") end @testset "arithmetic" begin @@ -102,4 +115,15 @@ end @test collect(t1:p:t2) isa Vector end +@testset "comparison with DateTime" begin + ut = UnixTime(2001, 03, 21, 13, 10, 0, 0) + dt = DateTime("2001-03-21T13:10:00") + + @test ut == dt + @test ut > DateTime("2001-03-21T13:00:00") + @test ut < DateTime("2001-03-21T13:20:00") + @test DateTime("2001-03-21T13:20:00") > ut + @test UnixTime(2001, 03, 21, 13, 10, 0, 114, 549, 673) > dt +end + end