Given a particular month, I need to find the third Friday of that month. I just found out that there is a function called dayofweekofmonth which basically does the reverse of what I want. You give it a date and it tells you that date is in fact the third Friday of the month (it returns 3).
For example, today is the second Monday of the month
julia> dayofweekofmonth(Date("2021-03-08"))
2
I basically want to know if there is a shorthand for the inverse of this function (as in, something that returns the date itself) or if anybody has a cute trick to come up with it beyond running a boolean check on every date inside a given month.
When I’ll have an access to my computer I’ll write full code, but idea is the following
Calculate day of the week for the first day of the month e.g. “2021-03-01”
Calculate the distance to the nearest Friday (number in range 0:6)
Add 14
Add result to the first day of the month
UPD:
function thirdfriday(dt)
dt = floor(Date(dt), Month)
dt |> dayofweek |>
(x -> 5 - x < 0 ? 12 - x : 5 - x) |>
(x -> x + 14) |>
(x -> dt + Day(x))
end
julia> dr = Date(2021):Month(1):Date(2022);
julia> thirdfriday.(dr)
13-element Vector{Date}:
2021-01-15
2021-02-19
2021-03-19
2021-04-16
2021-05-21
2021-06-18
2021-07-16
2021-08-20
2021-09-17
2021-10-15
2021-11-19
2021-12-17
2022-01-21
It should be efficient, I suppose, since it adds no allocations, pure arithmetic.
It was interesting to read Dates documentation, there are interesting tonext and toprev functions as well as filter. So, if you are not afraid of iterations, at least they can be written in a very compact form
Not sure this is particularly clever but here’s how I would do it:
julia> using Dates
julia> third_friday(dates) = dates[findfirst(x -> (dayofweek(x) == 5) && (dayofweekofmonth(x) == 3), dates)]
third_friday (generic function with 1 method)
julia> candidates = Date(2021,1,15):Day(1):Date(2021,1,21)
Date("2021-01-15"):Day(1):Date("2021-01-21")
julia> [third_friday(candidates + Month(i)) for i ∈ 0:12]
13-element Vector{Date}:
2021-01-15
2021-02-19
2021-03-19
2021-04-16
2021-05-21
2021-06-18
2021-07-16
2021-08-20
2021-09-17
2021-10-15
2021-11-19
2021-12-17
2022-01-21
This uses the fact that the third Friday must be between the 15th and 21st (which I suppose is a variation on the “find first Friday and add 14” theme above)
function get_next_expiry(calc_date:: Date, month:: Integer; holidays:: AbstractArray = [])
# get 3rd Wednesday of the corresponding month
expiry_date = Dates.tonext(calc_date + Month(month-1)) do x
Dates.dayofweek(x) == Dates.Friday && Dates.dayofweekofmonth(x) == 3
end
# move to next business day if the day is a holiday - not sure if you need this
Dates.tonext(expiry_date, same=true) do x
isbusday(x, holidays=holidays)
end
end
# get the next 300 3rd Fridays, shifted to next business day if required
expiry_dates = get_next_expiry.(START_DATE, 1:300; holidays=target_holidays)
Thanks for the responses. Extra points for @lungben for thinking ahead about business days (but I need them for the Eurex trading calendar, not sure that is built-in anywhere).
Have a look at BusinessDays.jl. This example uses an unexported function findweekday. Find the first business day (TARGET2) on or after the first Friday of January 2021.
julia> using BusinessDays, Dates
help?> BusinessDays.findweekday
findweekday(weekday_target::Integer, yy::Integer, mm::Integer, occurrence::Integer, ascending::Bool) → Date
Given a year yy and month mm, finds a date where a choosen weekday occurs.
weekday_target values are declared in module Base.Dates:
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday = 1,2,3,4,5,6,7.
If ascending is true, searches from the beginning of the month. If false,
searches from the end of the month.
If occurrence is 2 and weekday_target is Monday, searches the 2nd Monday of
the given month, and so on.
julia> dow = Dates.Friday; y = 2021; m = 1; occurence = 1
1
julia> tobday(:TARGET2, BusinessDays.findweekday(dow, y, m, occurence, true))
2021-01-04