Module:SBTE

-- SkyBlock Time Engine (MediaWiki Scribunto) -- A simplified version of its JavaScript implementation on MediaWiki:Gadget-SkyBlockTimeEngine.js -- This is a derivative of work licensed under the GNU General Public License v3.0 or later

local makeClass = require('Module:Class') local lang = mw.language.getContentLanguage

-- HELPERS AND CONSTANTS local h = {} h.LOCALES = { utc = 'U', sbst = 'S' } h.UNITS = { year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6 } h.RATIOS = { [6] = 1, -- one second [5] = 60, -- minute-to-second ratio [4] = 3600, -- hour-to-second ratio [3] = 86400, -- day-to-second ratio [2] = 2678400, -- month-to-second (=31 days) ratio [1] = 32140800 -- year-to-second (=372 days) ratio } h.MAGIC_RATIO = 72 h.UNIT_LIMITS = { -- note: VALUE in [LOWER, UPPER)	{ LOWER = 0, UPPER = nil },	{ LOWER = 0, UPPER = 12 },	{ LOWER = 1, UPPER = 32 },	{ LOWER = 0, UPPER = 24 },	{ LOWER = 0, UPPER = 60 },	{ LOWER = 0, UPPER = 60 } } h.SKYBLOCK_EPOCH = {	UNIX_TS_UTC = 1560275700,	UNIX_TS_SBST = 1560275700 * h.MAGIC_RATIO,	SKYBLOCK_TS_UTC = 0,	SKYBLOCK_TS_SBST = 0 } h.ALPHA_SKYBLOCK_EPOCH = {	UNIX_TS_UTC = 1560275700 - 211680,	UNIX_TS_SBST = (1560275700 - 211680) * h.MAGIC_RATIO,	SKYBLOCK_TS_UTC = 0,	SKYBLOCK_TS_SBST = 0 }

-- Shared Functions local function debugDurationString (self) -- For debugging only; Just throws the SBST and UTC durations respectively return table.concat({		self.sbstYears, self.sbstMonths, self.sbstDays,		self.sbstHours, self.sbstMinutes, self.sbstSeconds	}, ' ') .. '; ' .. lang:formatDuration(math.floor(self.duration / h.MAGIC_RATIO)) end local function debugDateString (self) -- For debugging only; Just throws the SBST and UTC dates respectively return table.concat({		self.sbstFullYear, self.sbstMonth, self.sbstDate,		self.sbstHour, self.sbstMinute, self.sbstSecond	}, ' ') .. '; ' .. lang:formatDate('r', '@' .. self.UNIX_TS_UTC) end

