Skip to content

Commit

Permalink
refactor(date): improve type hints
Browse files Browse the repository at this point in the history
- introduce types for internal date tables
- introduce some helper to encapsulate interaction with os api
  • Loading branch information
seflue committed Jun 10, 2024
1 parent ac9a6e0 commit acf9c4f
Showing 1 changed file with 79 additions and 38 deletions.
117 changes: 79 additions & 38 deletions lua/orgmode/objects/date.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-- TODO
-- Support diary format and format without short date name
---@type table<string, OrgDateSpan>
---@type OrgDateSpan
local spans = { d = 'day', m = 'month', y = 'year', h = 'hour', w = 'week', M = 'min' }
local config = require('orgmode.config')
local utils = require('orgmode.utils')
Expand All @@ -11,25 +11,31 @@ local time_format = '%H:%M'

---@alias OrgDateSpan 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'

---@class OrgDate
---@field type string
---@class OsTime

---@class OsDate
---@field day number?
---@field month number?
---@field year number?
---@field hour number?
---@field min number?
---
---@class OrgDateOpts : OsDate
---@field active boolean
---@field date_only boolean
---@field type string
---@field range OrgRange
---@field day number
---@field month number
---@field year number
---@field hour number
---@field min number
---@field timestamp number
---@field timestamp_end number
---@field is_dst boolean
---@field is_date_range_start boolean
---@field is_date_range_end boolean
---@field related_date_range OrgDate
---@field dayname string
---@field related_date_range OrgDate?
---@field dayname string?
---@field adjustments string[]
---@field private is_today_date boolean?

