Fire Emblem Heroes Wiki
Advertisement
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
Template-info Documentation

Lua metamodule containing functions for datetime formatting. All datetimes are assumed to be in UTC.

  • t: A Unix timestamp; that is, the number of seconds since the Unix epoch ().

Constants

  • MIN_TIME = '1970-01-01T00:00:00Z'
ISO 8601 representation of the Unix epoch.
  • MAX_TIME = '2038-01-19T03:14:07Z'
ISO 8601 representation of the furthest time that can fit into a signed 32-bit Unix timestamp. Only datetimes between MIN_TIME and MAX_TIME (inclusive) are considered safe for use.
  • MONTHS_FULL = {'January', 'February', 'March', ...}
Full English names of the months as a Lua array.
  • MONTHS_SHORT = {'Jan', 'Feb', 'Mar', ...}
Abbreviated English names of the months as a Lua array.

ISO 8601 formatting

  • is_iso8601 (s)
Returns whether s is a valid ISO 8601 datetime.
  • to_iso8601 (t)
Converts a Unix timestamp into its ISO 8601 representation.
  • from_iso8601 (s)
If the given argument is a valid ISO 8601 datetime, returns its corresponding Unix timestamp.
  • parse_iso8601 (s)
If the given argument is a valid ISO 8601 datetime, parses it and returns 6 values: year, month, day, hours, minutes, and seconds, in that order.
  • make_iso8601 (y, m, d, hh = 0, mm = 0, ss = 0)
Returns the ISO 8601 datetime with the given year, month, day, hours, minutes, and seconds.

Cargo datetime formatting

  • is_cargo (s)
Returns whether s is a valid Cargo datetime (same as ISO 8601, except without the T and the Z).
  • to_cargo (t)
Converts a Unix timestamp into its Cargo representation.
  • from_cargo (s)
If the given argument is a valid Cargo datetime, returns its corresponding Unix timestamp. Otherwise, if it is a valid Cargo date (just the YYYY-MM-DD part), returns a Unix timestamp representing 00:00 UTC on the given date.
  • parse_cargo (s)
If the given argument is a valid Cargo datetime, parses it and returns 6 values: year, month, day, hours, minutes, and seconds, in that order. If it is a valid Cargo date instead, the three time values are 0.
  • make_cargo (y, m, d, hh = 0, mm = 0, ss = 0)
Returns the Cargo datetime with the given year, month, day, hours, minutes, and seconds. This function always produces a datetime and never a date even if the time values are all omitted.

Wiki formatting

  • ht (t_or_s)
Formats a time according to ISO 8601 and attaches a Pacific Time tooltip to it. If the argument is a Lua number, it is treated as a Unix timestamp; otherwise, it must be either an ISO 8601 or Cargo datetime. A warning is generated for any invalid datetime arguments. This implementation properly handles Daylight Saving Time changes.
  • HT {t_or_s}
For use from wikitext. Notice the uppercase function name. Implements Template:HT.
  • ht_range (t_or_s1, t_or_s2)
Formats a datetime range. Converts both arguments to ISO 8601 tooltips and joins them with a hyphen. Either datetime can be nil in order to support indefinite datetime ranges.
local LibraryUtil = require 'libraryUtil'

local MONTHS_FULL = {'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'}
local MONTHS_SHORT = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'}

local PDT_RANGES = {
	[2017] = {1489312800, 1509872400}, -- Sun Mar 12 10:00:00 2017 - Sun Nov  5 09:00:00 2017
	[2018] = {1520762400, 1541322000}, -- Sun Mar 11 10:00:00 2018 - Sun Nov  4 09:00:00 2018
	[2019] = {1552212000, 1572771600}, -- Sun Mar 10 10:00:00 2019 - Sun Nov  3 09:00:00 2019
	[2020] = {1583661600, 1604221200}, -- Sun Mar  8 10:00:00 2020 - Sun Nov  1 09:00:00 2020
	[2021] = {1615716000, 1636275600}, -- Sun Mar 14 10:00:00 2021 - Sun Nov  7 09:00:00 2021
	[2022] = {1647165600, 1667725200}, -- Sun Mar 13 10:00:00 2022 - Sun Nov  6 09:00:00 2022
	[2023] = {1678615200, 1699174800}, -- Sun Mar 12 10:00:00 2023 - Sun Nov  5 09:00:00 2023
	[2024] = {1710064800, 1730624400}, -- Sun Mar 10 10:00:00 2024 - Sun Nov  3 09:00:00 2024
	[2025] = {1741514400, 1762074000}, -- Sun Mar  9 10:00:00 2025 - Sun Nov  2 09:00:00 2025
}

local get_dst_ranges = function (s, e)
	mw.log('local PDT_RANGES = {')
	for year = s, e do
		-- 2nd Sunday in March, 02:00 PST
		local datetime = {year = year, month = 3, day = 1, hour = 10}
		while os.date('!%w', os.time(datetime)) ~= '0' do
			datetime.day = datetime.day + 1
		end
		datetime.day = datetime.day + 7
		local pdt_start = os.time(datetime)

		-- 1st Sunday in November, 02:00 PDT
		datetime.month = 11
		datetime.day = 1
		datetime.hour = 9
		while os.date('!%w', os.time(datetime)) ~= '0' do
			datetime.day = datetime.day + 1
		end
		local pdt_end = os.time(datetime)

		mw.log(('\t[%d] = {%d, %d}, -- %s - %s'):format(year, pdt_start, pdt_end, os.date('!%c', pdt_start), os.date('!%c', pdt_end)))
	end
	mw.log('}')
