From fb513fb131b8d1dc79f6e9e5b05c31728fb43976 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Thu, 9 Feb 2023 15:26:11 +0900 Subject: [PATCH 01/11] add iso utils --- stdlib/Dates/src/accessors.jl | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index 05e9017303ef1..e9ec08577e259 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -32,6 +32,8 @@ function day(days) y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153) return c - div(153m - 457, 5) end + +# ISO year utils # https://en.wikipedia.org/wiki/Talk:ISO_week_date#Algorithms const WEEK_INDEX = (15, 23, 3, 11) function week(days) @@ -41,6 +43,47 @@ function week(days) return div(w, 28) + 1 end +""" +Year defined as ISO year. The equivalent is +`week(lastdayofyear(dt) - Day(3))` as 28 december is always in the last week +""" +function weeksinyear(dt::DateTime) + firstday = firstdayofyear(dt) + lastday = lastdayofyear(dt) + + if dayofweek(firstday) == 4 || dayofweek(lastday) == 4 + return 53 + end + return 52 +end + +""" +Return current ISO year as defined in +https://en.wikipedia.org/wiki/ISO_week_date +""" +function isoyear(dt::DateTime) + thisyear = Year(dt) + thismonth = Month(dt) + weeknumber = week(dt) + if weeknumber >= 52 && thismonth.value == 1 + # If it is january, then its the iso year from before + return Year(thisyear.value - 1) + elseif weeknumber == 1 && thismonth.value == 12 + # If it is december, then its the next year + return Year(thisyear.value + 1) + else + return thisyear + end +end + +""" +Return current ISO week date as defined in +https://en.wikipedia.org/wiki/ISO_week_date + +The return type is a tuple of `Year`, `Week` and `Integer` (from 1 to 7) +""" +isoweekdate(dt::DateTime) = (isoyear(dt), week(dt), dayofweek(dt)) + function quarter(days) m = month(days) return m < 4 ? 1 : m < 7 ? 2 : m < 10 ? 3 : 4 From 37a2e26b06e9a9a9f25f671b6c8b10570820d455 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Sun, 5 Mar 2023 23:34:31 +0900 Subject: [PATCH 02/11] Update stdlib/Dates/src/accessors.jl Co-authored-by: Jeremie Knuesel --- stdlib/Dates/src/accessors.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index e9ec08577e259..6c48ad11ef704 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -44,8 +44,7 @@ function week(days) end """ -Year defined as ISO year. The equivalent is -`week(lastdayofyear(dt) - Day(3))` as 28 december is always in the last week +Return the number of ISO weeks in the given year (see https://en.wikipedia.org/wiki/ISO_week_date). """ function weeksinyear(dt::DateTime) firstday = firstdayofyear(dt) From 2ce2e366ad8d16943763b971da035d1f0fdc13d1 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Sun, 5 Mar 2023 23:34:45 +0900 Subject: [PATCH 03/11] Update stdlib/Dates/src/accessors.jl Co-authored-by: Jeremie Knuesel --- stdlib/Dates/src/accessors.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index 6c48ad11ef704..4a94614a86650 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -57,8 +57,7 @@ function weeksinyear(dt::DateTime) end """ -Return current ISO year as defined in -https://en.wikipedia.org/wiki/ISO_week_date +Return the ISO year that contains `dt` (see https://en.wikipedia.org/wiki/ISO_week_date). """ function isoyear(dt::DateTime) thisyear = Year(dt) From c4498929525fef49ea7bc9619a6a669fabd9acc6 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Sun, 5 Mar 2023 23:34:58 +0900 Subject: [PATCH 04/11] Update stdlib/Dates/src/accessors.jl Co-authored-by: Jeremie Knuesel --- stdlib/Dates/src/accessors.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index 4a94614a86650..ccf212f164337 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -75,10 +75,10 @@ function isoyear(dt::DateTime) end """ -Return current ISO week date as defined in -https://en.wikipedia.org/wiki/ISO_week_date +Return the ISO week date that corresponds to `dt` (see +https://en.wikipedia.org/wiki/ISO_week_date). -The return type is a tuple of `Year`, `Week` and `Integer` (from 1 to 7) +The return type is a tuple of `Year`, `Week` and `Integer` (from 1 to 7). """ isoweekdate(dt::DateTime) = (isoyear(dt), week(dt), dayofweek(dt)) From 05237f2eeec16f1b23e7eebdde9392378b13b65e Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Mon, 6 Mar 2023 11:26:53 +0900 Subject: [PATCH 05/11] dt to year --- stdlib/Dates/src/accessors.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index ccf212f164337..00d472a337696 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -46,9 +46,9 @@ end """ Return the number of ISO weeks in the given year (see https://en.wikipedia.org/wiki/ISO_week_date). """ -function weeksinyear(dt::DateTime) - firstday = firstdayofyear(dt) - lastday = lastdayofyear(dt) +function weeksinyear(y::Year) + firstday = firstdayofyear(Date(y)) + lastday = lastdayofyear(Date(y)) if dayofweek(firstday) == 4 || dayofweek(lastday) == 4 return 53 From 1ab03e717c7c2d53eb0431cfcea1db00a2268670 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Mon, 6 Mar 2023 12:31:31 +0900 Subject: [PATCH 06/11] add tests and docs --- stdlib/Dates/src/Dates.jl | 1 + stdlib/Dates/src/accessors.jl | 29 +++++++++++++++- stdlib/Dates/test/accessors.jl | 63 ++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/stdlib/Dates/src/Dates.jl b/stdlib/Dates/src/Dates.jl index a111ea24089c4..39602d405ce38 100644 --- a/stdlib/Dates/src/Dates.jl +++ b/stdlib/Dates/src/Dates.jl @@ -60,6 +60,7 @@ export Period, DatePeriod, TimePeriod, yearmonthday, yearmonth, monthday, year, month, week, day, hour, minute, second, millisecond, dayofmonth, microsecond, nanosecond, + isoweekdate, isoyear, weeksinyear, # query.jl dayofweek, isleapyear, daysinmonth, daysinyear, dayofyear, dayname, dayabbr, dayofweekofmonth, daysofweekinmonth, monthname, monthabbr, diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index 00d472a337696..f9c326dd030ca 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -45,6 +45,14 @@ end """ Return the number of ISO weeks in the given year (see https://en.wikipedia.org/wiki/ISO_week_date). + +# Examples +```jldoctest +julia> weeksinyear(Year(2022)) +52 + +julia> weeksinyear(Year(2020)) +53 """ function weeksinyear(y::Year) firstday = firstdayofyear(Date(y)) @@ -58,6 +66,14 @@ end """ Return the ISO year that contains `dt` (see https://en.wikipedia.org/wiki/ISO_week_date). + +# Examples +```jldoctest +julia> isoyear(Date(2022, 1, 1)) +Year(2021) + +julia> isoyear(Date(2021, 12, 31)) +Year(2021) """ function isoyear(dt::DateTime) thisyear = Year(dt) @@ -73,14 +89,25 @@ function isoyear(dt::DateTime) return thisyear end end +isoyear(dt::Date) = isoyear(DateTime(dt)) """ Return the ISO week date that corresponds to `dt` (see https://en.wikipedia.org/wiki/ISO_week_date). The return type is a tuple of `Year`, `Week` and `Integer` (from 1 to 7). + +# Examples +```jldoctest +julia> isoweekdate(Date(2023, 03, 06)) +(2023, 10, 1) + +julia> isoweekdate(Date(2023, 01, 01)) +(2022, 52, 7) +``` """ -isoweekdate(dt::DateTime) = (isoyear(dt), week(dt), dayofweek(dt)) +isoweekdate(dt::DateTime) = (isoyear(dt).value, week(dt), dayofweek(dt)) +isoweekdate(dt::Date) = isoweekdate(DateTime(dt)) function quarter(days) m = month(days) diff --git a/stdlib/Dates/test/accessors.jl b/stdlib/Dates/test/accessors.jl index b690a81d70e49..774e145c3aa56 100644 --- a/stdlib/Dates/test/accessors.jl +++ b/stdlib/Dates/test/accessors.jl @@ -192,6 +192,69 @@ end dt1 = dt1 + Dates.Day(1) end end +@testset "ISO year utils" begin + # Tests from https://www.epochconverter.com/weeks + @test Dates.weeksinyear(Dates.Year(2023)) == 52 + @test Dates.weeksinyear(Dates.Year(2022)) == 52 + @test Dates.weeksinyear(Dates.Year(2021)) == 52 + @test Dates.weeksinyear(Dates.Year(2020)) == 53 + @test Dates.weeksinyear(Dates.Year(2019)) == 52 + @test Dates.weeksinyear(Dates.Year(2018)) == 52 + @test Dates.weeksinyear(Dates.Year(2017)) == 52 + @test Dates.weeksinyear(Dates.Year(2016)) == 52 + @test Dates.weeksinyear(Dates.Year(2015)) == 53 + @test Dates.weeksinyear(Dates.Year(2014)) == 52 + @test Dates.weeksinyear(Dates.Year(2013)) == 52 + @test Dates.weeksinyear(Dates.Year(2012)) == 52 + @test Dates.weeksinyear(Dates.Year(2011)) == 52 + @test Dates.weeksinyear(Dates.Year(2010)) == 52 + @test Dates.weeksinyear(Dates.Year(2009)) == 53 + + # From python datetime isocalendar + @test Dates.isoweekdate(Dates.Date(2023, 03, 06)) == (2023, 10, 1) + @test Dates.isoweekdate(Dates.Date(2023, 03, 07)) == (2023, 10, 2) + @test Dates.isoweekdate(Dates.Date(2023, 03, 08)) == (2023, 10, 3) + @test Dates.isoweekdate(Dates.Date(2022, 12, 29)) == (2022, 52, 4) + @test Dates.isoweekdate(Dates.Date(2022, 12, 30)) == (2022, 52, 5) + @test Dates.isoweekdate(Dates.Date(2022, 12, 31)) == (2022, 52, 6) + @test Dates.isoweekdate(Dates.Date(2023, 01, 01)) == (2022, 52, 7) + @test Dates.isoweekdate(Dates.Date(2023, 01, 02)) == (2023, 1, 1) + @test Dates.isoweekdate(Dates.Date(2023, 01, 03)) == (2023, 1, 2) + @test Dates.isoweekdate(Dates.Date(2021, 12, 28)) == (2021, 52, 2) + @test Dates.isoweekdate(Dates.Date(2021, 12, 29)) == (2021, 52, 3) + @test Dates.isoweekdate(Dates.Date(2021, 12, 30)) == (2021, 52, 4) + @test Dates.isoweekdate(Dates.Date(2021, 12, 31)) == (2021, 52, 5) + @test Dates.isoweekdate(Dates.Date(2022, 01, 01)) == (2021, 52, 6) + @test Dates.isoweekdate(Dates.Date(2022, 01, 02)) == (2021, 52, 7) + @test Dates.isoweekdate(Dates.Date(2022, 01, 03)) == (2022, 1, 1) + @test Dates.isoweekdate(Dates.Date(2022, 01, 04)) == (2022, 1, 2) + @test Dates.isoweekdate(Dates.Date(2022, 01, 05)) == (2022, 1, 3) + @test Dates.isoweekdate(Dates.Date(2022, 01, 06)) == (2022, 1, 4) + @test Dates.isoweekdate(Dates.Date(2020, 12, 29)) == (2020, 53, 2) + @test Dates.isoweekdate(Dates.Date(2020, 12, 30)) == (2020, 53, 3) + @test Dates.isoweekdate(Dates.Date(2020, 12, 31)) == (2020, 53, 4) + @test Dates.isoweekdate(Dates.Date(2021, 01, 01)) == (2020, 53, 5) + @test Dates.isoweekdate(Dates.Date(2021, 01, 02)) == (2020, 53, 6) + @test Dates.isoweekdate(Dates.Date(2021, 01, 03)) == (2020, 53, 7) + @test Dates.isoweekdate(Dates.Date(2021, 01, 04)) == (2021, 1, 1) + @test Dates.isoweekdate(Dates.Date(2021, 01, 05)) == (2021, 1, 2) + @test Dates.isoweekdate(Dates.Date(2021, 12, 31)) == (2021, 52, 5) + @test Dates.isoweekdate(Dates.Date(2022, 01, 01)) == (2021, 52, 6) + @test Dates.isoweekdate(Dates.Date(2022, 01, 02)) == (2021, 52, 7) + @test Dates.isoweekdate(Dates.Date(2020, 12, 31)) == (2020, 53, 4) + @test Dates.isoweekdate(Dates.Date(2021, 01, 01)) == (2020, 53, 5) + @test Dates.isoweekdate(Dates.Date(2021, 01, 02)) == (2020, 53, 6) + @test Dates.isoweekdate(Dates.Date(2021, 12, 31)) == (2021, 52, 5) + @test Dates.isoweekdate(Dates.Date(2022, 01, 01)) == (2021, 52, 6) + @test Dates.isoweekdate(Dates.Date(2022, 01, 02)) == (2021, 52, 7) + @test Dates.isoweekdate(Dates.Date(2022, 01, 03)) == (2022, 1, 1) + @test Dates.isoweekdate(Dates.Date(2019, 12, 31)) == (2020, 1, 2) + @test Dates.isoweekdate(Dates.Date(2020, 01, 01)) == (2020, 1, 3) + @test Dates.isoweekdate(Dates.Date(2020, 01, 02)) == (2020, 1, 4) + @test Dates.isoweekdate(Dates.Date(2018, 12, 31)) == (2019, 1, 1) + @test Dates.isoweekdate(Dates.Date(2019, 01, 01)) == (2019, 1, 2) + @test Dates.isoweekdate(Dates.Date(2019, 01, 02)) == (2019, 1, 3) +end @testset "Vectorized accessors" begin a = Dates.Date(2014, 1, 1) dr = [a, a, a, a, a, a, a, a, a, a] From e1b856e18dcc0cf3b7ceda95ce1546096a46be24 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Mon, 6 Mar 2023 17:40:44 +0900 Subject: [PATCH 07/11] close doctest blocks --- stdlib/Dates/src/accessors.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index f9c326dd030ca..b997e33e428f8 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -53,6 +53,7 @@ julia> weeksinyear(Year(2022)) julia> weeksinyear(Year(2020)) 53 +``` """ function weeksinyear(y::Year) firstday = firstdayofyear(Date(y)) @@ -74,6 +75,7 @@ Year(2021) julia> isoyear(Date(2021, 12, 31)) Year(2021) +``` """ function isoyear(dt::DateTime) thisyear = Year(dt) From d6417ed035b7f421f7a1ad0285a7ea2237bb8ce0 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Tue, 7 Mar 2023 19:59:52 +0900 Subject: [PATCH 08/11] Update stdlib/Dates/src/accessors.jl Co-authored-by: woclass --- stdlib/Dates/src/accessors.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index b997e33e428f8..a1e67102d4d80 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -71,10 +71,10 @@ Return the ISO year that contains `dt` (see https://en.wikipedia.org/wiki/ISO_we # Examples ```jldoctest julia> isoyear(Date(2022, 1, 1)) -Year(2021) +2021 years julia> isoyear(Date(2021, 12, 31)) -Year(2021) +2021 years ``` """ function isoyear(dt::DateTime) From 2e48a3c9b71b8b3021eefea496b4670976cecdaf Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Fri, 10 Mar 2023 14:10:52 +0900 Subject: [PATCH 09/11] Update stdlib/Dates/src/accessors.jl Co-authored-by: Jeremie Knuesel --- stdlib/Dates/src/accessors.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index a1e67102d4d80..5aa61f6d9591b 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -97,7 +97,7 @@ isoyear(dt::Date) = isoyear(DateTime(dt)) Return the ISO week date that corresponds to `dt` (see https://en.wikipedia.org/wiki/ISO_week_date). -The return type is a tuple of `Year`, `Week` and `Integer` (from 1 to 7). +The return type is a tuple of `Year`, `Week` and `Int64` (from 1 to 7). # Examples ```jldoctest From 0285827f7a47da411bf21f0ecb5ee3bb87c6ea84 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Wed, 1 Oct 2025 15:50:00 +0900 Subject: [PATCH 10/11] docs: add compat string --- stdlib/Dates/src/accessors.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/Dates/src/accessors.jl b/stdlib/Dates/src/accessors.jl index c2ee9eacd35c9..6199acc695bd8 100644 --- a/stdlib/Dates/src/accessors.jl +++ b/stdlib/Dates/src/accessors.jl @@ -54,6 +54,8 @@ julia> weeksinyear(Year(2022)) julia> weeksinyear(Year(2020)) 53 ``` +!!! compat "Julia 1.13" + This function requires Julia 1.13 or later. """ function weeksinyear(y::Year) firstday = firstdayofyear(Date(y)) @@ -76,6 +78,8 @@ julia> isoyear(Date(2022, 1, 1)) julia> isoyear(Date(2021, 12, 31)) 2021 years ``` +!!! compat "Julia 1.13" + This function requires Julia 1.13 or later. """ function isoyear(dt::DateTime) thisyear = Year(dt) @@ -107,6 +111,8 @@ julia> isoweekdate(Date(2023, 03, 06)) julia> isoweekdate(Date(2023, 01, 01)) (2022, 52, 7) ``` +!!! compat "Julia 1.13" + This function requires Julia 1.13 or later. """ isoweekdate(dt::DateTime) = (isoyear(dt).value, week(dt), dayofweek(dt)) isoweekdate(dt::Date) = isoweekdate(DateTime(dt)) From 684e7fa0664ea6a568d60fb38261b1c018ee1f68 Mon Sep 17 00:00:00 2001 From: Zhanibek Date: Wed, 8 Oct 2025 17:04:56 +0900 Subject: [PATCH 11/11] docs: update news --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index d6ecbf2762e14..d4568011c05cc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -112,6 +112,10 @@ Standard library changes * Introspection utilities such as `@code_typed`, `@which` and `@edit` now accept type annotations as substitutes for values, recognizing forms such as `f(1, ::Float64, 3)` or even `sum(::Vector{T}; init = ::T) where {T<:Real}`. Type-annotated variables as in `f(val::Int; kw::Float64)` are not evaluated if the type annotation provides the necessary information, making this syntax compatible with signatures found in stacktraces ([#57909], [#58222]). * Code introspection macros such as `@code_lowered` and `@code_typed` now have a much better support for broadcasting expressions, including broadcasting assignments of the form `x .+= f(y)` ([#58349]). +#### Dates + +* `isoweekdate, isoyear, weeksinyear` are now implemented and available for week based calendars, following [https://en.wikipedia.org/wiki/ISO_week_date](ISO week date). [#48507] + External dependencies ---------------------