Module:TimeUtil

local lang = mw.getLanguage('en') local util_table = require('Module:TableUtil') local util_vars = require("Module:VarsUtil") local Date = require('Module:Date') local SECONDS_IN_DAY = 24 * 60 * 60

local TZLIST = { 'PST', 'CET', 'KST', 'UTC' } local DAYS_PER_WEEK = 7

local h = {}

function h.printDelta(delta) return delta > 0 and ('+' .. delta) or delta end

local p = {}

p.monthNumberToTri = { 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' }

p.monthNumberToName = { 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' }

p.monthNameToNumber = util_table.hash(p.monthNumberToName)

p.toUTC = { [true] = { PST = 7, CET = -2, KST = -9, UTC = 0 }, [false] = { PST = 8, CET = -1, KST = -9, UTC = 0 }, yes = { PST = 7, CET = -2, KST = -9, UTC = 0 }, no = { PST = 8, CET = -1, KST = -9, UTC = 0 }, spring = { PST = 7, CET = -1, KST = -9, UTC = 0 }, fall = { PST = 7, CET = -1, KST = -9, UTC = 0 }, }

function p.strToTime(str) local year, month, day, hour, minute, second = string.match(str, "(%d%d%d%d)%-(%d%d)%-(%d%d) (%d%d):(%d%d):?(%d?%d?)") return { year = year, month = month, day = day, hour = hour, min = minute, sec = second } end

function p.strToDate(str) local year, month, day = string.match(str, "(%d%d%d%d)-(%d%d)-(%d%d)") return { year = year, month = month, day = day } end

function p.strToDateStr(str) if not str then return nil end local date = str:match("(%d%d%d%d%-%d%d%-%d%d)") return date end

function p.strToDateStrAllowYearOnly(str) local date = p.strToDateStr(str) if date then return date end local year = str:match("(%d%d%d%d)%-") return year end

function p.strToTimeStr(str) if not str then return nil end return str:match('%d%d:%d%d') end

function p.strToDateFuzzy(str) local year, month, day = str:match('(....)-(..)-(..)') return { year = tonumber(year), month = tonumber(month), day = tonumber(day) } end

function p.strToYearNumber(str) if not str then return nil end local year = str:match('(%d%d%d%d)%-') if not year then return nil end return tonumber(year) end

function p.strToDateStrFuzzy(str, isApprox) local year, month, day = str:match('(....)%-(..)%-(..)') if not year and not month and not day then error(('Bad fuzzy date: %s'):format(str)) end year = tonumber(year) month = tonumber(month) day = tonumber(day) local suffix = isApprox and ' (approx.)' or '' if not month and not day then return year .. suffix elseif not day then return lang:formatDate('F Y', ('%s-%s-01'):format(year, month)) .. suffix end return lang:formatDate('j F Y', str) .. suffix end

function p.strToDateStrFuzzyWithoutYear(str, isApprox) local year, month, day = str:match('(....)%-(..)%-(..)') if not year and not month and not day then error(('Bad fuzzy date: %s'):format(str)) end year = tonumber(year) month = tonumber(month) day = tonumber(day) local suffix = isApprox and ' (approx.)' or '' if not year then return 'Unknown date' end if not month and not day then return year .. suffix elseif not day then return lang:formatDate('F', ('%s-%s-01'):format(year, month)) .. suffix end return lang:formatDate('F j', str) .. suffix end

function p.isInFuture(str, intervalSeconds) if not intervalSeconds then intervalSeconds = 0 end return os.difftime(os.time(p.strToTime(str)),os.time) > intervalSeconds end

function p.dateIsInFuture(str, intervalSeconds) if not intervalSeconds then intervalSeconds = 0 end local time = p.strToDateFuzzy(str) if time.day == '??' then time.day = 31 end if time.month == '??' then time.month = 12 end return os.difftime(os.time(time),os.time) > intervalSeconds end

function p.dateIsInFutureOrNow(str, intervalSeconds) local time = p.strToDateFuzzy(str) if time.day == '??' then time.day = 31 end if time.month == '??' then time.month = 12 end return os.difftime(os.time(time),os.time) > -1 / 2 * SECONDS_IN_DAY end

function p.dateInLocal(str) local tbl = mw.html.create('span'):addClass('DateInLocal'):wikitext(lang:formatDate('Y,n,d,H,i', str)) return tostring(tbl) end

function p.dateOnlyInLocal(str) local tbl = mw.html.create('span'):addClass('DateOnlyInLocal'):wikitext(lang:formatDate('Y,n,d,H,i', str)) return tostring(tbl) end

function p.dateInLocalMatches(str) local tbl = mw.html.create('span'):addClass('DateInLocalMatches'):wikitext(lang:formatDate('Y,n,d,H,i', str)) return tostring(tbl) end

function p.timeInLocal(str) local tbl = mw.html.create('span'):addClass('TimeInLocal'):wikitext(lang:formatDate('Y,n,d,H,i', str)) return tostring(tbl) end

function p.countdown(str, args) if not args then args = {} end local i = args.i or '' local data_end = args.data_end or 'remove' local options = args.options and table.concat(args.options, ' ') or 'no-leading-zeros' local tbl = mw.html.create tbl:tag('span') :attr('data-end', data_end) :attr('data-options',options) :attr('data-toggle','.post-countdown' .. i)		:addClass('countdown') :css('display','none') :tag('span') :addClass('countdowndate') :wikitext(lang:formatDate('j F Y H:i:s', str) .. ' +0000') tbl:tag('span') :addClass('post-countdown' .. i)		:css('display','none') :wikitext(args.default or '-') return tostring(tbl) end

function p.unix(str) if type(str) == 'table' then str = p.strFromTable(str) end return lang:formatDate('U', str) end

function p.unixNumber(str) if not str then return nil end return tonumber(p.unix(str)) end

function p.age(bday, death) if type(bday) == 'string' then bday = p.strToDate(bday) end for k, v in pairs(bday) do		bday[k] = tonumber(v) end if type(death) == 'string' then death = p.unixNumber(death) end if not bday.year or not bday.month or not bday.day then return end local now = os.date('*t', death) local bdaypast = now.month > bday.month or now.month == bday.month and now.day >= bday.day return now.year - bday.year - 1 + (bdaypast and 1 or 0) end

function p.getTimezones(str, init_tz, dst) if type(dst) == 'string' then dst = dst:lower end if not p.toUTC[dst] then error(('Invalid timezone value of %s. Allowed values: yes, no, spring, fall.'):format(dst)) end local delta = p.toUTC[dst][init_tz] local utc = lang:formatDate('Y-m-d H:i', ('%s %s hours'):format(str, h.printDelta(delta))) local tbl = { UTC = utc } for _, v in pairs(TZLIST) do		delta = -1 * p.toUTC[dst][v] tbl[v] = lang:formatDate('Y-m-d H:i', ('%s %s hours'):format(utc, h.printDelta(delta))) end tbl.UTC = utc return tbl end

function p.addTimezonesToRowFromUTC(row) -- overwrite a row's timezones to add PST, KST, and CET local timezones = p.getTimezones(row.UTC, 'UTC', row.DST) util_table.merge(row, timezones) end

function p.strFromTable(dt, format) local str = os.date('%Y-%m-%d', os.time(dt)) if not format then return str end return lang:formatDate(format, str) end

function p.getDatesInAWeek(date) local sunday = lang:formatDate('Y-m-d', ('%s - %s days'):format( date, lang:formatDate('w', date) ))	local tbl = {} for offset = 0, DAYS_PER_WEEK - 1 do		tbl[#tbl+1] = lang:formatDate('Y-m-d', ('%s + %s days'):format(sunday, offset)) end return tbl end

return p