-- SKYDURATION local SkyDuration = makeClass('SkyDuration', {	constructor = function (self, settings)		self:setDuration(settings)	end,	setDuration = function (self, settings)		settings = settings or {}		self.locale = settings.locale or h.LOCALES.sbst		-- Calculate duration		local duration = (settings.second or 0) + (settings.minute or 0) * h.RATIOS[h.UNITS.minute] + (settings.hour or 0) * h.RATIOS[h.UNITS.hour] + (settings.day or 0) * h.RATIOS[h.UNITS.day]		if (self.locale == h.LOCALES.sbst) then			duration = duration + (settings.month or 0) * h.RATIOS[h.UNITS.month] + (settings.year or 0) * h.RATIOS[h.UNITS.year] -- month/year support for SBST		else			duration = duration * h.MAGIC_RATIO		end		self.duration = math.floor(duration)		-- SBST Duration Calculations		self.sbstSeconds = self.duration % h.RATIOS[h.UNITS.minute]		self.sbstMinutes = math.floor(self.duration % h.RATIOS[h.UNITS.hour] / h.RATIOS[h.UNITS.minute]) self.sbstHours = math.floor(self.duration % h.RATIOS[h.UNITS.day] / h.RATIOS[h.UNITS.hour]) self.sbstDays = math.floor(self.duration % h.RATIOS[h.UNITS.month] / h.RATIOS[h.UNITS.day]) self.sbstMonths = math.floor(self.duration % h.RATIOS[h.UNITS.year] / h.RATIOS[h.UNITS.month]) self.sbstYears = math.floor(self.duration / h.RATIOS[h.UNITS.year]) -- UTC Duration Calculations self.utc72thSecs = self.duration % h.MAGIC_RATIO local totalUtcSecs = math.floor(self.duration / h.MAGIC_RATIO) self.utcSeconds = totalUtcSecs % h.RATIOS[h.UNITS.minute] self.utcMinutes = math.floor(totalUtcSecs % h.RATIOS[h.UNITS.hour] / h.RATIOS[h.UNITS.minute]) self.utcHours = math.floor(totalUtcSecs % h.RATIOS[h.UNITS.day] / h.RATIOS[h.UNITS.hour]) self.utcDays = math.floor(totalUtcSecs / h.RATIOS[h.UNITS.day]) -- Computing Representation [years, months, days, hours, minutes, seconds] self.computing = { SBST = {self.sbstYears, self.sbstMonths, self.sbstDays, self.sbstHours, self.sbstMinutes, self.sbstSeconds}, UTC = {nil, nil, self.utcDays, self.utcHours, self.utcMinutes, self.utcSeconds, self.utc72thSecs} }		return self end, __tostring = debugDurationString, addUTCTime = function (self, unit, value) if (unit >= h.UNITS.day and unit <= h.UNITS.second) then self:addSBSTTime(h.UNITS.second, value * h.RATIOS[unit] * h.MAGIC_RATIO) end return self end, addSBSTTime = function (self, unit, value) if (unit >= h.UNITS.year and unit <= h.UNITS.second) then self:setDuration({ locale = h.LOCALES.sbst, second = self.duration + value * h.RATIOS[unit] }) end return self end })

