-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DateFormat can't handle microseconds? #31525
Comments
I think this discussion on discourse is related. |
@vtjnash although we have a |
with
|
An upvote for this issue - I have a JSON API which I'd like Julia to talk to and parse into structs - I'm new to the language - and settled on JSON3, HTTP as the apparent libraries of choice. All works startlingly well except with DateTime, which won't accept microseconds. As I have no control over the API, and all the parsing is handled in the libraries, I actually don't know what the solution to this problem is except to leave the dates as String in the struct. My google-fu has failed me on other solutions ... but this No DateTime solution is distasteful, and I fear the extra pain down the road - to the extent that I will consider reverting to scala for this use case. For me, this use case for the constructor is for someone like me, trying Julia. I'm sure a reasonably competent person expert has a workaround to this "microsecond API JSON problem" - a stupid one I grant you as I really don't care about the 4-6th digits. However, I can't find a workaround, and it is scary to me as a newcomer, that managing a fairly simple third party API appears to be a problem beyond my skills - this is normally a trivial thing in modern languages. If I understand correctly, the addition of the extra constructor makes that whole scary problem never appear... I'll also post an issue in JSON3, to see if there is a known workaround. Restated - a microsecond constructors eases DateTime interop with other languages. |
DateTime is limited to millisecond precision by design, but Time has nanosecond precision. It would be nice to be able use DateFormats with multiple julia> t=Time(1,2,3,456,789)
01:02:03.456789
julia> Dates.format(t, dateformat"HH:MM:SS.ssssss")
"01:02:03.456000"
julia> Time("01:02:03.456789", dateformat"HH:MM:SS.ssssss")
ERROR: InexactError: convert(Dates.Decimal3, 456789) |
I also have a use case for this. We use nanosecond-precision timestamps for research into networked AV media. The data structures we use for debugging output microsecond precision times. It would be useful to parse these for analysis & visualisation in Julia. |
I just spent quite a bit of time* trying to find out why my log timestamps are being rounded to the nearest ms. It turns out that the answer is So, I whip up a microsecond formatting function, how hard can it be? julia> formatus(t::Time) = string(Dates.format(t, "HH:MM:SS"), ".", lpad(microsecond(t), 6, "0"))
julia> formatus(Time(Nanosecond(1_000_000_000)))
"00:00:01.000000"
julia> formatus(Time(Nanosecond(1_000_123_000)))
"00:00:01.000123" ... so far so good, but then: julia> formatus(Time(Nanosecond(1_123_123_000)))
"00:00:01.000123" The milliseconds part disappeared! The way You can put ns in, and get ns back out again... julia> Time(Nanosecond(5))
00:00:00.000000005
julia> nanosecond(Time(Nanosecond(5)))
5 Except that if you put in more than 999 ns you don't get all of them back... julia> Time(Nanosecond(5005))
00:00:00.000005005
julia> nanosecond(Time(Nanosecond(5005)))
5 With μs you're not allowed to use values over 999. julia> Time(Microsecond(5))
00:00:00.000005
julia> Time(Microsecond(5005))
ERROR: ArgumentError: Microsecond: 5005 out of range (0:999) Is dt = Dates.DateTime(2000, 1, 1, 23, 59, 59, 50)
t = Dates.Time(dt)
@test Dates.hour(t) == 23
@test Dates.minute(t) == 59
@test Dates.second(t) == 59
@test Dates.millisecond(t) == 50
@test Dates.microsecond(t) == 0
@test Dates.nanosecond(t) == 0 Is there a use-case where this is desirable? I get that It would seem less surprising to have: @test Dates.microsecond(t) == 50_000
@test Dates.nanosecond(t) == 50_000_000 *(My log messages originate on an 8-bit micro, measuring a physical system with ~ μs resolution. They are passed to another 8-bit micro over SPI, then through a serial port to a raspberry Pi running Julia, then through WiFi to Julia on a mac, then through an RPC link to a log monitoring GUI, I checked everywhere else before suspecting that Dates.format might be broken) |
|
The documentation for
There is no mention that the return values are modulo 1000 and modulo 1000000.
To me, given that documentation, this seems broken:
|
@notinaboat I have previously had the same problem. The workaround in your case is: julia> using Printf: @sprintf
julia> formatus(t::Time) = @sprintf("%s.%03d%03d", Dates.format(t, "HH:MM:SS"), millisecond(t), microsecond(t))
formatus (generic function with 1 method)
julia> formatus(Time(Nanosecond(1_234_567_890)))
"00:00:01.234567" This should probably be in a new issue, but… To solve the documentation issue of what
For
Or much of the above could instead go into the second docstring shown, which currently is:
Happy to make a PR if this sounds sensible. None of the above actually addresses the issue at hand (that |
The implementation issue which is confusing is that Perhaps [Edited to refer correctly to |
@anowacki helpful workaround. regarding _this issue: DateFormat doesn't handle the full resolution of Times -- just for fun
|
Nanosecond does not limit values to 999 because the finest resolved unit of time (the smallest Period) must be available as an Int64 valued destination to keep internal |
Indeed—the issue is not with the constructors of any of the Instead, the issue is because of the
When you do However, when you do Perhaps a resolution to all of this is something like: struct TimeInstant{P<:TimePeriod} <: Instant
periods::P
end
struct Time <: TimeType
instant::Nanosecond
Time(instant::TimeInstant{Nanosecond}) = new(mod(instant.periods, 86400000000000))
end and then call the inner constructor by wrapping |
@anowacki thanks for posting the @JeffreySarnoff I see that Its seems there are two fundamental problems here. First, implementation detail is leaking out and polluting the generic interface as @anowacki describes above. Second, the current interface implies the unusual idea that ms, μs and ns should be treated as seperate "columns" (i.e. that each of these values is limited to three decimal digits). The decision to return nanoseconds modulo 1000 by default seems very unusual. As it stands if I want to get the fractional seconds part of a Julia e.g. go has t := time.Duration(10_123_456_789)
fmt.Println(t.Microseconds())
10123456
fmt.Println(t.Nanoseconds())
10123456789 Rust lets you ask for let t = Duration::from_nanos(10_123_456_789);
println!("{:?}", t);
10.123456789s
println!("{:?}", t.as_micros());
10123456
println!("{:?}", t.as_nanos());
10123456789
println!("{:?}", t.subsec_micros());
123456
println!("{:?}", t.subsec_nanos());
123456789 Swift has only one sub-second accessor: var t = DateComponents.init(nanosecond:10_123_456_789)
print("\(t.nanosecond!)")
10123456789 |
@notinaboat Thank you for the clarification and specificity of your notes. Although I have not thought about them for a year or two, I am aware of these issues and the preference for uniformity. It is possible to experiment with a modified Dates as an external package [say, AltDates.jl] that begins as a duplicate of Dates from https://github.com/JuliaLang/julia/tree/master/stdlib/Dates. Experimenting with the least change that provides the most satisfaction is recommended, and benchmarking the use of Dates and AltDates with their tests and with e.g. DataFrames and TimeSeries matters. |
@notinaboat I agree that the accessor function name is somewhat misleading. Maybe we could add functions to obtain the value of a What you want is done with Dates.value. Note that the unit of the integer you obtain depends on the type of the
|
That's interesting @jebej. Dates.value is documented as In my own code, I've decided to use Float64 seconds everywhere instead of @JeffreySarnoff, with regard to alternative time packages, I've found one that looks serious https://github.com/FugroRoames/Chrono.jl. However I don't have time available to work on time packages. I am spending a little time posting here in the hope that whoever next works on FWIW, a few suggested changes focused on the principle of least surprise...
# Current behaviour...
julia> Nanosecond(Time(Nanosecond(1000)))
0 nanoseconds
# New method:
julia> Dates.Nanosecond(t::Time) = t.instant
julia> Nanosecond(Time(Nanosecond(1000)))
1000 nanoseconds
julia> Nanosecond(Time(Nanosecond(10_123_456_789)))
10123456789 nanoseconds
|
I was not suggesting that you rework Dates -- my notes are for others who will find this at some later date. For all things carefully timed (overkill for your use) https://github.com/JuliaAstro/AstroTime.jl [nonetheless if you need to check results, this is reliable]. It is well understood by the community of people who write time libraries that using Floats is not safe and may introduce subtle, and difficult to track down (or to recognize) errors. Here is a thread from the early days of Julia |
While trying to parse our AV logs (https://ci.appveyor.com/api/projects/JuliaLang/julia/history?recordsNumber=1&startBuildId=20355614), I realized that we apparently can't handle times more precise than milliseconds (even though we have Dates.Microsecond):
The text was updated successfully, but these errors were encountered: