-
Notifications
You must be signed in to change notification settings - Fork 110
Allow getting the week number of a LocalDate and a LocalDateTime #129
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
Comments
Hi @LouisCAD! Sure, we could add such functionality, but could you please describe why you need it? |
Hello @dkhalanskyjb! I guess it'd also be useful for other use cases since a bunch of corporations use week numbers as a reference. |
https://en.wikipedia.org/wiki/Week#Week_numbering
The ISO is probably good compromise, yet the usage / usefulness is quite questionable then. |
For reference, moment.js has both locale dependent and ISO week number: https://momentjs.com/docs/#/get-set/week/ |
This feature would be a good addition. I would be very happy to work on this feature request. |
It's still not clear what exactly people do with the week numbers. Let's say they got the week number of a I'm asking this because use cases dictate the way we implement this. For example, to tell which week it is, we need to know on which day a week starts (in some countries it's Monday, in some it's Sunday). Should we add the ability to configure this? It depends entirely on the use cases that we aim to support. If we add a configuration option "just in case" and then nobody passes anything other than Also, about a symmetrical operation: do people also need to be able to construct a The bottom line: please feel free to share your use cases, we'll be glad to discuss them even if you feel they are pretty obscure. |
One use case that I can think of is check if two dates belong to same week, to show a header or something like "This week". Although I guess this can be achieved by floorDiv of dayOfYear by 7. |
This can vary from locale to locale, as in some countries, last Sunday is in the same week, and in some, it's not. "This week" would be difficult to implement reliably even if we did provide such a function.
A year doesn't always start on Mondays, so I don't think this would work. |
It seems like the whole week number scenario will be implemented correctly only after taking the whole calendar into consideration. Although there are not many use cases I can think of. 🤔 |
I agree, week comparison is something I'd want to do, though there are other ways to do it, without the week number, but simply checking the day in the week, and counting how many days two dates are apart. |
I'd also like to have |
@carlosefonseca Thanks for the use case! I don't completely get it though, could you clarify it? Where would the week number be displayed? Next to the list of the days of the week? If so, then you need to get the week numbers in a batch, not for just a couple of dates, and wouldn't a function that returned the week number on the given date be cumbersome to use in such a case? Isn't it a better solution for your case to just iterate once over the Mondays (or Sundays, it's up to you!) of the year, assigning to each of them their week number, counting up from 1? |
This is very much useful for any calendar-style apps. |
For now, the only clear choice is to implement the ISO-8601 numbering, which defines the weeks to start on Mondays, and, in case a week is shared between two years, to handle the split week by assigning it to the year that has the majority of days in it (at least 4): https://en.wikipedia.org/wiki/Week#Determining_Week_1
Constructing a |
Yes (via a user controllable setting).
We follow ISO 8601.
I'll give it a try, thanks for the hint! |
Let's go back to the use case of checking if two dates belong to the same week. How would one implement that, if we did provide the week numbering? // wrong attempt 1
fun LocalDate.sameWeekAs(other: LocalDate) =
weekNumber(weekStart = DayOfWeek.MONDAY) ==
other.weekNumber(weekStart = DayOfWeek.MONDAY) Obviously, this won't work if the dates are from different years. Jan 1st, 2007 is not the same week as Jan 1st, 2023. // wrong attempt 2
fun LocalDate.sameWeekAs(other: LocalDate) =
year == other.year &&
weekNumber(weekStart = DayOfWeek.MONDAY) ==
other.weekNumber(weekStart = DayOfWeek.MONDAY) This is much less obviously wrong, but still wrong since some weeks are shared by two years. // wrong attempt 3
fun LocalDate.sameWeekAs(other: LocalDate) =
monthsUntil(other).absoluteValue < 12 &&
weekNumber(weekStart = DayOfWeek.MONDAY) ==
other.weekNumber(weekStart = DayOfWeek.MONDAY) Now, this will almost always work, but not always! For example, Jan 1st, 2007, is week 1, but Dec 31st, 2007, is also week 1. The reason is that, by the ISO-8601 numbering, if a week is split between two years, it is assigned a number in terms of the year that has the bigger part of the week. I don't see a simple way to check if two dates are in the same week using a week numbering. Here's a working approach though: fun LocalDate.sameWeekAs(other: LocalDate, firstDayOfWeek: DayOfWeek): Boolean {
fun LocalDate.firstDayInWeek(): LocalDate =
minus((dayOfWeek.isoDayNumber - firstDayOfWeek.isoDayNumber).mod(7), DateTimeUnit.DAY)
return firstDayInWeek() == other.firstDayInWeek()
} Maybe this is the approach we should be expanding, especially if operations like "find next Monday" or "find last day before this one that was in January" have some other uses. |
If there is enough demand specifically for implementing the ISO-8601 week numbering for interoperability with other systems, maybe a better choice would be to introduce a separate data structure, called something like This way, those that want to know the ISO-8601 week number for some data processing will be able to do so (and with the week-year to match it, without which the week number is difficult to interpret, as shown above), but accessing it will also be difficult enough that people won't be encouraged to try to use it in day-to-day scenarios, where this would lead to counterintuitive results. |
Leaving this here as a workaround for some specific use-cases: import kotlinx.datetime.LocalDate
import kotlinx.datetime.toKotlinLocalDate
import java.time.format.DateTimeFormatter
import java.time.LocalDate as jtLocalDate
fun getFirstDayOfWeek(year: Int, week: Int): LocalDate {
val isoString = "$year-W${week.toString().padStart(2, '0')}-1"
val date = jtLocalDate.parse(isoString, DateTimeFormatter.ISO_WEEK_DATE)
return date.toKotlinLocalDate()
} The This is for a toy project, so performance is not a consideration for me, but it's probably not great. |
I would have another use-case. We have releases every 4 weeks and the name of the release is constructed as Currently, we have a java solution using |
@ferinagy, if you publish a release on the last week of December this year, your release will be called |
What you can do in kotlinx-datetime for your use case instead is val currentDate = LocalDate(2024, 2, 14)
val firstDayOfWeek = DayOfWeek.MONDAY // maybe SUNDAY, up to you
fun LocalDate.firstDayInWeek(): LocalDate =
minus((dayOfWeek.isoDayNumber - firstDayOfWeek.isoDayNumber).mod(7), DateTimeUnit.DAY)
val startOfWeekWithJan1st = LocalDate(currentDate.year, 1, 1).firstDayInWeek()
val currentWeekNumber = startOfWeekWithJan1st.until(currentDate, DateTimeUnit.WEEK) + 1 Essentially, this code calculates how many weeks have passed since the week of Jan 1st. |
Yes, the
Yes, but it's not the ISO numbering. Maybe I should be clearer on that - we already use ISO week numbers and would like to convert it to kotlin multiplatform, not come up with a new numbering scheme. But based on your suggestion, I tried to replicate the ISO week calculation: private fun LocalDate.isoWeekNumber(): Int {
if (firstWeekInYearStart(year + 1) < this) return 1
val currentYearStart = firstWeekInYearStart(year)
val start = if (this < currentYearStart) firstWeekInYearStart(year - 1) else currentYearStart
val currentCalendarWeek = start.until(this, DateTimeUnit.WEEK) + 1
return currentCalendarWeek
}
private fun firstWeekInYearStart(year: Int): LocalDate {
val jan1st = LocalDate(year, 1, 1)
val previousMonday = jan1st.minus(jan1st.dayOfWeek.ordinal, DateTimeUnit.DAY)
return if (jan1st.dayOfWeek <= DayOfWeek.THURSDAY) previousMonday else previousMonday.plus(1, DateTimeUnit.WEEK)
} Still, would be nice to have it provided by datetime library, rather than having to write it every time IMHO. |
Oh, ok, thanks. It's simply surprising that anyone would consciously want to number their version with repeats like |
Ah, but that's not what we use. The release would be |
@ferinagy |
Uh oh!
There was an error while loading. Please reload this page.
Hello,
I think it'd be great to have natively the ability to know which week number a particular date is from.
It's very easy to do with
GregorianCalendar
in the JVM or Android (calendar.get(Calendar.WEEK_OF_YEAR)
), but I see no such facility for Kotlin common code yet.Have a great day!
The text was updated successfully, but these errors were encountered: