mirror of
https://github.com/Ascyii/telekasten.nvim.git
synced 2026-01-01 06:14:23 -05:00
242 lines
6.9 KiB
Lua
242 lines
6.9 KiB
Lua
local M = {}
|
|
local luadate = require("telekasten.utils.luadate")
|
|
|
|
--- Returns the day of week (1..Monday, ..., 7..Sunday) for Dec, 31st of year
|
|
--- see https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm
|
|
--- see https://en.wikipedia.org/wiki/ISO_week_date
|
|
M.dow_for_year = function(year)
|
|
return (
|
|
year
|
|
+ math.floor(year / 4)
|
|
- math.floor(year / 100)
|
|
+ math.floor(year / 400)
|
|
) % 7
|
|
end
|
|
|
|
M.weeks_in_year = function(year)
|
|
local d = 0
|
|
local dy = M.dow_for_year(year) == 4 -- current year ends Thursday
|
|
local dyy = M.dow_for_year(year - 1) == 3 -- previous year ends Wednesday
|
|
if dy or dyy then
|
|
d = 1
|
|
end
|
|
return 52 + d
|
|
end
|
|
|
|
M.days_in_year = function(year)
|
|
local t = os.time({ year = year, month = 12, day = 31 })
|
|
return os.date("*t", t).yday
|
|
end
|
|
|
|
M.date_from_doy = function(year, doy)
|
|
local ret = {
|
|
year = year,
|
|
month = 1,
|
|
day = doy,
|
|
}
|
|
-- january is clear immediately
|
|
if doy < 32 then
|
|
return ret
|
|
end
|
|
|
|
local dmap = {
|
|
[1] = 31,
|
|
[2] = 28, -- will be fixed further down
|
|
[3] = 31,
|
|
[4] = 30,
|
|
[5] = 31,
|
|
[6] = 30,
|
|
[7] = 31,
|
|
[8] = 31,
|
|
[9] = 30,
|
|
[10] = 31,
|
|
[11] = 30,
|
|
[12] = 31,
|
|
}
|
|
if M.days_in_year(year) == 366 then
|
|
dmap[2] = 29
|
|
end
|
|
|
|
for month, d in pairs(dmap) do
|
|
doy = doy - d
|
|
if doy < 0 then
|
|
ret.day = doy + d
|
|
ret.month = month
|
|
return ret
|
|
end
|
|
end
|
|
return ret -- unreachable if input values are sane
|
|
end
|
|
|
|
-- the algo on wikipedia seems wrong, so we opt for full-blown luadate
|
|
M.isoweek_to_date = function(year, isoweek)
|
|
local ret = luadate(year .. "-W" .. string.format("%02d", isoweek) .. "-1")
|
|
return {
|
|
year = ret:getyear(),
|
|
month = ret:getmonth(),
|
|
day = ret:getday(),
|
|
}
|
|
end
|
|
|
|
local function daysuffix(day)
|
|
day = tostring(day)
|
|
if (day == "1") or (day == "21") or (day == "31") then
|
|
return "st"
|
|
end
|
|
if (day == "2") or (day == "22") then
|
|
return "nd"
|
|
end
|
|
if (day == "3") or (day == "23") then
|
|
return "rd"
|
|
end
|
|
return "th"
|
|
end
|
|
|
|
local daymap = {
|
|
"Monday",
|
|
"Tuesday",
|
|
"Wednesday",
|
|
"Thursday",
|
|
"Friday",
|
|
"Saturday",
|
|
"Sunday",
|
|
}
|
|
local monthmap = {
|
|
"January",
|
|
"February",
|
|
"March",
|
|
"April",
|
|
"May",
|
|
"June",
|
|
"July",
|
|
"August",
|
|
"September",
|
|
"October",
|
|
"November",
|
|
"December",
|
|
}
|
|
|
|
M.dateformats = {
|
|
date = "%Y-%m-%d",
|
|
week = "%V",
|
|
isoweek = "%Y-W%V",
|
|
time24 = "%H:%M:%S",
|
|
time12 = "%I:%M:%S %p",
|
|
}
|
|
|
|
function M.calculate_dates(date, calendar_monday)
|
|
local time = os.time(date)
|
|
local dinfo = os.date("*t", time) -- this normalizes the input to a full date table
|
|
local oneday = 24 * 60 * 60 -- hours * days * seconds
|
|
local oneweek = 7 * oneday
|
|
local df = M.dateformats
|
|
|
|
local dates = {}
|
|
|
|
-- this is to compensate for the calendar showing M-Su, but os.date Su is
|
|
-- always wday = 1
|
|
local wday = dinfo.wday - 1
|
|
if wday == 0 then
|
|
wday = 7
|
|
end
|
|
|
|
dates.year = dinfo.year
|
|
dates.month = dinfo.month
|
|
dates.day = dinfo.day
|
|
dates.hdate = daymap[wday]
|
|
.. ", "
|
|
.. monthmap[dinfo.month]
|
|
.. " "
|
|
.. dinfo.day
|
|
.. daysuffix(dinfo.day)
|
|
.. ", "
|
|
.. dinfo.year
|
|
|
|
local zonehour = string.sub(os.date("%z"), 1, 3)
|
|
local zonemin = string.sub(os.date("%z"), 4, 5)
|
|
dates.rfc3339 = os.date(df.date, time)
|
|
.. os.date("T%H:%M:%S")
|
|
.. "Z"
|
|
.. zonehour
|
|
.. ":"
|
|
.. zonemin
|
|
|
|
dates.time24 = os.date(df.time24, time)
|
|
dates.time12 = os.date(df.time12, time)
|
|
dates.date = os.date(df.date, time)
|
|
dates.prevday = os.date(df.date, time - oneday)
|
|
dates.nextday = os.date(df.date, time + oneday)
|
|
dates.week = os.date(df.week, time)
|
|
dates.prevweek = os.date(df.week, time - oneweek)
|
|
dates.nextweek = os.date(df.week, time + oneweek)
|
|
dates.isoweek = os.date(df.isoweek, time)
|
|
dates.isoprevweek = os.date(df.isoweek, time - oneweek)
|
|
dates.isonextweek = os.date(df.isoweek, time + oneweek)
|
|
|
|
-- things get a bit hairy at the year rollover. W01 only starts the first week ofs
|
|
-- January if it has more than 3 days. Partial weeks with less than 4 days are
|
|
-- considered W52, but os.date still sets the year as the new year, so Jan 1 2022
|
|
-- would appear as being in 2022-W52. That breaks linear linking respective
|
|
-- of next/prev week, so we want to put the days of that partial week in
|
|
-- January in 2021-W52. This tweak will only change the ISO formatted week string.
|
|
if tonumber(dates.week) == 52 and tonumber(dates.month) == 1 then
|
|
dates.isoweek = tostring(dates.year - 1) .. "-W52"
|
|
end
|
|
|
|
-- Find the Sunday that started this week regardless of the calendar
|
|
-- display preference. Then use that as the base to calculate the dates
|
|
-- for the days of the current week.
|
|
-- Finally, adjust Sunday to suit user calendar preference.
|
|
local starting_sunday = time - (wday * oneday)
|
|
local sunday_offset = 0
|
|
if calendar_monday == 1 then
|
|
sunday_offset = 7
|
|
end
|
|
dates.monday = os.date(df.date, starting_sunday + (1 * oneday))
|
|
dates.tuesday = os.date(df.date, starting_sunday + (2 * oneday))
|
|
dates.wednesday = os.date(df.date, starting_sunday + (3 * oneday))
|
|
dates.thursday = os.date(df.date, starting_sunday + (4 * oneday))
|
|
dates.friday = os.date(df.date, starting_sunday + (5 * oneday))
|
|
dates.saturday = os.date(df.date, starting_sunday + (6 * oneday))
|
|
dates.sunday = os.date(df.date, starting_sunday + (sunday_offset * oneday))
|
|
|
|
return dates
|
|
end
|
|
|
|
local function check_isoweek(year, isoweek, ydate)
|
|
print("*********** KW " .. isoweek .. " " .. year .. ": ")
|
|
-- local ret = M.weeknumber_to_date(year, isoweek)
|
|
local ret = M.isoweek_to_date(year, isoweek)
|
|
local result = ret.year == ydate.year
|
|
and ret.month == ydate.month
|
|
and ret.day == ydate.day
|
|
print(
|
|
ret.year
|
|
.. "-"
|
|
.. ret.month
|
|
.. "-"
|
|
.. ret.day
|
|
.. " == "
|
|
.. ydate.year
|
|
.. "-"
|
|
.. ydate.month
|
|
.. "-"
|
|
.. ydate.day
|
|
.. " : "
|
|
.. tostring(result)
|
|
)
|
|
end
|
|
|
|
M.run_tests = function()
|
|
print(check_isoweek(2020, 1, { year = 2019, month = 12, day = 30 })) -- 30.12.2019
|
|
print(check_isoweek(2020, 52, { year = 2020, month = 12, day = 21 })) -- 21.12.2020
|
|
print(check_isoweek(2020, 53, { year = 2020, month = 12, day = 28 })) -- 28.12.2020
|
|
print(check_isoweek(2021, 1, { year = 2021, month = 1, day = 4 })) -- 4.1.2020
|
|
print(check_isoweek(2021, 52, { year = 2021, month = 12, day = 27 })) -- 27.12.2021
|
|
print(check_isoweek(2022, 1, { year = 2022, month = 1, day = 3 })) -- 3.1.2022
|
|
end
|
|
|
|
-- M.run_tests()
|
|
|
|
return M
|