Skip to content
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

fix use of span to get number of days #29

Merged
merged 1 commit into from
Sep 1, 2024

Conversation

BurntSushi
Copy link
Contributor

This commit revises the change in #28 to be a bit more robust.

Originally, before #28, the code was buggy because, I think, Span was
being used like a normal "absolute" duration. But a Span keeps track
of values for each individual unit. It isn't just a single number of
nanoseconds like, e.g., std::time::Duration is. It's "smarter" than
that. It deals with non-uniform units like days, months and years. But
to do it correctly, you need a reference date.

What this means is that when you get a Span by subtracting two Zoned
values, you can't just ask the Span for the total number of days via
get_days(). It has to be computed. In #28, this was done for one
case but not the other via the Span::total API. While this works
today, the code was not providing a reference date, which means days are
silently treated as always being 24 hours long.
See BurntSushi/jiff#48 for more details where
it's likely that this sort of usage will return an error in jiff 0.2.

The main gotcha here is that since this is using gix::date, the
Zoned values that are created are just "fixed offset" datetimes. They
don't actually have a time zone. So in practice, such datetimes will
always have all days be 24 hours long. This is not correct, but it's not
clear to me that this is fixable inside the context of git. But at
least with this patch, if you do ever end up using true time zones, then
this code will be robust to that.

This commit revises the change in Byron#28 to be a bit more robust.

Originally, before Byron#28, the code was buggy because, I think, `Span` was
being used like a normal "absolute" duration. But a `Span` keeps track
of values for each individual unit. It isn't just a single number of
nanoseconds like, e.g., `std::time::Duration` is. It's "smarter" than
that. It deals with non-uniform units like days, months and years. But
to do it correctly, you need a reference date.

What this means is that when you get a `Span` by subtracting two `Zoned`
values, you can't just ask the `Span` for the total number of days via
`get_days()`. It has to be *computed*. In Byron#28, this was done for one
case but not the other via the `Span::total` API. While this works
today, the code was not providing a reference date, which means days are
silently treated as always being 24 hours long.
See BurntSushi/jiff#48 for more details where
it's likely that this sort of usage will return an error in `jiff 0.2`.

The main gotcha here is that since this is using `gix::date`, the
`Zoned` values that are created are just "fixed offset" datetimes. They
don't actually have a time zone. So in practice, such datetimes will
always have all days be 24 hours long. This is not correct, but it's not
clear to me that this is fixable inside the context of `git`. But at
least with this patch, if you do ever end up using true time zones, then
this code will be robust to that.
@BurntSushi
Copy link
Contributor Author

Copy link
Owner

@Byron Byron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am incredibly surprised and just as grateful that you saw my 'fix' on top of the previous 'hack', and feel honoured to have your correction in the history of cargo-smart-release 😊🙇‍♂️.

Also thanks for the additional explanation here, I spent some time understanding it and hopefully making the difference sink in. Admittedly, during my initial conversion from time to jiff I just tried to make it compile and didn't spend any time trying to understand the underlying concepts. Had I read the excellent docs of get_days() I would probably have seen the warning that it's not what you think it is :D.

And even from there, all I did was to try and find alternatives that seemed to work. Never would I have been able to arrive at the solution presented here.
When looking further into it, I noticed that Zoned::sub(Zoned) also returns a Span, but the difference seems to be that the smallest and largest unit probably default to hours. Then, when being interested in days, it probably yields incorrect results.

I hope that in future, I will be able to use jiffs excellent (and incredibly well documented) API to handle time correctly, and that should be facilitated by not coming from time but start from scratch.

Thanks again 🙏.

@Byron Byron merged commit 557f555 into Byron:main Sep 1, 2024
11 checks passed
@BurntSushi BurntSushi deleted the ag/fix-day-span branch September 1, 2024 11:15
@BurntSushi
Copy link
Contributor Author

Thanks for the kind words. :-)

Never would I have been able to arrive at the solution presented here.

Ug, this is bad! I guess once span.total(Unit::Day) returns an error in the future (because there is no reference date to interpret the length of a day), then that would have pushed you away from that at least. But yeah, my hope is here is that the API design should lead you to something that is correct. I think part of the problem here is that folks aren't used to the idea of a Span yet. They're used to things like std::time::Duration and apply patterns learned from using std::time::Duration to Span, and this ends up being wrong in a lot of cases.

Popping up a level, I spent some time last night thinking about git and its use of offset datetimes everywhere instead of time zones. It is an interesting case where perhaps time zones aren't the right thing! Consider what happens if one commit has a datetime recorded at 2024-03-08T05:00:00-05[America/New_York] and a subsequent commit has a datetime recorded at 2024-03-12T15:30+05:30[Asia/Kolkata]. And now you want to find the number of days between them. Well, you have a problem! Because 2024-03-10 in America/New_York is only 23 hours long. But In Asia/Kolkata, that day was 24 hours long. Indeed, Asia/Kolkata has no DST at all. So the question is itself ill-defined in cases like this.

@Byron
Copy link
Owner

Byron commented Sep 1, 2024

Ug, this is bad!

I think it's entirely unrelated to the quality of the documentation though, and agree that it's mostly an issue of habit and 'most developers have no clue about time'. In my case, it was a zero-thought-quick-and-dirty-conversion where compilation alone meant success.
I truly think that jiff does everything right and it does a great job in education as well. It just takes…erm…time, and you involving yourself like this is outstanding and amazing. One couldn't wish for more, really 🙇‍♂️.

Edit: I think what I wouldn't have done is to do the rounding, smallest and largest, that's entirely new concepts to me, strangely enough.

Popping up a level, I spent some time last night thinking about git and its use of offset datetimes everywhere instead of time zones. It is an interesting case where perhaps time zones aren't the right thing! Consider what happens if one commit has a datetime recorded at 2024-03-08T05:00:00-05[America/New_York] and a subsequent commit has a datetime recorded at 2024-03-12T15:30+05:30[Asia/Kolkata]. And now you want to find the number of days between them. Well, you have a problem! Because 2024-03-10 in America/New_York is only 23 hours long. But In Asia/Kolkata, that day was 24 hours long. Indeed, Asia/Kolkata has no DST at all. So the question is itself ill-defined in cases like this.

I suppose, one would have to choose one of the two dates, transform it to the other, and then check the amount of days that passed between the timezone-normalized dates accordingly?
It seems the same happens implicitly when using offset-datetime - when calculating passed time, all one does is to subtract UTC times which at least intuitively seems correct to me. However, 'most developers have no clue about time', and I am one of them, still :D. Now that I use jiff though, I am sure I will get it eventually, and that's a wonderful thought to entertain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants