[HN Gopher] Jiff: Datetime library for Rust
___________________________________________________________________
Jiff: Datetime library for Rust
Author : goranmoomin
Score : 417 points
Date : 2024-07-22 04:57 UTC (18 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| sam0x17 wrote:
| Not to mention that BurntSushi is the author of the entire rust
| regex ecosystem
| dhosek wrote:
| I remember getting into a debate with him on Reddit about
| something or other and then realizing who I was debating with
| and saying never mind.
| ramon156 wrote:
| That shouldn't stop you from settling an argument (given it
| was respectful). The best way to learn is to be wronged by
| smarter people.
| komadori wrote:
| Also, just because someone is famous/important/etc doesn't
| mean they're always right. In fact, one of the dangerous
| things about being famous is the people stop being willing
| to disagree with you and that can lead to becoming detached
| and warped as a person.
| qup wrote:
| I feel very certain that's not the best way to learn.
| sam0x17 wrote:
| training data is training data
| sam0x17 wrote:
| Yeah for example I've given him crap for years for not
| having the will to bring back compile-time regex in Rust
| even though all the pieces for it are there in his `regex-
| automata` crate ;)
| dhosek wrote:
| Well, the flip side of it was that in benchmarking our
| equivalent codebases for Unicode segmentation, my code was
| significantly faster, to both of our surprises as it turned
| out.
| sushibowl wrote:
| Overall this looks nice, but I found myself stumbling over the
| ToSpan syntax: let span =
| 5.days().hours(8).minutes(1);
|
| It feels sort of weird how the first number appears in front, and
| then all the other ones are function arguments. I suppose if you
| don't like that you can just write: let span =
| Span::new().days(5).hours(8).minutes(1);
|
| at the expense of a couple characters, which is not too bad.
| Galanwe wrote:
| If only Rust had named function parameters, you could write
| what is IMHO the most readable option:
| Span::new(days=5, hours=8, minutes=1)
| DemocracyFTW2 wrote:
| could you do that with `struct` / `record` fields? In
| JavaScript which doesn't have named function parameters
| either I often write functions with a single `cfg` parameter
| that are called like `f({ hours: 2, seconds: 53, })` which I
| find nice b/c it re-uses existing data structures.
| Galanwe wrote:
| Well the beautiful thing about software engineering is that
| pretty much everything is possible, it essentially boils
| down to "but should you really"? :-)
| devanl wrote:
| In Rust, you can't implicitly omit fields when
| instantiating a struct, so it would have to be a bit more
| verbose, explicitly using Rust's analog to the spread
| syntax.
|
| It would have to look something like: f({
| hours: 2, seconds: 53, ..Default::default() })
|
| The defaults could come from some value / function with a
| name shorter than Default::default(), but it would be less
| clear.
| estebank wrote:
| Adding support for struct default field values would
| allow for
|
| - leaving some mandatory fields
|
| - reduce the need for the builder pattern
|
| - enable the above to be written as f(S { hours: 2,
| seconds: 53, .. })
|
| If that feature ever lands, coupled with
| structural/anonymous structs or struct literal inference,
| you're getting everything you'd want from named arguments
| without any of the foot guns.
| kibwen wrote:
| Has anyone ever proposed it? It's such a straightforward
| feature with such obvious semantics ("default field
| values are const contexts") and impossible-to-bikeshed
| syntax (`a: i32 = 42`) that I've been meaning to write up
| an RFC myself for around, oh, ten years now...
| 4hg4ufxhy wrote:
| Kind of inefficient. Also it's less ergonomic since every
| struct is it's own type, so you need to have the signature
| on both sides.
| benmmurphy wrote:
| It should compile to the same because a struct passed by
| value is loaded into the registers the same as method
| arguments.
| _flux wrote:
| Yes, that could've been lended almost as-is from OCaml, in
| particular as Rust doesn't have partial application so
| optional arguments would work out-of-the-box as well.
| berkes wrote:
| Are named arguments on the roadmap somewhere? Or is it a
| won't-fix?
| _flux wrote:
| I haven't seen anything for or against in actual
| roadmaps. But there is, of course, at least one pre-
| proposal:
|
| https://internals.rust-lang.org/t/pre-rfc-named-
| arguments/16...
|
| It doesn't think about optional arguments (but somehow
| does include overloading). And a bit in a related
| fashion, it doesn't permit for reordering calling
| arguments, which I consider a downside:
|
| > Reordering named arguments when calling
|
| > No it is not possible. Just like unnamed arguments and
| generics, named arguments are also position-based and
| cannot be reordered when calling: register(name:surname:)
| cannot be called as register(surname:name:).
|
| > Reordering them at the definition site is an API break,
| just like reordering unnamed arguments or generics is an
| API break already.
|
| The rationale for this expressed in the comments says
| it's incompatible with overloading, but to me I don't see
| why named arguments and overloading should go hand-in-
| hand--or, indeed, how desirable overloading is in the
| first place. Or why should overloading be able to
| overload that kind of scenario. The other reasons for
| this don't seem really problems at all.
| > fn func2(pub name: u32, name hidden: u32) { /\* ... */
| } > fn func3(name hidden1: u32, name hidden2:
| u32) { /* ... */ }
|
| > func2 and func3 could work in theory: named arguments
| as proposed in this RFC are position-based and their
| internal names are different: just like two arguments can
| have the same type without ambiguity, those functions
| could be allowed.
|
| Maybe there are technical reasons that are simpler when
| considering type compatibility between function types
| that have or don't have labeled arguments? Seems the
| proposal has misunderstood something about OCaml labeled
| arguments when placing it under https://internals.rust-
| lang.org/t/pre-rfc-named-arguments/16... , though.
|
| In addition the proposal doesn't seem to have a neat
| syntax for forwarding named parameters, like in
| constructing records you can just fill in a field called
| foo by mentioning its name by itself--or, like in OCaml
| you can have let foo ~bar = bar + 1
| let baz ~bar = foo ~bar let main () = baz
| ~bar:42
|
| If it used the .-prefix as mentioned as an idea
| elsewhere, then this too could be naturally expressed.
|
| Maybe there are other ideas how to go about the labeled
| arguments, though that one seems pretty well thought-out.
|
| One thing I've enjoyed with Python (and Mypy) is the
| ability to _require_ the caller to use named arguments
| with the asterisk marker in the parameter list. This idea
| is mentioned in the proposal.
| PoignardAzur wrote:
| The feature is controversial enough that it's basically a
| wontfix.
| _flux wrote:
| Is it really, though? I didn't read all the comments, but
| the ones I read, very few were opposing the concept as
| such: https://internals.rust-lang.org/t/pre-rfc-named-
| arguments/16...
|
| However, many were opposing overloading the same pre-rfc
| was suggesting.
| darby_nine wrote:
| I'm guessing that general support doesn't translate to
| support for a specific syntax with changes to the calling
| convention. I wouldn't put money on this coming together
| any time soon.
| nurettin wrote:
| I'm all for named parameters. C++ is sorely lacking that
| feature as well.
|
| Currently using vs code with C++, I like how it handles the
| missing language feature by adding a grayed out parameter
| name before the value for function calls and initializers.
| Maybe there is something like that for rust.
| umanwizard wrote:
| Yes, editors can be configured to do the same thing for
| rust.
| hypeatei wrote:
| These are called "inlay hints" and exist for most
| editors/languages.
| tomas789 wrote:
| I agree with that. In my thought process is to specify what I'm
| doing and only then some details. This is the other way around.
| When reading the code, it would be better to see that I'm
| dealing with span at first.
| DemocracyFTW2 wrote:
| I stumbled over use jiff::{Timestamp,
| ToSpan}; fn main() -> Result<(), jiff::Error> {
| let time: Timestamp = "2024-07-11T01:14:00Z".parse()?;
|
| I seem to remember Rust does that thing with interfaces instead
| of classes, is it that? How come I import a library and all of
| a sudden strings have a `parse()` method that despite its
| generic name results in a `Timestamp` object? or is it the
| left-hand side that determines which meaning `str.parse()`
| should have? What if I have two libraries, one for dates and
| one for say, Lisp expressions that both augment strings with a
| `parse()` method? Why use this syntax at all, why not, say,
| `Timestamp.parse( str )`? I have so many questions.
| Tigress8780 wrote:
| Rust will determine what `parse` does based on the inferred
| return type (which is being explicitly set to `Timestamp`
| here). This is possible when the return type has `FromStr`
| trait.
| DemocracyFTW2 wrote:
| oh I see, almost what I anticipated
| kam wrote:
| `parse` is actually an inherent method on `str` that always
| exists: https://doc.rust-
| lang.org/core/primitive.str.html#method.par...
|
| Its return type is generic, and here it's inferred from the
| left hand side. It's implemented using the `FromStr` trait,
| and you can equivalently write `Timestamp::from_str(t)`.
|
| You're thinking of the "extension trait" pattern for using
| traits to add methods to existing types when the trait in
| scope, but that's not what's going on here. Jiff's `ToSpan`
| mentioned above is an example of that pattern, though:
| https://docs.rs/jiff/latest/jiff/trait.ToSpan.html
| nrabulinski wrote:
| It's because Timestamp implements the FromStr trait which is
| one of the first traits everyone learns about when learning
| rust. So when you say that your value is a Timestamp and the
| expression is string.parse()?, the compiler knows that it has
| to use the implementation which returns a Timestamp.
|
| There will never be two libraries that clash because of
| Rust's orphan rule: you can only implement either a trait
| which you define on any type, or define a foreign trait on a
| type which you define, so there's no way for some random
| library to also ship an implementation of FromStr for
| Timestamp
| csomar wrote:
| It's implied. Here is the full syntax.
|
| > let time: Timestamp =
| "2024-07-11T01:14:00Z".parse::<Timestamp>()?;
| dhosek wrote:
| That ::<TYPE> thing at the end is called a turbofish. It is
| rarely necessary to give it explicitly (but sometimes you
| do when the compiler cannot infer the return type on its
| own--thusfar in my own rust coding I've needed it exactly
| once).
| bluejekyll wrote:
| It's useful to know the full syntax, I've definitely
| encountered needing it more than one time.
| nindalf wrote:
| All of these options work and are equivalent.
|
| - let time = Timestamp::parse("2024-07-11T01:14:00Z")?;
|
| - let time: Timestamp = "2024-07-11T01:14:00Z".parse()?;
|
| - let time = "2024-07-11T01:14:00Z".parse::<Timestamp>()?;
|
| You're free to choose whatever you prefer, although the
| compiler needs to be able to infer the type of time. If it
| can't, it'll let you know.
|
| So a fourth option is allowed, as long as the subsequent
| lines make the type of time unambiguous.
|
| - let time = "2024-07-11T01:14:00Z".parse()?;
|
| This is a direct consequence of Timestamp implementing the
| FromStr trait.
| Sharlin wrote:
| let time = Timestamp::from_str("2024-07-11T01:14:00Z")?;
|
| I think you meant :)
| nindalf wrote:
| Haha, yes I did. If only the HN textbox integrated with
| rust-analyzer, it would have caught the mistake.
| 0x457 wrote:
| > I have so many questions.
|
| Not being snarky, but I suggest starting by reading at least
| a little about traits? None of your questions are really
| about this library - it's just FromStr and an orphan rule.
| DemocracyFTW2 wrote:
| To make it clear, I didn't want to be the snark either.
| Just wondered about the usual things like "locally
| detectable semantics" and so on. I still think
| `Library.method( argument )` wins over `argument.method()`
| for the simple reason that the former has `Library`
| explicitly mentioned. Also, `door.open()`, really? I think
| `strongman.open( door )` is just that much clearer,
| flexible and explicit.
| frereit wrote:
| I agree. Personally, I'd prefer let span =
| 5.days() + 8.hours() + 1.minutes();
| csomar wrote:
| I wonder if OP will accept a PR for such a change. Your
| proposal is much readable and flexible (it's not clear from
| the docs if you can add random time ranges together). Plus,
| you'll be able to create your own ranges like `1.decade` or
| `1.application_timeframe` and add/subtract them.
| mijoharas wrote:
| Have you checked the API to see if that works? I imagine it
| does.
| mutatio wrote:
| Looks like it should be supported:
| https://docs.rs/jiff/latest/jiff/struct.Span.html#impl-
| Add%3...
| wging wrote:
| That isn't an implementation of addition between Spans and
| other Spans. It looks like there isn't one in the library
| right now. `impl<'a> Add<Span> for &'a Zoned` means a
| borrow of Zoned is on the left hand side, and a Span on the
| right. So it says that if z is a Zoned (not a Span) and s
| is a Span, you can do `&z + s` to add a span to a Zoned.
| There are a bunch of implementations there, DateTime +
| Span, Date + Span, Time + Span, Offset + Span. All with
| Span on the right, but none for Span + Span (nor Span +
| &Span, or &Span + &Span, ...).
| burntsushi wrote:
| This is correct. You can't do a `span1 + span2`. You'd
| have to use `span1.checked_add(span2)`. The main problem
| I had with overloading `+` for span addition is that, in
| order to add two spans with non-uniform units (like years
| and months), you need a relative datetime. So `+` would
| effectively have to panic if you did `1.year() +
| 2.months()`, which seems like a horrific footgun.
|
| It would be plausible to make `+` for spans do _only_
| component wise addition, but this would be an extremely
| subtle distinction between `+` and `Span::checked_add`.
| To the point where sometimes `+` and `checked_add` would
| agree on the results and sometimes they wouldn't. I think
| that would also be bad.
|
| So I started conservative for the time being: no `+` for
| adding spans together.
| stouset wrote:
| What's the issue with having a span represent "one year
| and two months"? Both involve a variable number of days
| (365-366 and 28-31), but I would hope you could store the
| components separately so it can be unambiguously applied
| once given a specific moment in time.
|
| I'm thinking something along the lines of how
| ActiveSupport::Duration works: >>
| 4.years + 5.months + 3.weeks + 2.days + 1.hour +
| 4.minutes + 10.seconds => 4 years, 5 months, 3
| weeks, 2 days, 1 hour, 4 minutes, and 10 seconds
|
| Of course the downside being that it would need to be
| sized large enough to contain each component, even though
| they may be rarely used.
| burntsushi wrote:
| The components are stored separately. I think what you
| are advocating for is component wise addition, which I
| mentioned in my previous comment. It can totally "work,"
| but as I said, it will produce different results than
| `span1.checked_add(span2)`. For example:
| use jiff::{ToSpan, Unit, Zoned}; fn main()
| -> anyhow::Result<()> { let span1 =
| 1.year().months(3); let span2 = 11.months();
| let now = Zoned::now().round(Unit::Minute)?;
| let added = span1.checked_add((span2, &now))?;
| println!("{added}"); Ok(()) }
|
| Has this output: $ cargo -q r
| P2y2m
|
| Notice how the months overflow automatically into years.
| Days will do the same into months. You need a reference
| point to do this correctly. For example, just
| `span1.checked_add(span2)?` would produce an error.
|
| In contrast, component wise addition would lead to a span
| of `1 year 14 months`. Which is a valid `Span`. Jiff is
| fine with it. But it's different than what `checked_add`
| does. Having both operations seems too subtle.
|
| Also, I don't really think using the `+` operator just to
| construct spans is that much of a win over what Jiff
| already has. So that needs to be taken into account as
| well.
| Sharlin wrote:
| Yeah, or there could simply be a `days()` free function (and
| equivalents of the other methods too). No need for struct
| constructors to be associated functions.
| creata wrote:
| I haven't tried it (so I'm sorry if it's wrong or not what
| you're talking about) but can't you get a freestanding days
| function by use jiff::ToSpan::days;
| the_mitsuhiko wrote:
| You cannot import trait methods as free standing functions.
| I'm not sure if there was a discussion about making this a
| possibility but it's definitely not something you can do
| today.
| creata wrote:
| Oh, sorry about that then.
| dathinab wrote:
| multiple discussions happened for this and I don't quite
| remember the outcome.
|
| But it's much less simple then it seems.
|
| Because `use Trait::method` would not be one (potential
| generic) method but a group of them so it would be it's
| own kind of thing working differently to free functions
| etc. Furthermore as generics might be on the trait you
| might not be able to fill them in with `::<>` and even if
| you fill them in you also wouldn't be able to get a
| function pointer without having a way to also specify the
| type the trait is implemented on.
|
| All of this (and probably more issues) are solvable AFIK
| but in context of this being a minor UX benefit its IMHO
| not worth it, 1. due to additional compiler complexity
| but also due to 2. additional language complexity.
| Through maybe it will happen if someone really cares
| about it.
|
| Anyway until then you can always define a free function
| which just calls the method, e.g. `fn default<T:
| Default>() -> T { T::default() }`. (Which is probably
| roughly how `use` on a trait method would work if it
| where a thing.)
| the_mitsuhiko wrote:
| Ignoring the technical and UX complexities the desire is
| somewhat obvious. Particularly for things like
| ::default().
| gpderetta wrote:
| Can you do 5.days() + 8.hours() + 1.minutes()?
| coldtea wrote:
| I like your version's consistency.
|
| The original looks like something Ruby would do.
| waterhouse wrote:
| Or 0.days(5).hours(8).minutes(1)?
| tempodox wrote:
| Great, that gives us 0-days.
| arijun wrote:
| A little off topic but does anyone know the purpose of dual
| licensing MIT and the UNLICENSE? It seems like the second should
| already allow anyone to do whatever they want...
| nsajko wrote:
| The "Unlicense" is not considered as serious.
| GolDDranks wrote:
| What I gather about the author's thoughts about that, he isn't
| a fan of copyright in general, and uses UNLICENSE as an
| ideological statement, plus a practical way of saying "do
| whatever you want with this", but also slaps the option to use
| MIT as "something almost as good" because non-standard licenses
| deter corporate types, which kind of defeats the original "do
| whatever you want" purpose of UNLICENSE :D
| globular-toast wrote:
| That's what I gather from Unlicense too (in fact, this is
| confirmed in a linked bug thread where the author says he
| "hates copyright").
|
| I think the author is actually looking for the GPL but
| doesn't realise it yet. Unlicense can't make something free
| forever, no matter how hard the author wishes it. GPL can. In
| other words, Unlicense/MIT is idealistic, GPL is pragmatic.
| You can't turn off copyright, but you can make it work for
| the people instead of against them.
| rahkiin wrote:
| Not at all. If this library was GPL, any software using it
| also needs to be GPL. This means all code needs to be open
| source, which severely limits freedom of makers of end-user
| software.
|
| And almost the whole Rust ecosystem is MIT.
| richrichardsson wrote:
| > severely limits freedom of makers of end-user software
|
| Ironic for a "free" software license.
|
| It would be great if there was a license somewhere
| inbetween GPL and MIT: you'd be required to upstream (or
| make available) any changes you made to the parts of
| other people's code you're making use of, but _not_
| required to open your entire codebase.
| jstarks wrote:
| I think the MPL attempts to be that license.
| GoblinSlayer wrote:
| That's LGPL.
| agosz wrote:
| MPL or CDPL
| globular-toast wrote:
| > which severely limits freedom of makers of end-user
| software
|
| And thereby severely _guarantees_ the freedom of said
| end-users.
|
| The freedom to deny the freedom of another person is not
| a freedom worth discussing.
|
| The author expressly dislikes copyright. GPL is still the
| only real cure to copyright. "Permissive" licences are
| corporate friendly. They allow corporations to take what
| they want and give back nothing. In this day and age it's
| more important than ever to empower individuals and limit
| the growth of corporations/oligopolies.
| usea wrote:
| > The freedom to deny the freedom of another person is
| not a freedom worth discussing.
|
| If that was true, you wouldn't be doing just that.
| globular-toast wrote:
| So, to be clear, your argument is that the freedom to
| deny the freedom of other people is a freedom that should
| be protected? How do you deal with issues like slavery
| and, in particular, its abolishment?
| gjm11 wrote:
| usea's argument is clearly not that but only that you
| can't literally think something is "not worth discussing"
| while you are actually discussing it.
|
| The person who was explicitly defending non-GPL licences
| was rahkiin. I don't know how they'd respond to your
| challenge, but here is how I would:
|
| "The freedom to deny the freedom of other people" is
| impossibly vague, because "the freedom of other people"
| can mean zillions of things. It's also confusing to talk
| about since we have two separate freedoms here, so let's
| talk about the freedom(1) to deny the freedom(2) of other
| people.
|
| Suppose we put "the freedom to kill other people" in the
| freedom(2) slot. Most of us think that _isn 't_ a freedom
| people are entitled to, so the freedom(1) to deny that
| particular freedom(2) would be a good thing.
|
| Suppose we put "the freedom to breathe the air" in the
| freedom(2) slot. Most of us think that _is_ a freedom
| people are entitled to, so the freedom(1) to deny that
| particular freedom(2) would be a bad thing.
|
| In the present case, what goes in the freedom(2) slot is
| something more complicated and less clear-cut -- it isn't
| a Super-Obvious Fundamental Human Right like the right to
| go on breathing, but it also isn't a Right To Do Very
| Evil Things like the right to murder.
|
| It's something like "the freedom to read and modify the
| source code of a particular piece of software". We
| demonstrably _don 't_ presently have that freedom as
| regards many widely-used pieces of software; the world's
| legal systems pretty much unanimously agree that if you
| put this in the freedom(2) slot then the freedom(1) to
| deny it is worth having.
|
| Why? Well, the usual arguments would be (1) that creating
| something gives you some rights to limit what other
| people do with it, and (2) that giving creators some such
| rights is a good thing overall because it increases the
| incentives for people to create nice things.
|
| Of course you might disagree! (And, also of course, even
| if you agree with #1 and #2 in the abstract you might
| think that "intellectual property" law as currently
| implemented across the world is a very bad way to get #1
| and #2.) But I hope your reasons are a matter of thinking
| carefully about the tradeoffs involved, not just of
| saying "yay freedom" and therefore denying _every_
| instance of "it's good for X to have the freedom(1) to
| deny Y's freedom(2) to do Z".
|
| Not least because you literally _can 't_ consistently do
| so in every case -- if you say no one should ever have
| the freedom(1) to deny freedom(2) to others, whatever
| specific freedom(2) may be, then what you are calling for
| is _precisely_ to deny that freedom(1) to others.
| nindalf wrote:
| Burntsushi has written many important crates in the Rust
| ecosystem. He started with licensing under Unlicense
| exclusively, until people requested a dual license with MIT.
| See this issue from 2016 for more details -
| https://github.com/BurntSushi/byteorder/issues/26
|
| Almost all of the Rust ecosystem is dual licensed under
| MIT/Apache 2.0, so this combination is a bit unusual. But the
| presence of MIT means that it hasn't been a problem in
| practice.
| Xylakant wrote:
| The unlicense is considered problematic in various
| jurisdictions, among them Germany - under German law, you
| cannot relinquish certain rights that are associated with the
| author at all. Dedicating something to the public domain is not
| a valid concept here. This means the whole license could be
| declared invalid in court. Other jurisdictions may be similarly
| problematic- thus the fallback to MIT
|
| There's a stackoverflow post that discusses some of the issues
| https://softwareengineering.stackexchange.com/questions/1471...
| chrismorgan wrote:
| My own summary and collection of information about the
| problems with the Unlicense:
| https://chrismorgan.info/blog/unlicense/
|
| (I collected that mostly because I didn't find all the
| relevant information in one place, or explanation of the
| reasonable alternatives.)
| Xylakant wrote:
| That looks like a pretty comprehensive overview. I'll
| bookmark this for further reference :)
| treeshateorcs wrote:
| i love your site!!
| rsynnott wrote:
| Never understood why anyone uses this one; it's just too
| potentially messy, and a permissive license like 0BSD
| provides the intended effect without the risk.
| burntsushi wrote:
| I wrote down why I do it years ago: https://github.com/Burn
| tSushi/notes/blob/master/2020-10-29_l...
|
| You may not agree with me, which is fine, but you should
| now _understand_.
| Xylakant wrote:
| Part of the indended effect is making a policical/societal
| point about the copyright system, something the 0BSD
| license does not do. I personally believe that legal
| documents are a bad place to make these points, but
| obviously people differ.
| goodpoint wrote:
| Should have used GPL or at least LGPL
| tomas789 wrote:
| The state of calendar libraries in Rust is less than ideal. When
| working with Pandas, there is .tz_convert() and .tz_localize()
| and that is basically it for timezone conversions. My benchmark
| for this is: given a date, get first hour of CET/CEST day in UTC.
| In Pandas a very simple operation. In Chrono, you have to have a
| NaiveDate, convert to DateTime<FixedOffset> and then to
| DateTime<Utc>. And there is no pattern in those conversions I
| managed to find. Sometimes it is a member function, other times a
| static method on the timezone object.
|
| I hope somebody will rectify this at some point. Jiff seems like
| a step in a right direction but the syntax is sometimes weird. I
| guess I'd wellcome something more predictable
| burntsushi wrote:
| That should be `date(y, m, d).intz("Europe/Rome")?`. If you
| want to get UTC from there, then add
| `with_time_zone(TimeZone::UTC)`.
|
| > Jiff seems like a step in a right direction but the syntax is
| sometimes weird. I guess I'd wellcome something more
| predictable
|
| Can you say more? How can Jiff be better?
| nsajko wrote:
| The title doesn't conform to the HN guidelines, dang.
| croemer wrote:
| Came here to say this :)
|
| The original tag line is "Jiff is a datetime library for Rust
| that encourages you to jump into the pit of success."
| carbonatom wrote:
| > The title doesn't conform to the HN guidelines, dang.
|
| +1
|
| I don't understand why people are downvoting your comment. This
| title is absolutely a violation of HN guidelines. And a very
| blatant one no less!
|
| Do people not read
| https://news.ycombinator.com/newsguidelines.html anymore?
| jodrellblank wrote:
| Those guidelines say "Please don't post on HN to ask or tell
| us something. Send it to hn@ycombinator.com.". That's what
| the parent commenter should have done.
|
| And "Please don't comment about the voting on comments. It
| never does any good, and it makes boring reading."; your
| comment is commenting about voting, and this guideline sets a
| precedent that boring reading is not desirable.
|
| and "Please don't comment on whether someone read an article.
| "Did you even read the article?"" which you are doing about
| the guidelines.
|
| and "Please don't complain about tangential annoyances--e.g.
| article or website formats, name collisions, or back-button
| breakage. They're too common to be interesting." which
| reinforces the idea that uninteresting comments are not
| deisable. "The title is bad" is not a very interesting
| comment.
|
| Pet annoyance recently is seeing people comment "why am I
| being downvoted? What I said was _true_ " when what they said
| was arguably true - but also low effort, flamebait, tangent,
| off-topic, or similar; "it is correct" alone isn't enough.
| Since this has been annoying me I want to write more, here
| are examples:
|
| Searching Algolia for 'downvoted':
| https://news.ycombinator.com/item?id=41039279 which says "
| _Microsoft is like Disney, they steal from others and trounce
| others for stealing from them. Absurd people._ " followed by
| " _Sad to see this extremely historically accurate and
| relevant comment downvoted._ ". It's arguably true - but also
| arguably false - but definitely a single line of low effort
| flamebait. Actually justifying the claim would be better.
|
| And https://news.ycombinator.com/item?id=41027729 - about
| Kamala Harris "I hate to say it yet I don't think she's
| polling well because even in 2024 I don't think America is
| ready to allow a woman of colour be POTUS." reply "You're
| downvoted, but you're correct.". Maybe it is correct, but
| they could at least argue the case for their claim? Something
| more substantial than a one line which is basically "America
| racist"? ("Eschew flamebait. Avoid generic tangents. Omit
| internet tropes." - guidelines).
|
| and https://news.ycombinator.com/item?id=41008957 CrowdStrike
| thread -> substantive comment on centralised control and
| security -> risks of splaying personal valuables out in front
| of everyone at security -> security tried to screw me because
| they had to do their job -> I got downvoted "by some swede
| who doesn't think this stuff can happen"; assuming the
| downvote is because people _don 't believe it happened_,
| rather than because it's marginally - if at all - relevant,
| substantive, thoughtful, interesting.
| alfiedotwtf wrote:
| I've been dealing with time and timezones for a long time, but
| this is the first time I have ever seen the "[Olson/Name]"
| suffix. Is that actually standard?
| Nekit1234007 wrote:
| I've seen it being used in ECMAScript's Temporal
| https://tc39.es/proposal-temporal/docs/strings.html
| alfiedotwtf wrote:
| Ah, thank you.
|
| Now that I'm back at my desk I had to check ISO8601, that
| suffix is not included. However it does look like an
| extension - RFC 9557 - which looks like is still in proposed
| state.
|
| I would personally caution using these suffixes until wider
| adoption, because AFAIK the Olson database names themselves
| are not standardised on non-POSIX systems (i.e you might have
| a hard time on Windows).
| lmz wrote:
| The database names are standardized:
| https://www.iana.org/time-zones
|
| The database itself may not be available but there are
| various implementations including Windows
| https://data.iana.org/time-zones/tz-link.html#software
| alfiedotwtf wrote:
| Aha! Yeah the last time I needed to play with timezones I
| had map between Olson and Windows's proprietary names.
| Looks like they came out their NIH Syndrome:
|
| https://learn.microsoft.com/en-
| gb/archive/blogs/bclteam/expl...
| bakkoting wrote:
| "Proposed" is the final state of many stable RFCs; see a
| list at [1], and compare it to the (much shorter) list of
| finished standards above it. The name is kind of misleading
| these days. Some discussion at [2]. Just to give an
| example, the base64 RFC 4648 is also a "proposed standard".
|
| [1] https://www.rfc-editor.org/standards#PS
|
| [2] https://datatracker.ietf.org/doc/html/rfc7127#section-2
| alfiedotwtf wrote:
| Oh no... I've been treating "Proposed" as "Looks nice but
| I'll look at it when it gets out of proposed. Doh!
| teohhanhui wrote:
| In addition to RFC meaning Request for Comments, when in
| fact they're standards lol
| burntsushi wrote:
| See for RFC 9557 document history:
| https://datatracker.ietf.org/doc/rfc9557/history/
|
| I believe java.time also uses this syntax. I'm addition to
| Temporal.
|
| It enables lossless round-trips of zoned datetimes. See:
| https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
| alfiedotwtf wrote:
| Ah. Yep, that makes sense. Thanks!
| 7bit wrote:
| So, ist it pronounced jiff or gif?
| sschueller wrote:
| The author gets to decide IMO. Gif was always pronounced "jif"
| according to the creator so I hope the creator of this new tool
| will pronounce Jiff as "gif" to avoid confusion...
| runiq wrote:
| Imma pronounce it biff, it's a time handling library after all.
| Sharlin wrote:
| https://github.com/BurntSushi/jiff/blob/master/DESIGN.md#why...
|
| > Jiff is pronounced like "gif" with a soft "g," as in "gem."
|
| Heh, I see what they did there...
| runiq wrote:
| We're only minutes away from GAYPEG, an image format which
| you pronounce with a soft g, like in GIF.
| neallindsay wrote:
| Yes
| mijoharas wrote:
| This looks like a cool library.
|
| Does anyone know why burntsushi is making this new library? I
| haven't messed around with times in rust much, but do the
| existing libraries have performance problems? Or are the existing
| API's awkward to use? Or is he just doing it for fun, or some
| other reason?
| Sharlin wrote:
| Linked from the readme:
|
| https://github.com/BurntSushi/jiff/blob/master/DESIGN.md
|
| https://github.com/BurntSushi/jiff/blob/master/COMPARE.md
|
| TLDR: API and IANA timezone support primarily.
| tikkabhuna wrote:
| In Java they had the same problem. The Java standard library
| implementation wasn't great, Jodatime came along to address
| those issues. Java 8 then introduced a new DateTime API that
| was heavily influenced by Jodatime with the benefit that as it
| is in the standard library, it can be more heavily adopted by
| library-writers.
|
| https://www.joda.org/joda-time/
| https://www.baeldung.com/java-8-date-time-intro
| WorldMaker wrote:
| There's an additional related stepping stone here (as it is
| name dropped in the library's design document as well) in
| that TC-39 has been hard at work on a proposal to standardize
| similar APIs into EcmaScript (JS) called Temporal:
| https://tc39.es/proposal-temporal/docs/
|
| Temporal benefits from the JodaTime/Java 8+ date work, but
| also includes more recent IETF and IANA standards as other
| influences.
| noxs wrote:
| The fact that we need a such complicated datetime library just
| means so many unncessary artificial complexities were introduced
| before (yes the daylight saving, leap seconds etc.)
| berkes wrote:
| > unncessary artificial complexities
|
| The very fact they are there, still used, and so on,
| contradicts "unnessary [sic]". Sure, it might be outdated now,
| or technically better alternatives might be there.
|
| But, in the end, software that deals with "The Real World" is
| going to be a complex, illogical mess. Because the real world
| is a complex, illogical mess. We could make a time that is
| global, counts resonant frequency of atoms. While technically
| superior, I will continue saying "The job took me 3h25 minutes"
| and not "The job took me 113,066,170,771,000,000 cycles", or
| even "The job took me 113066 Tera-cycles." or such. Messy,
| illogical and complex is often simply more practical. If only
| because "everyone does it that way".
| nindalf wrote:
| And we're going to say "let's do this a day from now",
| leaving the software to decide whether that's 24, 23 or 25
| hours from now. It could be any of those things, depending on
| where it was said and the DST changes for that timezone.
|
| Or conversely, is a specified instant considered "tomorrow"?
| GoblinSlayer wrote:
| Time library can be simple, it's just rust libraries tend to be
| philosophic for some reason, but it's only one of many design
| approaches.
| Smaug123 wrote:
| They can certainly be simple and incomplete, or simple and
| incorrect; do you have an example of a simple, complete, and
| correct time library?
| KingMob wrote:
| Why don't we standardize on kilosecs and megasecs!?
| raasdnil wrote:
| Has to be pronounced gif right?
| returnfalse wrote:
| Looks cool, thank you burntsushi for this. I have similar
| complaints about existing date-time libraries in rust. I'll
| replace chrono/time with Jiff in my projects.
| sva_ wrote:
| It's pronounced 'Giff' (with a hard 'G')
| sapiogram wrote:
| Non-native speaker here, what does "hard G" even mean? G as in
| "go", or G as in "gin"?
| Vinnl wrote:
| "G" as in "gadverdamme".
|
| (Native speaker here - but of Dutch, the language known for
| its proper hard Gs. Unless you're from the South.)
| Smaug123 wrote:
| "hard G" is the sound denoted "g" in IPA. The other "g" is
| "dZ".
| n_plus_1_acc wrote:
| That is the convention in english as least. Other languages
| may differ.
| Smaug123 wrote:
| Fortunately, since we are explicitly talking about
| English here, that convention is indeed the relevant one
| to use!
| stonemetal12 wrote:
| Hard G is "go". The name of the format if a play on words, in
| the US there is a brand of Peanut butter called "Jiff". The
| file format is supposed to be pronounced the same way (soft
| G). Some people, like the OP, claim that the G is supposed to
| be pronounced like the G in graphics (hard G). IDK why anyone
| cares.
| drtgh wrote:
| IMHO, unwrap() , expect() and company, has infected the Rust
| language so deeply that one wonders when (and not "if") will a
| library send a panic and crash the whole program.
|
| How could be erased those _panic!_ methods that are used in most
| of Rust 's libraries is something that may be is beyond the
| possible? beside is promoted from all the Rust's tutorials and
| reference code.
|
| So much correctness in the Rust language just for to promote to
| all the community to crash the program from libraries without
| handling the error is something I can not understand.
|
| I hope this philosophy do not reach the Linux kernel.
| db48x wrote:
| What does this have to do with anything?
| diggan wrote:
| Trying to figure this out as well... Tests have a bunch of
| .expect and .unwrap (which is to be expected), but core logic
| of the library doesn't seem to have any that seems they'll
| get in the way?
| hypeatei wrote:
| It's done in bad faith. Some are vehemently against Rust
| because of the "culture" around criticizing other languages'
| memory safety models, namely C/C++.
| db48x wrote:
| You may be right.
| aw1621107 wrote:
| > How could be erased those panic! methods that are used in
| most of Rust's libraries is something that may be is beyond the
| possible?
|
| It's arguably quite possible, though not as straightforwards as
| one may hope. For example, there's no_panic, which results in a
| linker error if the compiler cannot prove a function cannot
| panic [0], albeit with some caveats.
|
| > So much correctness in the Rust language just for to promote
| to all the community to crash the program from libraries
| without handling the error is something I can not understand.
|
| _Is_ there that much "promoting" of unchecked
| unwrap()/expect()/etc. going on? How do you distinguish that
| from "genuine" cases of violations of the programmer's
| assumptions?
|
| I ask because Result/? along with libraries like
| thiserror/anyhow/etc. are right there and arguably easier/more
| concise, so unwarranted unwrap()/etc. would seem "harder" to
| write/justify than the alternative. The main exception I can
| think of are more one-off cases where the author is
| intentionally sacrificing robust error handling for the sake of
| speed/convenience, but that's a more language-agnostic thing
| that pretty much "doesn't count" by definition.
|
| > I hope this philosophy do not reach the Linux kernel.
|
| IIRC this is being worked on, especially given Linus's position
| on panics in the kernel.
|
| [0]: https://github.com/dtolnay/no-panic
| drtgh wrote:
| > Is there that much "promoting" of unchecked
| unwrap()/expect()/etc. going on? How do you distinguish that
| from "genuine" cases of violations of the programmer's
| assumptions?
|
| More like promoted indirectly, I think by being used widely
| on reference code and tutorials the programmers absorbs as a
| familiar and quick to write method without planning much. And
| at same time by not being actively promoted that such methods
| should not be used within a library runtime or similar at
| least, because many people do not see it as wrong, what
| convert it in philosophy I guess.
|
| When the dependency chain of library loading is fired, almost
| always I checked some unwrap ends within the program's
| runtime, so distinguishing whether those are genuine cases of
| violations (IMHO they can't be genuine if a lib can panic the
| program), or if it was just a unfinished prototyping part or
| etc, I think is not exactly important as individual until it
| reach terms of generalized behavior along the language
| libraries, and even seen in some programs of the community.
|
| > IIRC this is being worked on, especially given Linus's
| position on panics in the kernel.
|
| These are good news
| aw1621107 wrote:
| > More like promoted indirectly, I think by being used
| widely on reference code and tutorials the programmers
| absorbs as a familiar and quick to write method without
| planning much.
|
| For references/documentation/tutorials I think the use of
| unwrap() and friends is a tradeoff. It (arguably) allows
| for more focused/self-contained examples that better
| showcase a particular aspect, though with the risk that a
| reader uses those examples as-is without taking other
| factors into consideration. There's also the fact that
| documentation examples can be used as tests, in which case
| use of unwrap() in docs/examples/etc. is arguably a _good_
| thing.
|
| > And at same time by not being actively promoted that such
| methods should not be used within a library runtime or
| similar at least, because many people do not see it as
| wrong, what convert it in philosophy I guess.
|
| I think it might depend on where you're looking. For
| example, the Rust book has a section titled "To panic! or
| Not to panic!" [0] which outlines some things to consider
| when deciding whether to panic/call unwrap()/etc. Not sure
| if that counts as active promotion, but the fact it's in
| official docs should count for something at least.
|
| > IMHO they can't be genuine if a lib can panic the program
|
| I feel this is a rather strong position to take. Given how
| panics are intended to be used (handling unexpected
| state/precondition violations/etc.), it seems it seems akin
| to saying "just don't write bugs", which would certainly be
| nice but isn't really realistic for the vast majority of
| development. I suppose one could hypothetically bubble up
| every possible error but that comes with its own
| maintainability/readability/etc. costs.
|
| In addition, that stance seems similar to stating that
| there's no "genuine" assertion failures or similar in
| libraries, which seems... bold? What would the alternative
| be?
|
| > or if it was just a unfinished prototyping part or etc
|
| At least in Rust there's todo!() and unimplemented!() which
| more directly convey meaning.
|
| [0]: https://doc.rust-lang.org/book/ch09-03-to-panic-or-
| not-to-pa...
| burntsushi wrote:
| > it seems it seems akin to saying "just don't write
| bugs"
|
| That is indeed exactly what is being said as far as I can
| tell. And yes, it's exactly as ridiculous as you think it
| is.
|
| I'll link my blog on the topic again because I think it
| might help here as well:
| https://blog.burntsushi.net/unwrap/
|
| `unwrap()` contains an assertion. Just like `slice[i]` or
| even `Box::new(whatever)`. The way to avoid these in C is
| to commit UB instead of panicking. I've seen arguments
| that seem understandable for why this is _maybe_
| appropriate in the Linux kernel ( "I'd rather continue
| executing with garbage than shut down the user's
| system"), but I don't think it applies much beyond that.
| And to be clear, I'm not saying I agree with that either.
| drtgh wrote:
| > Given how panics are intended to be used (handling
| unexpected state/precondition violations/etc.), it seems
| it seems akin to saying "just don't write bugs"
|
| It is more about always making the error reach the
| function that called the method/library (lost if one of
| the own dependencies of this library rise the panic [UB
| included] crashing the program), what allows the
| programmer to take the decision (and not to a deep
| dependency^5 ) about if to continue running the program,
| by taking alternative route, or not.
| burntsushi wrote:
| In other words, "every bug should be an error, and every
| failure should be an error." Except in order to make
| every bug be an error, you have to, well, know about
| every bug. And now all of your implementation details
| leak out into your public API errors.
| drtgh wrote:
| Not exactly. It is more like if a library is gonna crash
| the program (the deep dependencies used in that library
| triggered a _panic_ on its own), just to let the
| programmer know it before happens; thus allowing the
| programmer to try alternatives for to avoid it, and if it
| 's inexorable after those tries, for procedures for a
| controlled shutdown with proper protocols, actions, and
| so on.
|
| I mean, as the crash is not exposed in the public API,
| given the things, I think it might not matter if this
| signal is exposed or not.
| burntsushi wrote:
| How do you let the programmer know? Through the public
| API...
|
| Otherwise you have to catch panics via unwinding.
| drtgh wrote:
| By the public API then, may be a language generalized
| Err(panicked) through Result, nothing more as it is about
| the request cannot be accomplished at all (target/debug
| would tell), or may be someone thought on simpler tip
| through compiler or other thing. I humbly do not know
| what approach would be simpler, efficient or less
| intrusive, I would embrace anything to avoid sudden
| crash.
| burntsushi wrote:
| The juxtaposition between this comment where you seem to
| have absolutely no idea what you're talking about, and
| the certainty with which your initial comment was
| expressed is really quite something.
| burntsushi wrote:
| Eh? There isn't a single unwrap/expect in the examples at the
| top level crate documentation. There should be very few
| overall. But there are hundreds of executable doctests, so
| there are certainly some unwraps.
|
| But I've already opined on this topic:
| https://blog.burntsushi.net/unwrap/
| the_mitsuhiko wrote:
| That is not my experience at all. It's very rare that libraries
| unwrap. Beyond example code and tests I rarely see unwrap.
| bigstrat2003 wrote:
| > I hope this philosophy do not reach the Linux kernel.
|
| Well, I hope it does. Albeit it almost certainly will not,
| because Linus is opposed to it. But ever since I read Joe
| Duffy's blog posts on the Midori research project at MS, I have
| been convinced that using panics leads to _increased_
| reliability, not decreased. From his blog[1]:
|
| "Given that bugs are inherently not recoverable, we made no
| attempt to try. All bugs detected at runtime caused something
| called abandonment, which was Midori's term for something
| otherwise known as "fail-fast"."
|
| And:
|
| "Abandonment, and the degree to which we used it, was in my
| opinion our biggest and most successful bet with the Error
| Model. We found bugs early and often, where they are easiest to
| diagnose and fix."
|
| I think that the Midori team's work shows that a practice of
| "there's a bug, stop everything" leads to _more_ reliable
| software. Sure, there 's an initial period of pain where you're
| fixing a ton of bugs as they cause the software to panic. But
| you reap the rewards of that effort. I don't think Linux will
| ever move towards a model like this, but I think it would be
| beneficial in the end if they did.
|
| 1: https://joeduffyblog.com/2016/02/07/the-error-model/#bugs-
| ar...
| zokier wrote:
| While this does seem to be an improvement in general, I find it
| extremely disappointing that we now got another, greenfield,
| library that ignores leap seconds and continues the propagation
| of UNIXy time. I appreciate that it was at least informed
| decision, and seems to have been tough call to make. So full
| respect to burntsushi nevertheless.
|
| That makes it mostly uninteresting to me; nice api is nice to
| have, but I'd _personally_ appreciate correct results more.
|
| From the wider ecosystem C++ std::chrono seems like the only one
| that shows some promise on this front. Last I checked the
| implementations were not there quite yet though, and the API
| definitely didn't seem all that pleasant. Maybe in couple of
| years we'll see how it'll work out.
|
| Hifitime 4.0 seems like almost the only option at this point, and
| it is in early alpha still.
|
| I recall using astropy at one point just for time calculations,
| but it is quite overkill solution.
|
| The quest for perfect datetime lib (for any language) continues.
| fanf2 wrote:
| Leap seconds are being abolished. The current rotation speed of
| the earth is very close to 24h/day and is changing very slowly,
| so it is not very likely there will be another leap second
| before they are abolished.
| zokier wrote:
| That doesn't change the situation of past leap seconds in any
| way, those still need to be accounted for.
| spenczar5 wrote:
| The nice thing is that you would have a static table of
| leap seconds, and would not need to poll a URL to check for
| new leap second data (as Astropy does, for example, on
| import!).
| weinzierl wrote:
| While leap seconds are planned to be abolished, there is no
| plan to give up the coupling of UTC and the Earth's angle.
|
| Leap seconds are just to be replaced by a yet to be defined
| adjustment, likely _leap minutes_.
|
| If you don't like leap seconds and don't care about a small
| (but increasing) deviation from Earth's angle you can do so
| today: Just use TAI.
| kibwen wrote:
| Burntsushi explains here in great detail the reasoning for not
| supporting leap seconds:
| https://github.com/BurntSushi/jiff/issues/7
| zokier wrote:
| Yes, that was what I was referring to with
|
| > I appreciate that it was at least informed decision, and
| seems to have been tough call to make
|
| but you are right, having that link here helps others.
| burntsushi wrote:
| Can you say why you wouldn't want to use a TAI time zone? Like,
| generate the TZif data for TAI and then just do
| `jiff::tz::TimeZone("TAI", tzif_data)`. Then you'll get leap
| second accurate durations.
|
| Can you also say why you need the precision? Like, what's the
| use case? What happens if your program computes durations that
| are off because of leap seconds?
| spenczar5 wrote:
| I work in astronomy, on detection of asteroids. Catalogs of
| historical asteroid detections may be reported in UTC from
| some observatories for historical reasons.
|
| Finding a trajectory that matches several candidate
| detections is called "linking" and it is very sensitive to
| time. Being off by even one second will result in a predicted
| position which is far off course, and so a candidate asteroid
| detection will not be linked.
|
| Linking is not quite sensitive enough to demand a
| relativistic time scale, but definitely sensitive enough to
| require correct leap seconds.
| burntsushi wrote:
| Right, but I address this in the issue linked elsewhere in
| this thread: https://github.com/BurntSushi/jiff/issues/7
|
| Like yes, scientific applications are a very valid use case
| for this. But scientific applications usually want other
| things not afforded by general purpose datetime libraries,
| like large time ranges and high precision. What I ask in
| that issue, and what I don't understand, is why folks who
| want leap second support aren't happy with using
| specialized libraries for that task, and instead request
| that leap second support be added to general purpose
| datetime libraries.
| spenczar5 wrote:
| Yeah, I use the specialized libraries. But in Python,
| this has been painful: the good astronomy library is
| Astropy's Time, but everyone uses datetime. So if I want
| to use a third library - for my database, or for making
| plots, or whatever - it will use datetime, and now I have
| to think really hard about how to do conversions. You can
| imagine how hard that is to get right!
|
| Since Jiff hopes to be ubiquitous (I think? Seems that
| way) it would be nice if this sort of thing could be
| avoided. Time is such a fundamental in many APIs that
| having one common library is very important.
| burntsushi wrote:
| I think I would rather see this supported by paved path
| conversions to-and-from the specialized library. It's
| very hard to be all things to all people because there
| are irreducible trade-offs. The linked issue does a
| tortured tour through the trade-offs. I found it very
| difficult to wire in leap second support in a way that
| was satisfying. And even if Jiff supported leap seconds,
| that doesn't mean it would be well suited for scientific
| applications. Do you need more precision than
| nanoseconds? Do you need a bigger range than -9999 to
| 9999? If so, those come at the cost of everyone else by
| using bigger representations. They _could_ be opt-in
| crate features, but now we're talking about non-trivial
| additional maintenance/testing burden.
|
| IDK, maybe there is a way to unify everything in a
| satisfying way, but they seem way too different to me.
| zokier wrote:
| Going through TAI is probably the best way for me, I'll have
| to play around with Jiff to see how practical that is. I'm
| glad if there is good support for TAI though!
|
| One random use-case (reminded by other thread on the front-
| page) is that I occasionally have needed to analyze some
| logs, and get some stats from the data. For example having
| logs like "2024-07-24T14:46:53.123456Z Foo id=42 started" and
| "2024-07-24T14:46:54.654321Z Foo id=42 finished", and wanting
| to get some histogram on how long "Foo" took.
|
| Sure, ideally you'd have some explicit metrics/tracing system
| or some other measurements for getting that data, but
| unfortunately in practice that is not always the case and I
| have to make do with what I have.
|
| Or even more simple, I just want to sort bunch of events to
| establish a timeline. UNIX style time handling makes that
| difficult.
|
| NTP adjustments can also cause problems in these sort of
| cases, but at least the systems I work with are usually kept
| in relatively tight sync so the "window of uncertainty" is
| much less than 1s.
| burntsushi wrote:
| I think Jiff should handle that log use case pretty fine?
| That seems pretty standard. Just parse into a
| `jiff::Timestamp` and then you can sort or whatever.
| binarycoffee wrote:
| TBH I consider the C++ `std::chrono` as the worse possible
| design. `tai_clock::now` does not actually take into account
| leap seconds. Unless it does, who knows ("Implementations may
| use a more accurate value of TAI time."). Likewise,
| `tai_clock::from_utc/to_utc` does not correct for leap second.
| It just translates the UTC epoch to the TAI 1958 epoch.
|
| I found Hifitime to be very opinionated and give a false sense
| of security due to its automatic computation of leap seconds
| based on historical tables. Yes, leap seconds are announced
| some ~6 month in advance, but what if you don't update
| regularly the library? Or if you can't because it is deployed
| on an embedded system?
|
| In the end I wrote my own minimalistic TAI timestamp library
| [1] and made the conscious decision to let the user take the
| responsibility to deal with leap seconds in UTC conversion.
|
| [1] https://github.com/asynchronics/tai-time
| magnio wrote:
| I have seen many people downplaying the complexity of a datetime
| library. "Just use UTC/Unix time as an internal representation",
| "just represent duration as nanoseconds", "just use offset
| instead of timezones", and on and on
|
| For anyone having that thought, try reading through the design
| document of Jiff
| (https://github.com/BurntSushi/jiff/blob/master/DESIGN.md),
| which, as all things burntsushi do, is excellent and extensive.
| Another good read is the comparison with (mainly) chrono, the de
| facto standard datetime library in Rust:
| https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
|
| Stuffs like DST arithmetic (that works across ser/de!), roundable
| duration, timezone aware calendar arithmetic, retrospective
| timezone conflict detection (!), etc. all contribute to a making
| the library correct, capable, and pleasant to use. In my
| experience, chrono is a very comprehensive and "correct" library,
| but it is also rigid and not very easy to use.
| TacticalCoder wrote:
| I love burntsushi's ripgrep and certainly use it all the time,
| calling it directly from my beloved Emacs (and I do invoke it
| all the time). If was using ripgrep already years before Debian
| shipped _rg_ natively.
|
| I was also using JodaTime back when some people still though
| Eclipse was better than IntelliJ IDEA.
|
| But there's nothing in that document that contradicts: _" just
| represent duration as nanoseconds"_.
|
| Users needs to see timezones and correct hour depending on DST,
| sure. Programs typically do not. Unless you're working on stuff
| specifically dealing with different timezones, it's usually a
| very safe bet to: _" represent duration as
| milliseconds/nanoseconds"_.
|
| That humans have invented timezones and DST won't change the
| physics of a CPU's internal clock ticking x billion times per
| second.
|
| Just look at, say, the kernel of an OS that didn't crash on
| half the planet a few days ago: there are plenty of timeouts in
| code expressed as milliseconds.
|
| Reading your comment could be misinterpreted as: _" We'll allow
| a 30 seconds cooldown, so let's take the current timezone, add
| 30 seconds to that, save that time as a string with the time 30
| seconds from now, complete with its timezone, DST, 12/24 hours
| representation and while we're at it maybe add exta code logic
| to check if there's going to be a leap second or not to make
| sure we don't wait 29 or 31 seconds, then let the cooldown
| happen at the 'correct' time"_. Or you could, you know, just
| use a freakin' 30 seconds timeout/cooldown expressed in
| milliseconds (without caring about whether a leap second
| happened or not btw because we don't care if it actually
| happens after 29 seconds _as seen by the user_ ).
| throwawaymaths wrote:
| > That humans have invented timezones and DST won't change
| the physics of a CPU's internal clock ticking x billion times
| per second.
|
| Increasingly we are programming in distributed systems. One
| milli or nano on one node is not a milli or nano on another
| node, and _that_ is physics that is more inviolable.
| tracker1 wrote:
| In which case, does being off a few milli actually matter
| that much in any significant number of those distributed
| instances? No precision is exact, so near enough, should
| generally be near enough for most things.
|
| It may depend in some cases, but as soon as you add network
| latency there will be variance regardless of the tool you
| use to correct for variance.
| tijsvd wrote:
| Of course you don't need a calendar library to measure 30
| seconds. That's not the use case.
|
| Try adding one year to a timestamp because you're tracking
| someone's birthday. Or add one week because of running a
| backup schedule.
| burntsushi wrote:
| I'm not sure what the issue is here exactly, but there are
| surely use cases where a `std::time::SystemTime` (which you
| can think of as a Unix timestamp) is plenty sufficient.
| ripgrep, for example, uses `SystemTime`. But it has never
| used a datetime library. Just because Jiff exists doesn't all
| of a sudden mean you can't use `SystemTime`.
|
| But there's a whole world above and beyond timestamps.
| coldtea wrote:
| Unless you're just using time information to implement a
| stopwatch on your program, anything you do with time will
| eventually have to deal with timezones, and DSTs, and leap
| seconds, and tons of other intricasies.
|
| Even something as simple as schedulling a periodic batch
| process.
| wodenokoto wrote:
| Calling sleep for 30 seconds really doesn't have anything to
| do with dates or time of day.
| J_Shelby_J wrote:
| > (that works across ser/de!)
|
| uhg, I can't believe it took me this long to realize why Serde
| crate is named that!
| dist1ll wrote:
| The abbreviation is also used by EE folks, e.g. SerDes [0].
| The capitalization makes it a bit more obvious.
|
| [0] https://en.wikipedia.org/wiki/SerDes
| kibwen wrote:
| Once you've gathered yourself, allow me to blow your mind as
| to where "codec" and "modem" come from. :P
| LaffertyDev wrote:
| Thank you for pointing me towards the design document. Its well
| written and I missed it on my first pass through the
| repository. I genuinely found it answered a lot of my
| questions.
| devman0 wrote:
| Java had a pretty comprehensive rewrite of it's own time
| handling library and it was much needed. Time is hard because
| time zones are not engineering they are political and
| arbitrary.
|
| So yeah keeping things in Unix time is great if all your doing
| is reading back timestamps for when an event occurred, but the
| moment you have to schedule things for humans, everything is on
| fire.
| soperj wrote:
| Didn't they just incorporate JodaTime? I thought the changes
| were even made by the JodaTime developer.
| fridder wrote:
| If someone wants an entertaining and approachable dive into the
| insanity that is datetime, Kip Cole did a great talk at
| ElixirConf in 2022: https://www.youtube.com/watch?v=4VfPvCI901c
| coldtea wrote:
| > _I have seen many people downplaying the complexity of a
| datetime library. "Just use UTC/Unix time as an internal
| representation", "just represent duration as nanoseconds",
| "just use offset instead of timezones", and on and on_
|
| Anyone thinking datetimes are easy, should not be allowed near
| any schedulling or date processing code!
| power78 wrote:
| >I have seen many people downplaying the complexity of a
| datetime library.
|
| Where? Maybe people downplay storing dates but not making a
| library.
| the__alchemist wrote:
| Comparison with Chrono link:
| https://github.com/BurntSushi/jiff/blob/master/COMPARE.md#ch...
|
| This is interesting, in that Rust with Chrono is the best
| datetime experience I've had in any language.
| mrbluecoat wrote:
| bonus: it's pronounced like 'gif' ;)
| demurgos wrote:
| The main issue I have with existing time libraries, in Rust or
| other ecosystems is poor support for leap seconds. This is mostly
| caused by using UNIX timestamps instead of TAI internally, and
| this lib is no different unfortunately. There seems to be some
| way to support it with tzif files, but it does not have first-
| class support.
|
| Here is the relevant Jiff issue with more details:
| https://github.com/BurntSushi/jiff/issues/7
|
| UNIX timestamps don't use the SI second definition (instead it's
| 1/86000th of the current day, all UNIX seconds don't have the
| same duration) which breaks all correct duration computations. I
| understand that the tradeoff was to inherit compat with older
| time tracking methods and enable faster calendar formatting, I
| disagree that it was the right trade-off. UNIX timestamps mix
| representation concerns with data. In my opinion, leap seconds
| should be treated exactly like the 29th of February or time
| zones.
| burntsushi wrote:
| Why do you want it? What's your use case? And why doesn't a
| specialized scientific library like `hifitime` work for your
| use case?
|
| Whenever people talk about leap seconds, it always seems to be
| in some abstract notion. But it's very rare to see folks
| connect them to real world use cases. I get the scientific use
| case, and I feel like that's well served by specialized
| libraries. Do we need anything else? I'm not sure that we do.
| demurgos wrote:
| This is about the "pit of success", being correct and
| predictable by default. A difference of timestamps not
| returning the corresponding elapsed wall-time is _very_
| surprising.
|
| I want to be able to compute durations between timestamps
| stored in the DB, received from API calls or retrieved from
| the system and get the right duration "out of the box".
| Computing these durations lets me apply business logic
| relying on it. A message can be editable for x amount of
| time, a token is valid for y amount of time, a sanction
| expires after z amount of time, etc.
|
| For example, I want to issue some token valid for 60s. What
| should I set the expiry time to? `now + 60s`. Except if `now`
| is 2016-12-31T23:59:30Z, then most libs will return a time
| 61s in the future.
|
| 1 second may not be a big error, but it's still an error and
| depending on context it may be relevant. This is a systematic
| error unrelated to time sync / precision concerns, so it's
| pretty frustrating to see it being so common. It seems though
| that we won't have any new leap seconds in the near future so
| eventually it will just become a curiosity from the past and
| we'll be stuck with a constant offset between UNIX and TAI.
|
| > I feel like that's well served by specialized libraries.
|
| Agreed that you need a specialized lib for this, but my point
| is that _you shouldn't have to_ and the current situation is
| a failure of software engineering. Computing `t2 - t1` in a
| model assuming global synchronized time should not be hard. I
| don't mean it as a personal critique, this is not an easy
| problem solve since UNIX timestamps are baked almost
| everywhere. It's just disappointing that we still have to
| deal with this.
| burntsushi wrote:
| What I'm not clear on though is what the failure mode is in
| your scenario. What happens when it's wrong? Does something
| bad happen? If something is one second longer or shorter
| than what it ought to be on very rare occasions, then what
| does wrong? I would, for example, imagine that the business
| use case of "editable for x amount of time" would be
| perfectly fine with that being plus-or-minus 1 second. It's
| not just about correctness, it's about the error and what
| it means.
|
| A few months ago, Jiff did have leap second support. It
| worked. I know _how_ to do it, but none of the arguments in
| its favor seem to justify its complexity. Especially when
| specialized libraries exist for it. You can 't look at this
| in a vacuum. By make a general purpose datetime library
| more complex, you risk the introduction of new and
| different types of errors by users of the library that
| could be much worse than the errors introduced by missing
| leap second support.
| demurgos wrote:
| > You can't look at this in a vacuum. By make a general
| purpose datetime library more complex, you risk the
| introduction of new and different types of errors by
| users of the library that could be much worse than the
| errors introduced by missing leap second support.
|
| Agreed, I can easily imagine that it could cause
| situation where some numeric value is not interpreted
| correctly and it causes a constant offset of 37 seconds.
| UNIX timestamps are entrenched, so deviating from it
| introduces misuse risks.
|
| Regarding my use-cases, I agree that these ones should
| still work fine. I could also come up with issues where a
| 1s error is more meaningful, but they would be
| artificial. The main problem I can see is using some
| absolute timestamp instead of a more precise timer in a
| higher frequency context.
|
| Overall, it's the general discussion about correctness VS
| "good enough". I consider that the extra complexity in a
| lib is warranted if it means less edge cases.
| burntsushi wrote:
| > Overall, it's the general discussion about correctness
| VS "good enough". I consider that the extra complexity in
| a lib is warranted if it means less edge cases.
|
| Yeah I just tend to have a very expansive view of this
| notion. I _live_ by "all models are wrong, but some are
| useful." A Jiff timestamp is _wrong_. Dead wrong. And
| it's a total lie. Because it is _not_ a precise instant
| in time. It is actually a reference to a _range_ of time
| covered by 1,000 picoseconds. So when someone tells me,
| "but it's not correct,"[1] this doesn't actually have a
| compelling effect on me. Because from where I'm standing,
| _everything_ is incorrect. Most of the time, it 's not
| about a binary correct-or-incorrect, but a tolerance of
| thresholds. And that is a much more nuanced thing!
|
| [1]: I try hard not to be a pedant. Context is everything
| and sometimes it's very clear what message is being
| communicated. But in this context, the actual
| ramifications of incorrectness really matters here,
| because it gets to the heart of whether they should be
| supported or not.
| egnehots wrote:
| I think that you have made the right decision.
|
| One of the most common and high-impact failure mode is
| crashing when parsing a leap second that is represented
| as "23:59:60".
|
| Jiff is able to parse leap seconds, and beyond that, I
| doubt that there are many scenarios where you care about
| leap seconds but not enough to use a library that
| supports them.
| LegionMammal978 wrote:
| It's not really in Rust (at least, not unless I choose to
| rewrite it in WASM), but there is one case where a lack of
| leap-second support has really been a thorn in my side. I've
| been working on and off on a JS web application that performs
| certain high-precision astronomical calculations, and leap
| seconds are a big challenge w.r.t. future correctness.
| Ephemerides don't change very much, but leap seconds can.
|
| I can embed a big list into my application, but then I have
| to remember to periodically update it until 2035, or maybe
| longer if they decide to keep leap seconds after all. So I
| can try to download a leap-seconds.list from somewhere,
| except that only a few sources set "Access-Control-Allow-
| Origin: *", and none promise to keep it set indefinitely.
|
| The annoying part is, applications on both Windows (since 10)
| and Unix-like systems generally have access to up-to-date
| leap-second information; at worst, browsers can be expected
| to be updated regularly. But since the JS standards devs want
| to stick strictly to POSIX time, they provide no means to
| obtain this information from the inside, through Date,
| Intl.Locale, or the proposed Temporal.
|
| This is all to say, I would really love to always have some
| way available to get "all leap-second info that the current
| system knows about, if it does have any such info". For its
| part, hifitime either downloads from a fixed URL (which
| doesn't have that Access-Control-Allow-Origin header IIRC),
| consults a fixed list, or gets the user to locate the info,
| which isn't nearly as general or foolproof as it could be.
| burntsushi wrote:
| Yeah the way Jiff's leap second support worked (before I
| ripped it out) is that it would just read from
| `/usr/share/zoneinfo/leap-seconds.list` (or
| `/usr/share/zoneinfo/leapseconds`, whichever was available)
| and use that with some rudimentary caching. That way, Jiff
| isn't responsible for keeping the list up-to-date. The
| sysadmin is. Just like what we do for tzdb.
| LegionMammal978 wrote:
| Indeed. The problem is, in the absence of one good
| solution like that, there are no good solutions, as is
| the case with web JS. Currently (on the desktop or
| laptop), the best bet is hifitime's leap-seconds parser,
| but then the programmer is still responsible for coming
| up with a sane fallback path for all their target
| systems. (Which can be tricky for individuals to do,
| e.g., I have no way to tell whether the tz database is in
| an expected location on macOS or iOS.)
| airstrike wrote:
| _> Jiff is pronounced like "gif" with a soft "g," as in "gem."_
|
| Over my dead body!
| faitswulff wrote:
| Well, it had a good run at the top of hackernews for a minute,
| but I think we can all see its glaring flaws now. I'll stick
| with chrono +
|
| + pronounced "ch" as in "chase" of course, i.e. "trono"
| JohnTHaller wrote:
| You pronounce it GIF instead of GIF?!?
| coldtea wrote:
| https://x.com/drgonzo123/status/527520297512345600
| aceazzameen wrote:
| Huh, I thought Jiff was pronounced with a hard "J."
| jcgrillo wrote:
| What does it mean for a Span to be negative? This is one thing I
| really like about Durations, they can't be negative and therefore
| match physical reality.
| burntsushi wrote:
| ISO 8601-2:2019 defines it as: negative
| duration: duration in the reverse direction to the
| proceeding time scale
|
| Matching "physical reality" is a non-goal. What's important is
| modeling the problem domain in a way that makes sense to
| programmers and helps them avoid mistakes. A negative span
| doesn't give you any extra expressivity that "subtract a span
| from a datetime" doesn't already give you. Both are a means to
| go backwards in time. And they line up with human concepts that
| talk about the past. So for example, if I say, "I went camping
| 1 year ago." I am expressing a concept that is _modeled_ by a
| negative span.
|
| And there are also just practical benefits to allowing a span
| to be signed. For example: use jiff::{ToSpan,
| Unit, Zoned}; fn main() -> anyhow::Result<()> {
| let now = Zoned::now().intz("Europe/Kyiv")?; let
| next_year = now.checked_add(1.year())?; let span =
| now.since((Unit::Month, &next_year))?;
| println!("{span}"); Ok(()) }
|
| Has this output: $ cargo -q r -P12m
|
| If negative spans weren't supported, what would the behavior of
| this routine be? It could return an error. Or maybe an out-of-
| band sign. I'm not sure. But this seems like the most sensible
| thing.
|
| And of course, Temporal has negative durations too. This design
| was copied from them.
| jcgrillo wrote:
| Thanks for the explanation. I agree signed spans make it
| easier to express concepts like "1 year ago" vs "1 year from
| now". And clearly if the concept of a negative duration has
| made it into the standard then it makes sense to support it.
| But I do wonder if there's some value still in being precise,
| if I _think_ I 've measured a negative duration--e.g I look
| at my watch and write down t0, wait a bit, look at my watch
| again and write down t1 and find t0 > t1--that's something
| surprising that probably warrants further investigation. The
| explanation likely isn't "well time just went backwards for a
| bit there" :P. This happens frequently in computers, though,
| so maybe making the programmer handle an error each time is
| excessive.
| burntsushi wrote:
| Yeah it's hard for me to get a sense of where that would
| really matter in practice, or even when it would be correct
| to do so. You could a `assert!(!span.is_negative());`.
| Although, you have to be careful, because system time can
| change arbitrarily.
|
| Your watch example is actually an important use case that
| specifically _isn 't_ served by Jiff. You should be using
| monotonic time (in Rust, that's `std::time::Instant`) for
| things like that. In that case, you really do get a
| guarantee that the duration between two instants (one in
| the past and one in the future) is non-negative. And if it
| weren't, then that would be worthy of an assert. But you
| get no such guarantees with system time.
| jcgrillo wrote:
| Upon thinking about it a little more, I actually have a
| stronger opinion that it's probably not ideal to try to
| assign direction to durations themselves, and even then
| making them signed wouldn't be the best way. A duration is a
| scalar length measurement in the time axis. Those things are
| always positive, at least in our universe. I don't think we
| gain much by pretending otherwise, and I think we lose
| clarity.
|
| Expressing a before/after relationship requires that we
| establish what is the anchor point. Is it the present time
| experienced in my reference frame? Is it unix epoch? Using a
| negative number to denote a disembodied "duration before"
| leaves me guessing about the anchor. In the case when we want
| to express the timestamp "3 days before/after time t0" I
| think it's probably best to say something like:
| t0.before(3.days()) // -> Timestamp t0.after(3.days())
|
| This isn't a criticism of your library, it looks awesome and
| I'll definitely use it. I am fairly surprised that negative
| duration definition made it into the standard though.
| burntsushi wrote:
| There are `checked_add` and `checked_sub`. But that shifts
| the "direction" of the span to the operation, which is a
| static property of your program, instead of being part of
| the value itself, which is a dynamic property of your
| program. I think this results in more annoying code where
| you just wind up carrying a sign around anyway.
|
| There are also negative years, even though that doesn't
| really correspond to anything "real" either.
|
| Here's the thread about signed durations for Temporal:
| https://github.com/tc39/proposal-temporal/issues/558
|
| Apparently signed durations are a common feature in
| virtually every datetime library.
| andrewfromx wrote:
| Wow! What timing. I need this.
| https://github.com/andrewarrow/toggl I'm doing UTC conversion in
| my head.
| pkulak wrote:
| Thank you! Dealing with time, calendars and durations has always
| been the hardest part of Rust for me. And yes, I'm including the
| borrow checker and async!
|
| It's so painful to come from something like the JVM where time
| operations and formed in near natural language. From a quick
| glance, this looks even better than java.time.
| hardwaresofton wrote:
| "babe wake up, the new burntsushi just dropped"[0]
|
| On a serious note, any rustaceans in here know the reason the
| crate doesn't use something like `tracing`? A bit too heavyweight
| maybe -- it is about half the size on crates?
|
| `log` is of course fine, and I don't know that tracing down to
| the calls for tz operations is a normal use case, but always
| interested to know if there was a specific why here.
|
| [0] https://knowyourmeme.com/memes/wake-up-babe
| burntsushi wrote:
| I think `log` is the lower common denominator, right? And
| `tracing` interops with it just fine? That's why I used it in
| Jiff I guess. And it's also been the thing I've been using
| since the birth of crates.io itself. Jiff doesn't have any
| requirements other than emitting messages at a specific log
| level.
|
| But no, I'm sure `tracing` would have worked fine too.
| durandal1 wrote:
| I really think Rust should adopt one of the popular datetime
| crates into the std lib - it's not reasonable to offload
| researching maintainer status and quality to each developer
| needing to use dates correctly.
| bobbylarrybobby wrote:
| Rust's stdlib is intentionally minuscule so that breaking
| changes can be made to important crates. Once a crate becomes
| part of Rust, no breaking change can ever be made to it, which
| is a pretty high risk situation (if chrono had been added, jiff
| would not be able to). Better would be for Rust to simply fund
| development of critical crates and let the marketplace work out
| which crates those are.
| p0w3n3d wrote:
| Name is pretentious. Jiff? Seriously? How do you read it?
| "Gif"????
___________________________________________________________________
(page generated 2024-07-22 23:11 UTC)