Module:Date: Difference between revisions

4,849 bytes added ,  9 years ago
major refactor with fixes; force Date to be read-only (error on write); list of dates in a month on a particular day of week
(clean up and some aliases)
(major refactor with fixes; force Date to be read-only (error on write); list of dates in a month on a particular day of week)
Line 4: Line 4:
local MINUS = '−'  -- Unicode U+2212 MINUS SIGN
local MINUS = '−'  -- Unicode U+2212 MINUS SIGN


local Date, DateDiff, datemt -- forward declarations
local Date, DateDiff, diffmt -- forward declarations
local uniq = { 'unique identifier' }


local function is_date(t)
local function is_date(t)
return type(t) == 'table' and getmetatable(t) == datemt
-- The system used to make a date read-only means there is no unique
-- metatable that is conveniently accessible to check.
return type(t) == 'table' and t._id == uniq
end
 
local function is_diff(t)
return type(t) == 'table' and getmetatable(t) == diffmt
end
 
local function _list_join(list, sep)
return table.concat(list, sep)
end
end


Line 18: Line 29:
self[self.n] = item
self[self.n] = item
end,
end,
join = function (self, sep)
join = _list_join,
return table.concat(self, sep)
end,
}
}
end
end
Line 93: Line 102:
local floor = math.floor
local floor = math.floor
local calname = date.calname
local calname = date.calname
local jd = date.jd
local limits  -- min/max limits for date ranges −9999-01-01 to 9999-12-31
local limits  -- min/max limits for date ranges −9999-01-01 to 9999-12-31
if calname == 'Julian' then
if calname == 'Julian' then
Line 100: Line 108:
limits = { -1930999.5, 5373484.49999 }
limits = { -1930999.5, 5373484.49999 }
else
else
limits = { 1, 0 }  -- impossible
return
end
end
if not (limits[1] <= jd and jd <= limits[2]) then
local jd = date.jd
if not (type(jd) == 'number' and limits[1] <= jd and jd <= limits[2]) then
return
return
end
end
Line 150: Line 159:
return
return
end
end
local y = numbers.y or numbers[1]
local y = numbers.year  or date.year
local m = numbers.m or numbers[2]
local m = numbers.month  or date.month
local d = numbers.d or numbers[3]
local d = numbers.day    or date.day
local H = numbers.H or numbers[4]
local H = numbers.hour
local M = numbers.M or numbers[5] or 0
local M = numbers.minute or date.minute or 0
local S = numbers.S or numbers[6] or 0
local S = numbers.second or date.second or 0
if not (y and m and d) then
if not (y and m and d) or not
return
(-9999 <= y and y <= 9999 and
end
1 <= m and m <= 12 and
if not (-9999 <= y and y <= 9999 and 1 <= m and m <= 12 and
1 <= d and d <= days_in_month(y, m, date.calname)) then
1 <= d and d <= days_in_month(y, m, date.calname)) then
return
return
end
end
Line 166: Line 174:
date.hastime = true
date.hastime = true
else
else
H = 0
H = date.hour or 0
end
end
if not (0 <= H and H <= 23 and
if not (0 <= H and H <= 23 and
Line 206: Line 214:
-- Example: 'am:AM era:BC'
-- Example: 'am:AM era:BC'
for item in options1:gmatch('%S+') do
for item in options1:gmatch('%S+') do
local lhs, rhs = item:match('^(%w+):(.+)$')
local lhs, rhs = item:match('^(%w+)[:=](.+)$')
if lhs then
if lhs then
result[lhs] = rhs
result[lhs] = rhs
Line 339: Line 347:
:gsub('%%%%', PERCENT)
:gsub('%%%%', PERCENT)
:gsub('(%s*)%%{(%w+)}', replace_property)
:gsub('(%s*)%%{(%w+)}', replace_property)
:gsub('(%s*)%%(-?)(%a)', replace_code)
:gsub('(%s*)%%(%-?)(%a)', replace_code)
:gsub(PERCENT, '%%')
:gsub(PERCENT, '%%')
)
)
Line 345: Line 353:


local function _date_text(date, fmt, options)
local function _date_text(date, fmt, options)
-- Return formatted string from given date.
-- Return a formatted string representing the given date.
if not is_date(date) then