Skip to content

Conversation

isentropic
Copy link
Contributor

Based on some discussion in https://discourse.julialang.org/t/determine-date-range-given-year-and-week-number/93878
and the relevant issue #48490
I created a couple of utilities to help handle 2023-W05-4 (YYYY-WWW-D) dateformats (https://en.wikipedia.org/wiki/ISO_week_date)
Currently there is no functionality of ISO Week with weekday | 2023-W05-4 in Julia, but this could be good start and could help a lot of people who require such dateformat.

Notably, with firstmondayofyear it is trivial to convert from week format 2023-W05-4 to usual date format 2023-02-02 as:

firstmondayofyear(Date(2023)) + Week(5 - 1) + Day(3)

This was otherwise impossible within std lib

@inkydragon inkydragon added dates Dates, times, and the Dates stdlib module stdlib Julia's standard library labels Feb 3, 2023
@isentropic
Copy link
Contributor Author

Since the first week is defined in terms of Thursday according to ISO, perhaps this should be named firstthursdayofyear and then it would be both right by intuition and ISO convention. But then, most users will need to subtract three days to come back to monday.

@knuesel
Copy link
Member

knuesel commented Feb 3, 2023

I find both firstmondayofyear and firstthursdayofyear a bit ambiguous, because with ISO week dates the weird thing is the year in which a date can fall. This "ISO week-numbering year" is often called "ISO year" for short (see e.g. here).

For example, 2019-12-30 and 2021-01-01 are two dates that have ISO year 2020: https://www.epochconverter.com/weeks/2020.

So with

julia> date = Date(2021)   # A day of 2021 that has ISO year 2020
2021-01-01

what should firstmondayofyear(date) or firstthursdayofyear(date) be, when it's not clear which year we're talking about?

The current implementation in this PR gives

julia> firstmondayofyear(date)   # date = 2021-01-01
2021-01-04

Is this expected? If the function is supposed to help computations with ISO weeks I would expect here to get 2019-12-30, because that is the first Monday in the ISO year that contains the date 2021-01-01.

@knuesel
Copy link
Member

knuesel commented Feb 3, 2023

Here are some ideas of other functions/names to help working with ISO week dates:

  • firstmondayofisoyear (and matching lastmondayofisoyear) to get the first Monday of the ISO year containing the given date,
  • isoyear to get the the ISO year containing the given date,
  • Date and DateTime constructors that accept Year and Week values and interpret the inputs as ISO values.

Maybe most useful would be weekdates to address the original problem directly (find date range given ISO year and week).

Examples:

julia> date = Date(2021)  # 2021-01-01 falls in ISO year 2020 which has first Monday 2019-12-30
2021-01-01

julia> firstmondayofisoyear(date)
2019-12-30

julia> isoyear(date)
2020

julia> Date(Year(2021), Week(1))
2019-12-30

julia> weekdates(Year(2020), Week(1))
Date("2019-12-30"):Date("2020-01-05")

@isentropic
Copy link
Contributor Author

The current behavior is intentional, but you are right about the ambiguity. Initially I was going to only let iso year as an input, but then I thought it would make it easier to let users input arbitrary Date which is technically wrong. I could keep the functions implementations mostly the same, but the input will be iso year (integer). The names will change to reflect iso year. How about this for a start?

Next, for the extra functionality I will follow the examples you have given. Do you know where could I find test data?

@knuesel
Copy link
Member

knuesel commented Feb 8, 2023

I think it makes sense to let users pass an arbitrary Date: this is how the existing first... functions work (for example firstdayofyear). If a function is called firstmondayofyear, it should probably work the same: accept any TimeType that specifies a day (so either a Date or a DateTime) and give the correct answer for that day.

In the case of firstmondayofyear, this means the user can give any day, and should get the first Monday of the year that contains this day. So that's unrelated to the notion of ISO week... For example the proposed implementation gives

julia> firstmondayofyear(Date(1901))
1900-12-31

while I think it should give 1901-01-06...

But anyway for working with ISO weeks, aren't the functions I proposed easier and clearer? For example to find 2023-W05-4, you could call Date(Year(2023), Week(5), Day(4)), or weekdates(Year(2023), Week(1))[4]. What do you think? Do you see a case that would be easier to solve with firstmondayofyear?

For the test data maybe the easiest is to make a CSV with Excel (or with Julia and the week function but that would be less of a cross-check). Or we can just take a few interesting cases from a page like https://www.epochconverter.com/weeks/ ?

@isentropic
Copy link
Contributor Author

isentropic commented Feb 9, 2023

I actually thought and decided against firstmondayofyear functions completely, as I feel it is not needed now that I learned more about ISO year system. I implemented some utils that would translate from normal date to ISO-week-day format.

Though, the current implementation might not be too fast, but it feels to be more robust. What do you think? If this looks to be satisfactory I will proceed to implement inverse conversion and weekdates functionality and Date wrappers. I will add tests too, once we agree on the implementation.

@isentropic
Copy link
Contributor Author

For weeksperyear I used https://en.wikipedia.org/wiki/ISO_week_date#Weeks_per_year equivalent definition which seems safe

@isentropic isentropic changed the title add first/lastmondayofyear utilities add ISO year / ISO week utilities Feb 13, 2023
Copy link
Member

@knuesel knuesel left a comment

Choose a reason for hiding this comment

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

Thanks for the update, the proposed API looks good to me except for the argument type for weeksperyear: I think that function should accept a Year instead of a DateTime. With a DateTime it's not clear if the function will give an answer for the actual year of dt, or for the ISO year of dt.

Also it would be good to add one or two examples (jldoctests) in each of the docstrings.

isentropic and others added 3 commits March 5, 2023 23:34
Copy link
Member

@inkydragon inkydragon left a comment

Choose a reason for hiding this comment

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

@isentropic
Copy link
Contributor Author

Yeah, I'll get on to that, sorry for the delay. My editor keeps formatting files on save according to juliaformatter (thereby chaning like 50% of the lines), i have not committed these changes but do you guys try to format the julia stdlib codebase? Should I commit the stylistic changes?

@inkydragon
Copy link
Member

do you guys try to format the julia stdlib codebase?

We do want to format the whole codebase.
And there is a related issue: #47052

Should I commit the stylistic changes?

Better not, Only format your newly written code.

@isentropic
Copy link
Contributor Author

Yeah i have no idea why tests are failing on apple ...

@isentropic
Copy link
Contributor Author

#!/usr/bin/env python3

import pandas as pd
data = pd.read_csv("testdate.csv", parse_dates=['date'])
data["iso-date"] = data.date.apply(lambda x: x.isocalendar())

for row in range(data.shape[0]):
    normaldate = data.iloc[row, 0].strftime("%Y,%m,%d")
    isoyear = data.iloc[row,1].year
    isoweek = data.iloc[row,1].week
    isoday = data.iloc[row,1].weekday
    print(f"@test isoweekdate(Date({normaldate})) == ({isoyear}, {isoweek}, {isoday})")

was used to generate the test cases

@inkydragon
Copy link
Member

i have no idea why tests are failing on apple ...

The error is not related to Dates, just ignore it.

@inkydragon
Copy link
Member

doctest failed.
The output needs to be updated.

@isentropic
Copy link
Contributor Author

I dont understand, where/what should I fix?

@knuesel
Copy link
Member

knuesel commented Mar 28, 2025

This plugs a functionality hole in Dates, ISO week is the standard for week numbering. And it's a simple addition, just implementing "missing" methods and some tests.

@DilumAluthge
Copy link
Member

Is this PR still needed? cc: @tecosaur (who previously added the forget me not label to this PR).

FWIW, I still think it might be easier to just add this to a separate package, unless there's a specific need for it to be in the stdlib (e.g. some other stdlib functionality would utilize this PR).

Maybe @quinnj or @omus could weigh in.

@isentropic
Copy link
Contributor Author

isentropic commented Sep 29, 2025

It's 2 functions total more or less that patches an obviously missing functionality in Dates that is present in python's standard library. ISO year is a little niche but sometimes you need this for 'week' based calendars (like schools starting on first monday of the year, or similar). Being 3rd party isn't necessary because it is just a single function (10 lines). This kind of functionality isn't likely to change in the future being a ISO standard

@knuesel
Copy link
Member

knuesel commented Sep 29, 2025

@DilumAluthge I don't think "required by other stdlib functionality" is the best measure in this case. Much of the Dates module is about calendars, weeks, months, etc. The small addition in this PR fits perfectly in that, and plugs a hole that's not trivial to cover by oneself, and it would be annoying having to rely on a third party package just for that.

Maybe Julia shouldn't come with a Dates module if the stdlib doesn't need it, but as long as we ship one, I think it should include the functionality in this PR.

@adienes
Copy link
Member

adienes commented Sep 29, 2025

my opinion is that the fact that the Int return type matches dayofweek is more than enough justification to not have to fix that API inconsistency in this PR, even if in theory a DayOfWeek type would have been nice to have throughout Dates

probably does need compat note in docs though (now will be 1.13)

@adienes adienes added needs docs Documentation for this change is required and removed status: waiting for PR reviewer labels Sep 29, 2025
@isentropic
Copy link
Contributor Author

@DilumAluthge im not sure if im supposed to add docs somewhere now? Please tell me where and how and I will do it.

@adienes
Copy link
Member

adienes commented Sep 30, 2025

just a compat note; something like

!!! compat "Julia 1.13"
    This function requires Julia 1.13 or later.

at the end of each of the docstrings of the 3 functions

@isentropic
Copy link
Contributor Author

@adienes is this right?

@adienes adienes added merge me PR is reviewed. Merge when all tests are passing and removed needs docs Documentation for this change is required status: waiting for PR author labels Oct 1, 2025
@adienes
Copy link
Member

adienes commented Oct 1, 2025

yes, thanks!

I'm just merging master to restart CI

@adienes adienes merged commit f6dde2e into JuliaLang:master Oct 1, 2025
7 checks passed
@adienes adienes removed the merge me PR is reviewed. Merge when all tests are passing label Oct 1, 2025
@isentropic
Copy link
Contributor Author

isentropic commented Oct 2, 2025

Nice took like 2.5years only.

@isentropic
Copy link
Contributor Author

By the way, should I also edit the NEWS.md file too?

@adienes
Copy link
Member

adienes commented Oct 2, 2025

sorry that it took so long, and thank you for the contribution!

sure, a NEWS entry could be a good thing to add. I forgot about that

@isentropic
Copy link
Contributor Author

Should I update news file in a new PR or somehow it can be merged again from this PR

@knuesel
Copy link
Member

knuesel commented Oct 6, 2025

Please make a new PR, you can't add to one that's merged already.

@isentropic
Copy link
Contributor Author

#59776

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dates Dates, times, and the Dates stdlib module stdlib Julia's standard library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants