-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Support :week or :day units in Date.add/3 #10199
Conversation
This adds `Date.add/3`, which accepts an optional unit value. The only acceptable units are `:day` and `:week` and it defaults to `:day`.
@@ -589,6 +594,8 @@ defmodule Date do | |||
The days are counted as Gregorian days. The date is returned in the same | |||
calendar as it was given in. | |||
|
|||
Alternate units such as `:week` may only be used with `Calendar.ISO` dates. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only Calendar.ISO? We are heading down a path in which a generalised Calendar behaviour is no longer generalised but specialised. In thats the intention, then lets remove the behaviour and just make the Date/Time/DateTIme modules only for Calendar.ISO. There has been previous conversations around adding the concept of weeks to Calendar behaviour with the conclusion that this was not easily generalised. If weeks are now back on the drawing board should we not make weeks a full partner in the Calendar behaviour and solve the problem generally?
As @kipcole9 said, we cannot add @kipcole9 I think the temptation to add Calendar.ISO specific behaviour is natural, we may need to help to keep us honest, so your input is appreciated. :) |
Thanks for chiming in on this @kipcole9. Honestly the |
Wait a second. All of the @sorentwo can you please bring your branch back so we can merge it? |
Well, here is another concern though. Doing: time
|> Time.add(1, :hour)
|> Time.add(30, :minute) is not going to be efficient because we will need to convert these representations back and forth multiple times. Perhaps it is best to add a
Or similar that can do all operations at once. We can delegate the interpretation of each key-value pair to the underlying calendar. We keep the The only remaining question is: if we do have |
Are there calendars where 1 day != 24 hours? I think adding :day but not adding :week is a good start, we can always add the latter later. If 1 day is always 24 hours, then I think the logic is not ISO specific. I also think it's a great point about |
Ok, I've restored the branch while we continue the discussion. I really like the readability of If we add |
The Hebrew calendar has a different notion of time although as I understand it civil time is most commonly used. From wikipedia:
I am building a Mars calendar in the background, its day is not 24 hours (but I'm not for a moment suggestion non-earth calendars are in scope). |
If the approach of |
@kipcole9 re: Mars, :D (I think we should support it!) Perhaps whether 1 day is or is not 24h is moot because we have |
Good point about formalising the idea of duration, I always thought we'd eventually have something like this: def shift(time, %Duration{} = duration) do
...
end
def shift(time, duration) when is_list(duration) do
shift(time, struct!(Duration, duration))
end |
The biggest problem with periods/durations is on how we want to handle multiple calendars and timezones. Adding 1 day + 12 hours is not the same as adding 36 hours if there is daylight saving time. Java solves this problem by having two classes: periods and durations. One is only time aware, so 1 day is always 24 hours, while the other is date-aware and respects timezones. So when talking about periods/durations, we need to be clear about which approach we are advocating for. In Java, both accept only ISO units, even though there is support for multiple calendars. Implementation-wise, going with the time-based variant is much simpler. The internal representation is also straight-forward, it could be as simple as a tuple with @kipcole9 is it correct to say that a duration in a calendar always have an equivalent duration in another calendar? Because if so, supporting multiple calendars for a time-based duration is always a matter of simply doing this: Time.add(time, Mars.Duration.to_iso_seconds(days: 1, hours: 3)) In any case, it seems that the best option is to not add these concerns to For now, I will revert the previous addition and we can explore how to add time-based durations next. |
Hello, I released Tox yesterday. The library deals with the topic of this issue. I have seen this topic before I release Tox, but at this time, it was closed. Maybe Tox can be used as an example of what we want and what we don't want. To simplify things, the library assumes that every week consists of 7 days. I think that fits for many calendars. But it would be nice to make weeks a full partner in the Calendar behaviour and solve the problem generally, as @kipcole9 mentioned above. Tox knows durations and periods. Duration is specified as a keyword list like Tox defines an iex> Tox.Date.add(~D[2020-01-31], month: 1)
~D[2020-02-29] In other calendars, this can result in very different time differences. iex> date = Date.convert!(~D[2020-09-05], Cldr.Calendar.Coptic)
~D[1736-12-30 Cldr.Calendar.Coptic]
iex> Tox.Date.add(date, month: 1)
~D[1736-13-05 Cldr.Calendar.Coptic]
iex> Tox.Date.add(date, month: 2)
~D[1737-01-30 Cldr.Calendar.Coptic]
iex> Tox.Date.add(date, month: 1) |> Date.diff(date)
5
iex> Tox.Date.add(date, month: 2) |> Date.diff(date)
35
iex> date |> Tox.Date.add(month: 1) |> Tox.Date.add(month: 1) |> Date.diff(date)
10 Durations will always be added from the largest to the smallest unit. iex> datetime = DateTime.from_naive!(~N[2000-01-30 00:00:00], "Europe/Oslo")
iex> Tox.DateTime.add(datetime, month: 1, day: 1)
#DateTime<2000-03-01 00:00:00+01:00 CET Europe/Oslo>
iex> datetime |> Tox.DateTime.add(month: 1) |> Tox.DateTime.add(day: 1)
#DateTime<2000-03-01 00:00:00+01:00 CET Europe/Oslo>
iex> datetime |> Tox.DateTime.add(day: 1) |> Tox.DateTime.add(month: 1)
#DateTime<2000-02-29 00:00:00+01:00 CET Europe/Oslo> |
This is exactly why I like @wojtekmach's calendar_interval and the underlying concepts.
Those simply are not the same operations, but people like to think they're the same. |
Maybe it's just me, but if I'm adding "1 day" to a DateTime, my gut instinct is that it's 24 hours. If the actual DateTime produced is +/-1 hour from the previous DateTime (due to stoopid Daylight Savings) I don't care too much. As a dev, I'm always working in UTC anyway, what "DateTime" it really is, is UI dressing. Almost all of my timers end up looking like "24 * 60 * 60 , :second" and I would way rather be able to do "1, :day" or "3, :hour" when I'm |
It's nice if you can treat datetimes like you described, but this doesn't work if datetimes are meant for humans in places and not for computers. Sometimes adding "1 day" might mean "send me this notification again tomorrow" a.k.a. next day, same (wall-)time as today in the users timezone. There's a difference between adding a fixed chunk of time (DateTime.diff always the same) and doing something like my example, where the actual time difference is irrelevant, but consistent wall time is expected. I feel |
If you're talking about human stuff, IMHO, that's not a standardizable kind of thing, whereas 1 week = 7 days, 1 day = 24 hours, 1 hour = 60 minutes, 1 minute = 60 seconds -- those are pretty constant. There IS precedent for this in legacy languages and libraries -- for example, how do SQL databases do it? it's not standard SQL, but the feature seems pretty standard -- MySQL, for example: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_timestampadd supports years, weeks, days, hours, minutes, even "quarters" (and I'm not entirely sure how you compute that last one). I guess when I think about adding time, I look to the consistent legacy of time-adding functions and libraries that came before. 😄 |
This adds
Date.add/3
, which accepts an optional unit value. The only acceptable units are:day
and:week
and it defaults to:day
.Unlike the recent changes to
Time
, this doesn't changeDate.diff/2
because it doesn't currently accept any units.