-- SKYDATE local SkyDate = makeClass('SkyDate', {	constructor = function (self, settings)		self:setTime(settings)	end,	setTime = function (self, settings)		settings = settings or {}		if type(settings.skyDuration) == 'table' and SkyDuration:instanceof(settings.skyDuration) then			self.locale = h.LOCALES.sbst			self.duration = settings.skyDuration -- note: this must be a SkyDuration since SkyBlock Epoch		elseif type(settings.duration) == 'number' then			self.duration = self:durationFromUTCUnixTime(settings.duration)		else			self.locale = settings.locale or h.LOCALES.sbst			local meme = {settings.year, settings.month, settings.day, settings.hour, settings.minute, settings.second}			local current = self:currentTime			local most_significant			for i = 1, 6 do				if not most_significant then					if meme[i] then						most_significant = i -- on most significant					end					meme[i] = meme[i] or current[self.locale][i] else meme[i] = meme[i] or h.UNIT_LIMITS[i].LOWER end end if self.locale == h.LOCALES.utc then local ts = os.time({					year = meme[h.UNITS.year],					month = meme[h.UNITS.month] - 1,					day = meme[h.UNITS.day],					hour = meme[h.UNITS.hour],					min = meme[h.UNITS.minute],					sec = meme[h.UNITS.second],				}) self.duration = self:durationFromUTCUnixTime(ts) else self.duration = SkyDuration({					locale = h.LOCALES.sbst,					second = meme[h.UNITS.second] or 0, -- 0-60					minute = meme[h.UNITS.minute] or 0, -- 0-60					hour = meme[h.UNITS.hour] or 0, -- 0-60					day = meme[h.UNITS.day] - 1, -- 1-31					month = meme[h.UNITS.month], -- 0-11					year = meme[h.UNITS.year] - 1, -- 1-inf				}) end end -- UTC Date self.date = self:dateFromDuration(self.duration) -- SBST Date self.sbstFullYear = self.duration.sbstYears + 1 self.sbstMonth = self.duration.sbstMonths self.sbstDate = self.duration.sbstDays + 1 self.sbstHour = self.duration.sbstHours self.sbstMinute = self.duration.sbstMinutes self.sbstSecond = self.duration.sbstSeconds -- Make Representations -- Computing Representation [fullyear, monthindex, date, hour, minute, second] self.computing = { SBST = {self.sbstFullYear, self.sbstMonth, self.sbstDate, self.sbstHour, self.sbstMinute, self.sbstSecond}, UTC = {self.date.year, self.date.month - 1, self.date.day, self.date.hour, self.date.min, self.date.sec} }		-- Timestamps self.SKYBLOCK_TS_SBST = self.duration.duration -- The SKYBLOCK timestamp (seconds from SkyBlock Epoch) to this instance using SBST units self.SKYBLOCK_TS_UTC = math.floor(self.duration.duration / h.MAGIC_RATIO) -- The SKYBLOCK timestamp (seconds from SkyBlock Epoch) to this instance using UTC units self.UNIX_TS_SBST = self.EPOCH.UNIX_TS_SBST + self.SKYBLOCK_TS_SBST -- The UNIX Timestamp (seconds from Unix Epoch) to this instance using SBST units self.UNIX_TS_UTC = self.EPOCH.UNIX_TS_UTC + self.SKYBLOCK_TS_UTC -- The UNIX Timestamp (seconds from Unix Epoch) to this instance using UTC units return self end, setSBSTTimestamp = function (self, ts) -- ts in SBST seconds (not ms)! return self:setTime({skyDuration = SkyDuration({ locale = h.LOCALES.sbst, second = ts })}) end, setUTCTimestamp = function (self, ts) -- ts in UTC seconds (not ms)! return self:setTime({skyDuration = self:durationFromUTCUnixTime(ts)}) end, addDuration = function (self, sbstSeconds) self.duration:addSBSTTime(h.UNITS.second, sbstSeconds) return self:setTime({skyDuration = self.duration}) end, setSBSTTime = function (self, unit, value) if (unit >= h.UNITS.year and unit <= h.UNITS.second) then self.duration:addSBSTTime(unit, value - self.computing.SBST[unit]) end return self:setTime({skyDuration = self.duration}) end, setUTCTime = function (self, unit, value) if (unit >= h.UNITS.year and unit <= h.UNITS.second) then self.duration:addUTCTime(unit, value - self.computing.UTC[unit]) end return self:setTime({skyDuration = self.duration}) end, -- Tip: Use self.date to get UTC Time information -- See https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#os.date __tostring = debugDateString, -- Helpers currentTime = function (self) local currentDate = os.date('!*t') -- get current UTC time local currentSkyDuration = SkyDuration({ locale = h.LOCALES.utc, second = os.time(currentDate) - self.EPOCH.UNIX_TS_UTC }) local currentDuration = currentSkyDuration.computing.SBST local result = {} result[h.LOCALES.utc] = {currentDate.year, currentDate.month - 1, currentDate.day, currentDate.hour, currentDate.min, currentDate.sec} result[h.LOCALES.sbst] = {currentDuration[1] + 1, currentDuration[2], currentDuration[3] + 1, currentDuration[4], currentDuration[5], currentDuration[6]} return result end, durationFromUTCUnixTime = function (self, ts) return SkyDuration({ locale = h.LOCALES.utc, second = ts - self.EPOCH.UNIX_TS_UTC}) end, dateFromDuration = function (self, duration) return os.date('!*t', duration.duration / h.MAGIC_RATIO + self.EPOCH.UNIX_TS_UTC) end, prototype = { -- Items pushed to prototype for inheritance purposes EPOCH = h.SKYBLOCK_EPOCH } })

-- SKYDATEALPHA -- Note: Must call on .__tostring local SkyDateAlpha = makeClass('SkyDateAlpha', {	parent = SkyDate,	__tostring = debugDateString,	prototype = {		EPOCH = h.ALPHA_SKYBLOCK_EPOCH	} })

-- One cannot get a full class structure log from outside of this environment, -- so here is a way to do it local function logSkyDuration mw.logObject(SkyDuration) end local function logSkyDate mw.logObject(SkyDate) end local function logSkyDateAlpha mw.logObject(SkyDateAlpha) end

return { helpers = h,	SkyDuration = SkyDuration, SkyDate = SkyDate, SkyDateAlpha = SkyDateAlpha, logSkyDuration = logSkyDuration, logSkyDate = logSkyDate, logSkyDateAlpha = logSkyDateAlpha }