---@class OrgDate : OrgDateOpts
local Date = {
---@type fun(this: OrgDate, other: OrgDate): boolean
__eq = function(this, other)
Expand All @@ -53,10 +59,13 @@ local Date = {
end,
}

---@alias DateTbl { year: number, month: number, day: number, hour: number, min: number }
---@alias DateTblWithSec { year: number, month: number, day: number, hour: number, min: number, sec: number }

---@param source table
---@param target? table
---@param include_sec? boolean
---@return table
---@return OrgDateOpts
local function set_date_opts(source, target, include_sec)
target = target or {}
for _, field in ipairs({ 'year', 'month', 'day' }) do
Expand All @@ -71,17 +80,40 @@ local function set_date_opts(source, target, include_sec)
return target
end

---@param data table
---@param timestamp integer
---@return string
local function os_dayname(timestamp)
---@diagnostic disable-next-line:return-type-mismatch
return os.date('%a', timestamp)
end

---@param timestamp integer
---@return OsDate
local function os_date(timestamp)
---@diagnostic disable-next-line:return-type-mismatch
return os.date('*t', timestamp)
end

---@param date OsDate
---@return integer
local function os_time(date)
---@diagnostic disable-next-line:return-type-mismatch
---@diagnostic disable-next-line:param-type-mismatch
return os.time(date)
end

---@param data OrgDateOpts
---@return OrgDate
function Date:new(data)
data = data or {}
local date_only = data.date_only or (not data.hour and not data.min)
local opts = set_date_opts(data)
opts.type = data.type or 'NONE'
opts.active = data.active or false
opts.range = data.range
opts.timestamp = os.time(opts)
opts.timestamp = os_time(opts)
opts.date_only = date_only
opts.dayname = os.date('%a', opts.timestamp)
opts.dayname = os_dayname(opts.timestamp)
opts.is_dst = os.date('*t', opts.timestamp).isdst
opts.adjustments = data.adjustments or {}
opts.timestamp_end = data.timestamp_end
Expand All @@ -90,6 +122,7 @@ function Date:new(data)
opts.related_date_range = data.related_date_range or nil
setmetatable(opts, self)
self.__index = self
---@diagnostic disable-next-line:return-type-mismatch
return opts
end

Expand All @@ -99,8 +132,8 @@ function Date:from_time_table(time)
local timestamp_end = self.timestamp_end
local timestamp = self.timestamp
local range_diff = timestamp_end and timestamp_end - timestamp or 0
timestamp = os.time(set_date_opts(time, {}, true))
local opts = set_date_opts(os.date('*t', timestamp))
timestamp = os_time(set_date_opts(time, {}, true))
local opts = set_date_opts(os_date(timestamp))
if time.date_only ~= nil then
opts.date_only = time.date_only
else
Expand All @@ -124,7 +157,7 @@ end
---@return OrgDate
function Date:set(opts)
opts = opts or {}
local date = os.date('*t', self.timestamp)
local date = os_date(self.timestamp)
for opt, val in pairs(opts) do
date[opt] = val
end
Expand All @@ -142,14 +175,16 @@ function Date:clone(opts)
end

---@param date string
---@param dayname string
---@param dayname string?
---@param time string
---@param adjustments string
---@param adjustments string[]
---@param data table
---@return OrgDate
local function parse_datetime(date, dayname, time, time_end, adjustments, data)
local date_parts = vim.split(date, '-')
local time_parts = vim.split(time, ':')

---@type OrgDate
local opts = {
year = tonumber(date_parts[1]),
month = tonumber(date_parts[2]),
Expand All @@ -161,7 +196,7 @@ local function parse_datetime(date, dayname, time, time_end, adjustments, data)
opts.adjustments = adjustments
if time_end then
local time_end_parts = vim.split(time_end, ':')
opts.timestamp_end = os.time({
opts.timestamp_end = os_time({
year = tonumber(date_parts[1]),
month = tonumber(date_parts[2]),
day = tonumber(date_parts[3]),
Expand All @@ -174,8 +209,8 @@ local function parse_datetime(date, dayname, time, time_end, adjustments, data)
end

---@param date string
---@param dayname string
---@param adjustments string
---@param dayname string?
---@param adjustments string[]
---@param data table
---@return OrgDate
local function parse_date(date, dayname, adjustments, data)
Expand All @@ -194,8 +229,8 @@ end
---@param data? table
---@return OrgDate
local function today(data)
local date = os.date('*t', os.time()) --[[@as osdate]]
local opts = vim.tbl_deep_extend('force', date, data or {})
local date = os_date(os.time())
local opts = vim.tbl_deep_extend('force', date, data or {}) --[[@as OrgDateOpts]]
opts.date_only = true
return Date:new(opts)
end
Expand All @@ -209,8 +244,8 @@ end
---@param data? table
---@return OrgDate
local function now(data)
local date = os.date('*t', os.time()) --[[@as osdate]]
local opts = vim.tbl_deep_extend('force', date, data or {})
local date = os_date(os.time())
local opts = vim.tbl_deep_extend('force', date, data or {}) --[[@as OrgDateOpts]]
return Date:new(opts)
end

Expand All @@ -221,8 +256,8 @@ local function is_valid_date(datestr)
end

---@param datestr string
---@param opts? table
---@return OrgDate
---@param opts table?
---@return OrgDate?
local function from_string(datestr, opts)
if not is_valid_date(datestr) then
return nil
Expand All @@ -248,10 +283,10 @@ local function from_string(datestr, opts)
end

if time then
return parse_datetime(date, dayname, time, time_end, adjustments, opts)
return parse_datetime(date, dayname, time, time_end, adjustments, opts or {})
end

return parse_date(date, dayname, adjustments, opts)
return parse_date(date, dayname, adjustments, opts or {})
end

--- @param datestr string
Expand Down Expand Up @@ -316,7 +351,7 @@ local function from_org_date(datestr, opts)
range = Range:new({
start_line = line,
end_line = line,
start_col = start_date.range.end_col + 3,
start_col = start_date and start_date.range.end_col + 3,
end_col = opts.range.end_col,
}),
related_date_range = start_date,
Expand Down Expand Up @@ -397,7 +432,7 @@ function Date:adjust_end_time(value)
if not self.timestamp_end then
return self
end
local time_end = from_string(os.date(date_format .. ' ' .. time_format, self.timestamp_end))
local time_end = Date:from_time_table(os_date(self.timestamp_end))
time_end = time_end:adjust(value)
self.timestamp_end = time_end.timestamp
return self
Expand All @@ -424,7 +459,7 @@ function Date:without_adjustments()
return self:clone({ adjustments = {} })
end

---@param span OrgDateSpan
---@param span OrgDateSpan | string
---@return OrgDate
function Date:start_of(span)
if #span == 1 then
Expand Down Expand Up @@ -481,7 +516,7 @@ function Date:end_of(span)
end

if span == 'month' then
local date = os.date('*t', self.timestamp)
local date = os_date(self.timestamp)
return self:set({ day = Date._days_of_month(date) }):end_of('day')
end

Expand All @@ -490,12 +525,16 @@ end

---@return number
function Date:get_isoweekday()
---@type table
---@diagnostic disable-next-line: assign-type-mismatch
local date = os.date('*t', self.timestamp)
return utils.convert_to_isoweekday(date.wday)
end

---@return number
function Date:get_weekday()
---@type table
---@diagnostic disable-next-line: assign-type-mismatch
local date = os.date('*t', self.timestamp)
return date.wday
end
Expand All @@ -519,6 +558,7 @@ end
function Date:add(opts)
opts = opts or {}
---@type table
---@diagnostic disable-next-line: assign-type-mismatch
local date = os.date('*t', self.timestamp)
for opt, val in pairs(opts) do
if opt == 'week' then
Expand Down Expand Up @@ -631,7 +671,7 @@ function Date:is_after(date, span)
end

---@param date OrgDate
---@param span string
---@param span string?
---@return boolean
function Date:is_same_or_after(date, span)
local d = date
Expand Down Expand Up @@ -664,7 +704,7 @@ end

---@return boolean
function Date:has_date_range_end()
return self.related_date_range and self.is_date_range_start
return self.related_date_range ~= nil and self.is_date_range_start
end

function Date:has_time()
Expand Down Expand Up @@ -858,8 +898,9 @@ function Date:get_repeater()
return nil
end

---@return OrgDate
function Date:set_todays_date()
local time = os.date('*t', os.time())
local time = os_date(os.time())
return self:set({
year = time.year,
month = time.month,
Expand Down Expand Up @@ -965,7 +1006,7 @@ end
---@param close string
---@param last_match? OrgDate
---@param type? string
---@return OrgDate
---@return OrgDate?
local function from_match(line, lnum, open, datetime, close, last_match, type)
local search_from = last_match ~= nil and last_match.range.end_col or 0
local from, to = line:find(vim.pesc(open .. datetime .. close), search_from)
Expand Down

0 comments on commit acf9c4f

Please sign in to comment.