sourcelibs/Dates/src/Dates.xtl

1⍝# Dates: dates -- day numbers from calendar dates and back, weekdays, 2⍝# leap years, month lengths, ISO text, month calendars. 3⍝# Import it with an alias of your choice: "d:" u_se< "Dates". 4⍝# Put libs/Dates/src on XETAL_PATH ("just path"); the reference is libs/Dates/docs. 5⍝# Names with l: are exported; those under h: are private to this file. 6⍝# 7⍝# A date is three numbers, year month day (2026 10 3), or a 3-row 8⍝# matrix of them, one date per column; a day number counts days from 9⍝# 1970-01-01 (day 0), so dates subtract and add as numbers. The 10⍝# calendar is the proleptic Gregorian one, for any year. 11 12ᵗ⁼u̲se< "Strings" 13 14⍝## Day numbers 15 16⍝# d_ays ymd: the day number of each date (Howard Hinnant's 17⍝# days_from_civil, whole-array). 18ˡd̲ays ← { ymd → 19 m ← f̲irst 1 d̲rop ymd 20 d ← f̲irst 2 d̲rop ymd 21 y ← (f̲irst ymd) − m ≤ 2 ⍝ March-based years 22 era ← y d̲iv 400 23 yoe ← y − era × 400 24 doy ← (d − 1) + (2 + 153 × m + 9 − 12 × m > 2) d̲iv 5 25 doe ← (yoe × 365) + (yoe d̲iv 4) − (yoe d̲iv 100) − doy ⍝ read right to left: + doy 26 (era × 146097) + doe − 719468 27} 28 29⍝# c_ivil n: the date (year month day) of each day number: three 30⍝# numbers for one, a 3-row matrix for several (civil_from_days). 31ˡc̲ivil ← { n → 32 z ← n + 719468 33 era ← z d̲iv 146097 34 doe ← z − era × 146097 35 yoe ← (doe − (doe d̲iv 1460) − (doe d̲iv 36524) − doe d̲iv 146096) d̲iv 365 36 doy ← doe − (365 × yoe) + (yoe d̲iv 4) − yoe d̲iv 100 37 mp ← (2 + 5 × doy) d̲iv 153 38 d ← 1 + doy − (2 + 153 × mp) d̲iv 5 39 m ← mp + 3 − 12 × mp ≥ 10 40 y ← (yoe + era × 400) + m ≤ 2 41 (3 c̲at s̲hape n) r̲eshape y c̲at m c̲at d 42} 43 44⍝# t_oday @: today's day number, from the clock ([]TS: year, month, 45⍝# day, ... of the local time stamp). Read each time it is called. 46ˡt̲oday ← { @ → ˡd̲ays 3 t̲ake ⎕TS } 47 48⍝## Calendar facts 49 50⍝# w_eekday n: the day of the week of each day number, 1 Monday to 7 51⍝# Sunday (ISO 8601; day 0, 1970-01-01, was a Thursday). 52ˡw̲eekday ← { n → 1 + (n + 3) m̲od 7 } 53 54⍝# l_eap? y: whether each year is a leap year. 55ˡl̲eap? ← { y → (0 = y m̲od 400) ∨ (0 = y m̲od 4) ∧ 0 ≠ y m̲od 100 } 56 57⍝# y d_aysIn m: the number of days in month m of year y. 58ˡd̲aysIn ← { y m → 59 '∨ r̲/ r̲avel (m < 1) ∨ m > 12 ? @ p̲anic< "months run 1 to 12, not {m}"
p̲anic< expands to
(⎕P̲ANIC ("months run 1 to 12, not " c̲at (f̲ormat (m))))
60 (m s̲elect 31 28 31 30 31 30 31 31 30 31 30 31) + (m = 2) ∧ ˡl̲eap? y 61} 62 63⍝## Text 64 65⍝# A number of one or two digits as two, 03. 66ʰt̲wo ← { x → " " "0" ᵗr̲eplace 2 ᵗp̲adLeft f̲ormat x } 67 68⍝# i_so n: each day number as ISO text, 2026-10-03 (a list for several). 69ˡi̲so ← { n → 70 c ← ˡc̲ivil r̲avel n 71 '{ i → 72 v ← i s̲elect₂ c 73 (f̲ormat f̲irst v) c̲at "-" c̲at (ʰt̲wo f̲irst 1 d̲rop v) c̲at "-" c̲at ʰt̲wo f̲irst 2 d̲rop v 74 } m̲ap r̲ange t̲ally r̲avel n 75} 76 77⍝# y c_alendar m: month m of year y as a 6 by 7 matrix of days, Monday 78⍝# first, 0 where a cell has no day. 79ˡc̲alendar ← { y m → 80 first ← ˡw̲eekday ˡd̲ays y c̲at m c̲at 1 81 n ← y ˡd̲aysIn m 82 cells ← (o̲ffsets 42) − first − 2 83 6 7 r̲eshape cells × (cells ≥ 1) ∧ cells ≤ n 84} 85 86⍝# y m_onth m: the month as text, a header and a line per week. 87ˡm̲onth ← { y m → 88 c ← y ˡc̲alendar m 89 weeks ← '{ i → " " ᵗj̲oin '{ d → d = 0 ? " "◆ 2 ᵗp̲adLeft f̲ormat d } m̲ap i s̲elect c } m̲ap r̲ange 6 90 ᵗm̲ix (e̲nclose "Mo Tu We Th Fr Sa Su") c̲at weeks 91}