end



local is_iso8601 = function (str)
	return type(str) == 'string' and string.find(str, '^(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z$') ~= nil
end

local to_iso8601 = function (t)
	return os.date('!%Y-%m-%dT%H:%M:%SZ', t)
end

local from_iso8601 = function (str)
	local y, m, d, hh, mm, ss = string.match(str, '^(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z$')
	if ss then
		return os.time {year = y, month = m, day = d, hour = hh, min = mm, sec = ss}
	end
end

local parse_iso8601 = function (str)
	local y, m, d, hh, mm, ss = string.match(str, '^(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z$')
	if ss then
		return tonumber(y), tonumber(m), tonumber(d), tonumber(hh), tonumber(mm), tonumber(ss)
	end
end

local make_iso8601 = function (y, m, d, hh, mm, ss)
	return os.date('!%Y-%m-%dT%H:%M:%SZ', os.time {year = y, month = m, day = d, hour = hh or 0, min = mm or 0, sec = ss or 0})
end



local MIN_TIME = to_iso8601(0)
local MAX_TIME = to_iso8601(0x7FFFFFFF)



local is_cargo = function (str)
	return type(str) == 'string' and string.find(str, '^(%d+)%-(%d+)%-(%d+) (%d+):(%d+):(%d+)$') ~= nil
end

local to_cargo = function (t)
	return os.date('!%Y-%m-%d %H:%M:%S', t)
end

local from_cargo = function (str)
	local y, m, d, hh, mm, ss = string.match(str, '^(%d+)%-(%d+)%-(%d+) (%d+):(%d+):(%d+)$')
	if ss then
		return os.time {year = y, month = m, day = d, hour = hh, min = mm, sec = ss}
	end

	y, m, d = string.match(str, '^(%d+)%-(%d+)%-(%d+)$')
	if d then
		return os.time {year = y, month = m, day = d, hour = 0}
	end
end

local parse_cargo = function (str)
	local y, m, d, hh, mm, ss = string.match(str, '^(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)$')
	if ss then
		return tonumber(y), tonumber(m), tonumber(d), tonumber(hh), tonumber(mm), tonumber(ss)
	end

	y, m, d = string.match(str, '^(%d+)-(%d+)-(%d+)$')
	if d then
		return tonumber(y), tonumber(m), tonumber(d), 0, 0, 0
	end
end

local make_cargo = function (y, m, d, hh, mm, ss)
	return os.date('!%Y-%m-%d %H:%M:%S', os.time {year = y, month = m, day = d, hour = hh or 0, min = mm or 0, sec = ss or 0})
end



local Template_Hover_nocargo = function (text, title)
	if title == nil or title == '' then
		return text
	end
	return tostring(mw.html.create('span'):attr('title', mw.text.decode(title)) -- mw.html automatically HTML-escapes attributes
		:css('border-bottom', '0'):css('text-decoration', 'underline dotted'):css('cursor', 'help')
		:wikitext(text))
end

local ht1 = function (t)
	LibraryUtil.checkTypeMulti('t', 1, t, {'number', 'string', 'nil'})
	if t == nil or t == '' then
		return '<i>(Unknown datetime)</i>'
	end

	local timestamp = type(t) == 'number' and t or from_iso8601(t) or from_cargo(t)
	if timestamp then
		local datetime = os.date('!*t', timestamp)
		local pdt_range = PDT_RANGES[datetime.year] -- done manually as datetime.isdst is always false
		local is_dst = pdt_range and timestamp >= pdt_range[1] and timestamp < pdt_range[2]
		local pdt_datetime = os.date('!*t', timestamp - 60 * 60 * (is_dst and 7 or 8))

		-- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time:
		-- "If the element does not have a datetime attribute, it must not have any
		--  element descendants, and the datetime value is the element's child text content."
		local text = tostring(mw.html.create('time'):wikitext(to_iso8601(timestamp)))
		local title = ('Pacific %s Time: %d/%d/%02d at %02d:%02d %s'):format(
			is_dst and 'Daylight' or 'Standard',
			pdt_datetime.month, pdt_datetime.day, pdt_datetime.year % 100,
			(pdt_datetime.hour - 1) % 12 + 1, pdt_datetime.min, pdt_datetime.hour >= 12 and 'p.m.' or 'a.m.')
		return Template_Hover_nocargo(text,  title)
	else
		mw.addWarning('Invalid timestamp: ' .. t)
		return require 'Module:Error'.error(t)
	end
end

local ht_range = function (t1, t2)
	return ('%s&nbsp;– %s'):format(t1 ~= nil and ht1(t1) or '', t2 ~= nil and ht1(t2) or '')
end

local ht = function (args)
	return ht1(tonumber(args[1]) or args[1])
end



local p = require 'Module:MakeMWModule'.makeMWModule {ht = ht}
return {
	MIN_TIME = MIN_TIME,
	MAX_TIME = MAX_TIME,
	MONTHS_FULL = MONTHS_FULL,
	MONTHS_SHORT = MONTHS_SHORT,

	is_iso8601 = is_iso8601,
	to_iso8601 = to_iso8601,
	from_iso8601 = from_iso8601,
	parse_iso8601 = parse_iso8601,
	make_iso8601 = make_iso8601,

	is_cargo = is_cargo,
	to_cargo = to_cargo,
	from_cargo = from_cargo,
	parse_cargo = parse_cargo,
	make_cargo = make_cargo,

	HT = p.ht,
	HT_ = p.ht_,
	ht = ht1,
	ht_range = ht_range,
}
Advertisement