-
Notifications
You must be signed in to change notification settings - Fork 103
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
Adding LocalDateRange #190
Comments
Is it correct to say that the use case is that the API you're working with requires querying data date by date with no option to request the data for a wider range? |
That would be correct. |
I'm doing this by adding some extensions for |
I've browsed through GitHub to see how people are using
|
Related to the choice of arbitrary I'm not hugely concerned about the first case. A range that goes backward temporarily, but forward ultimately, isn't wrong so much as it is perhaps unexpected. Given that The second case is more interesting. It did occur to me that there could be I think ultimately restricting As a matter of syntax, for |
Thinking about this more, there is an issue that persists in any choice that allows for a step in months (as opposed to just days, or just years*): If one were to create the following date progression: The latter is the current behavior with simple The more I think about it, the more I believe that any progression that allows months inherently comes with caveats. Even if we restrict the steps to only positive months, we would still have caveats like the one above. It may make sense to trend toward maximum freedom, and just put strong warnings in the docs about known quirks. *technically this would also occur if one tried to iterate, by year, starting on the 29th of February, as one encountered non-leap years, but that's an edge case of an edge case |
It is okay to abandon the infix notation: it's consistent with things like
That's a great point. Actually, we can implement this in a way that the last day of the month is always returned (treat this as pseudocode, I neither compiled it nor checked it for edge cases): sequence {
var i = 0
while (true) {
val nextDate = startDate.plus(i, stepUnit)
if (nextDate > endDate) break
yield(nextDate)
++i
}
} We only have to think carefully about what's less unintuitive / more likely to be useful. |
I think we can distill this problem down to the following question: Which of the following accurately describes the next date in a progression?:
The end-of-month edge case is one we've already uncovered where the distinction is meaningful. Are there any others? |
|
Addition on Multiplication could be defined largely the same way: public operator fun DatePeriod.times(other: Int): DatePeriod = DatePeriod(
safeMultiply(totalMonths, other),
safeMultiply(days, other)
) I think this is ultimately a question of user expectation. Which of the following is more intuitive to a user and less likely to cause confusion: range[N] = range[N-1] + step |
Oh, good point, filed an issue: #381 |
I've made some time to research this a bit more. It looks like people don't actually want iteration steps other than day-based, so it's a waste of time to get stuck on how months behave. Here's the document detailing the findings and the proposed API: https://gist.github.com/dkhalanskyjb/3758db55f37cfdedc7901151f8838384 During an internal discussion with the team, everyone agreed to sticking to @PeterAttardo, are you interested in adapting your pull request to match this API, or would you like us to implement it? |
I'll take a crack at it. I should have time starting tomorrow, so I'll take a deeper look at it then. |
After reading through the linked gist: I think being able to control the size of the step is a hard requirement, and as long as the steps are in days, it should be fairly straightforward to accomplish. I can think of a few ways to accomplish it:
Do we have a preference for which of the options I should go with? |
Second question: public fun LocalDateProgression.first(): LocalDate
public fun LocalDateProgression.firstOrNull(): LocalDate?
public fun LocalDateProgression.last(): LocalDate
public fun LocalDateProgression.lastOrNull(): LocalDate?
public fun LocalDateProgression.reversed(): LocalDateProgression These five functions from the spec are given to us for free* because * |
Yes, please see https://gist.github.com/dkhalanskyjb/3758db55f37cfdedc7901151f8838384#api-shape : // in any case, we'll need these overloads
public infix fun LocalDateProgression.step(
value: Long,
unit: DateTimeUnit.DayBased,
): LocalDateProgression
public infix fun LocalDateProgression.step(
value: Int,
unit: DateTimeUnit.DayBased,
): LocalDateProgression Consistency with
We want to re-implement them, as is done for the other ranges: see https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/ for a list. By clicking |
I have updated the pull request. Two things I figured were of note:
|
Per request I am opening an issue to explain #189
My use case comes from my recent exploration of the new Kotlin Spark API. In data engineering, a lot of batch jobs are parameterized with
startDate
andendDate
. Processing the latest date of data in a daily task runner (such as Airflow) is done by feeding the same date as bothstartDate
andendDate
; processing a backfill is done by feeding an appropriate range to (re)process.For some jobs it is necessary to iterate over every date in the range. This could be required if the relevant data files are organized by date in the S3/HDFS/etc path (
s3://some-bucket/data_type/{date}/[00..n].parquet
), or if the job calls a process that is parameterized by a single date, or another reason.As mentioned, there is a similar request for Instant Interval #34. I contemplated adding something similar in my pull request (specifically I considered doing for
LocalDateTime
what I did forLocalDate
), but decided against it for the following reasons:Integer
s,Long
s, andChar
s all have a natural value for that step, because they are discrete.LocalDate
is likewise discrete, and therefore a progression with a step size of one day is a natural fit.LocalDateTime
is not discrete, and therefore there is no natural step size. Should(dateTime1..dateTime2).forEach { ... }
run for every second between the twoLocalDateTime
s? Every hour? Every nanosecond?LocalDateTime
, there is still a valid use case for the general range semantics. However, most of that use case is already covered by the genericClosedRange<T : [Comparable]<T>>
that Kotlin gives you out of the box for anyComparable
.(dateTime1..dateTime2).contains(dateTime3)
is already provided free of charge.The third reason does not apply to
Instant
, but the first two reasons do. If others disagree that they are sufficient to preclude a use case for anInstantRange
that applies Kotlin's progression semantics, I can add it to my pull request. But for now I omitted it.The text was updated successfully, but these errors were encountered: