local F={}F.__index=F local G=(game:GetService("\082\117\110\083\101\114\118\105\099\101")):IsServer()local h={"\083\117\110\100\097\121";"\077\111\110\100\097\121";"\084\117\101\115\100\097\121";"\087\101\100\110\101\115\100\097\121";"\084\104\117\114\115\100\097\121","\070\114\105\100\097\121";"\083\097\116\117\114\100\097\121"}local l={"\083\117\110","\077\111\110";"\084\117\101";"\087\101\100";"\084\104\117","\070\114\105";"\083\097\116"}local d={"\074\097\110\117\097\114\121";"\070\101\098\114\117\097\114\121","\077\097\114\099\104";"\065\112\114\105\108";"\077\097\121","\074\117\110\101";"\074\117\108\121";"\065\117\103\117\115\116";"\083\101\112\116\101\109\098\101\114","\079\099\116\111\098\101\114";"\078\111\118\101\109\098\101\114";"\068\101\099\101\109\098\101\114"}local o={"\074\097\110","\070\101\098","\077\097\114";"\065\112\114";"\077\097\121";"\074\117\110";"\074\117\108";"\065\117\103";"\083\101\112";"\079\099\116","\078\111\118";"\068\101\099"}function CopyTable(F)local G={}for F,h in pairs(F)do G[F]=h end return G end function F.new(h,l)if h~=nil then assert(type(h)=="\110\117\109\098\101\114","\'\115\101\099\111\110\100\115\' \097\114\103\117\109\101\110\116 \035\049 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")else h=tick()end local d=G if l~=nil then d=l end local o=os.date(d and"\033\042\116"or"\042\116",h)local O=setmetatable({Hour=o.hour;Minute=o.min;Weekday=o.wday;Day=o.day,Month=o.month;Year=o.year;Second=o.sec;Millisecond=math.floor((h%1)*1000);Yearday=o.yday;IsDST=o.isdst;_d=o,_s=h},F)return O end function F.fromJSON(G)assert(type(G)=="\115\116\114\105\110\103","\'\106\115\111\110\083\116\114\' \097\114\103\117\109\101\110\116 \035\049 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103")local h,l=pcall(function()return(game:GetService("\072\116\116\112\083\101\114\118\105\099\101")):JSONDecode(G)end)if not h then error("\070\097\105\108\101\100 \116\111 \100\101\099\111\100\101 \074\083\079\078 \115\116\114\105\110\103\058 "..tostring(l))end local d if l._s then d=l._s else d=os.time(l)end return F.new(d)end function F.ToJSON(F)local G=CopyTable(F._d)G._s=F._s end function F.ToSeconds(F)return F._s end function F.GetTimezoneHourOffset(F)local G=os.date("\033\042\116",F._s)return F._d.hour-G.hour end function F.ToISOString(F)local G=F:ToUTC()local h=G._d return("\037\046\050\105-\037\046\050\105-\037\046\050\105\084\037\046\050\105\058\037\046\050\105\058\037\046\050\105\046\037\046\051\105"):format(h.year,h.month,h.day,h.hour,h.min,h.sec,math.floor((G._s%1)*1000))end function F.ToDateString(F)local G=F._d return("\037\115 \037\115 \037\105 \037\105"):format(l[G.wday],o[G.month],G.day,G.year)end function F.ToTimeString(F)local G=F._d return("\037\046\050\105\058\037\046\050\105\058\037\046\050\105"):format(G.hour,G.min,G.sec)end function F.ToString(F)return F:ToDateString()..(" "..F:ToTimeString())end function F.ToUTC(G)return F.new(G._s,true)end function F.ToLocal(G)return F.new(G._s,false)end function F.Format(G,F)local O=G._d local y=O.hour if y>12 then y=y-12 end if y==0 then y=0 end F=(((((((((((((((((((((((((((F:gsub("\037\037\097",l[O.wday])):gsub("\037\037\065",h[O.wday])):gsub("\037\037\098",o[O.month])):gsub("\037\037\066",d[O.month])):gsub("\037\037\099",G:ToString())):gsub("\037\037\067",(O.year-O.year%1000)/100+1)):gsub("\037\037\100",("\037\046\050\105"):format(O.day))):gsub("\037\037\068",("\037\046\050\105\047\037\046\050\105\047\037\115"):format(O.month,O.day,(tostring(O.year)):sub(-2)))):gsub("\037\037\070",("\037\105-\037\046\050\105-\037\046\050\105"):format(O.year,O.month,O.day))):gsub("\037\037\072",("\037\046\050\105"):format(O.hour))):gsub("\037\037\107",("\037\046\050\105"):format(O.hour))):gsub("\037\037\073",("\037\046\050\105"):format(y))):gsub("\037\037\108",("\037\046\050\105"):format(y))):gsub("\037\037\106",("\037\046\051\105"):format(O.yday))):gsub("\037\037\109",("\037\046\050\105"):format(O.month))):gsub("\037\037\077",("\037\046\050\105"):format(O.min))):gsub("\037\037\110","\n")):gsub("\037\037\112",O.hour>=12 and"\080\077"or"\065\077")):gsub("\037\037\080",O.hour>=12 and"\112\109"or"\097\109")):gsub("\037\037\114",("\037\046\050\105\058\037\046\050\105\058\037\046\050\105 \037\115"):format(y,O.min,O.sec,O.hour>=12 and"\080\077"or"\065\077"))):gsub("\037\037\082",("\037\046\050\105\058\037\046\050\105"):format(O.hour,O.min))):gsub("\037\037\115",math.floor(G._s))):gsub("\037\037\083",("\037\046\050\105"):format(O.sec))):gsub("\037\037\116","\t")):gsub("\037\037\084",("\037\046\050\105\058\037\046\050\105\058\037\046\050\105"):format(O.hour,O.min,O.sec))):gsub("\037\037\119",("\037\046\050\105"):format(O.wday))):gsub("\037\037\121",(tostring(O.year)):sub(-2))):gsub("\037\037\089",tostring(O.year))return F end F.New=F.new F.FromJSON=F.fromJSON F.__tostring=F.ToString F.__metatable="\108\111\099\107\101\100"function F.__lt(F,G)return F._s<G._s end function F.__le(F,G)return F._s<=G._s end function F.__eq(F,G)return F._s==G._s end function F.__unm(G)return F.new(-G._s)end return F
-- Date
-- Crazyman32
-- September 12, 2017

--[=[

	Represents a date at a specific time. On the server, this will
	return UTC time. On the client, this will return local time.
	Note that the server-side in Play-Solo testing will also return
	local time.

	You can optionally force UTC within the Date.new constructor.


	REQUIRE:

		local Date = require(thisModule)

	
	CONSTRUCTORS:

		local date = Date.new([seconds [, useUtc]])
		local date = Date.fromJSON(jsonString)


	METHODS:

		date:ToJSON()
		date:ToSeconds()
		date:GetTimezoneHourOffset()
		date:Format(strFormat)
		date:ToUTC()
		date:ToLocal()
		date:ToISOString()
		date:ToDateString()
		date:ToTimeString()
		date:ToString()


	PROPERTIES:

		date.Hour
		date.Minute
		date.Weekday
		date.Day
		date.Month
		date.Year
		date.Second
		date.Millisecond
		date.Yearday
		date.IsDST


	NOTE ON SAVING:

		You should use 'date:ToSeconds()' for saving. It can
		represent the date in the smallest format. While using
		'date:ToJSON()' will work too, it has a higher data
		footprint. Example:

		SAVE:

			local date = Date.new()
			dataStore:SetAsync("myDate", date:ToSeconds())

		LOAD:

			local myDateSeconds = dataStore:GetAsync("myDate")
			local date = Date.new(myDateSeconds)

	

--]=]



local Date = {}
Date.__index = Date


local useUTC = game:GetService("RunService"):IsServer()


local WEEKDAYS = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
local WEEKDAYS_SHORT = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}

local MONTHS = {"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"}


-- Single-level table copy:
function CopyTable(t)
	local tCopy = {}
	for k,v in pairs(t) do
		tCopy[k] = v
	end
	return tCopy
end


function Date.new(seconds, useUtcOverride)

	if (seconds ~= nil) then
		assert(type(seconds) == "number", "'seconds' argument #1 must be a number")
	else
		seconds = tick()
	end

	local utc = useUTC
	if (useUtcOverride ~= nil) then
		utc = useUtcOverride
	end

	local d = os.date(utc and "!*t" or "*t", seconds)

	local self = setmetatable({
		Hour = d.hour;
		Minute = d.min;
		Weekday = d.wday;
		Day = d.day;
		Month = d.month;
		Year = d.year;
		Second = d.sec;
		Millisecond = math.floor((seconds % 1) * 1000);
		Yearday = d.yday;
		IsDST = d.isdst;
		_d = d;
		_s = seconds;
	}, Date)

	return self

end


function Date.fromJSON(jsonStr)
	assert(type(jsonStr) == "string", "'jsonStr' argument #1 must be a string")
	local success, data = pcall(function()
		return game:GetService("HttpService"):JSONDecode(jsonStr)
	end)
	if (not success) then
		error("Failed to decode JSON string: " .. tostring(data))
	end
	local seconds
	if (data._s) then
		seconds = data._s
	else
		seconds = os.time(data)
	end
	return Date.new(seconds)
end


function Date:ToJSON()
	local data = CopyTable(self._d)
	data._s = self._s
end


function Date:ToSeconds()
	return self._s
end


function Date:GetTimezoneHourOffset()
	local dUTC = os.date("!*t", self._s)
	return (self._d.hour - dUTC.hour)
end


function Date:ToISOString()
	local utc = self:ToUTC()
	local d = utc._d
	return ("%.2i-%.2i-%.2iT%.2i:%.2i:%.2i.%.3i"):format(
		d.year,
		d.month,
		d.day,
		d.hour,
		d.min,
		d.sec,
		math.floor((utc._s % 1) * 1000)
	)
end


function Date:ToDateString()
	local d = self._d
	return ("%s %s %i %i"):format(
		WEEKDAYS_SHORT[d.wday],
		MONTHS_SHORT[d.month],
		d.day,
		d.year
	)
end


function Date:ToTimeString()
	local d = self._d
	return ("%.2i:%.2i:%.2i"):format(
		d.hour,
		d.min,
		d.sec
	)
end


function Date:ToString()
	return (self:ToDateString() .. " " .. self:ToTimeString())
end


function Date:ToUTC()
	return Date.new(self._s, true)
end


function Date:ToLocal()
	return Date.new(self._s, false)
end


-- See GNU date commands:
-- https://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/
function Date:Format(str)
	local d = self._d
	local h12 = d.hour
	if (h12 > 12) then
		h12 = h12 - 12
	end
	if (h12 == 0) then
		h12 = 0
	end
	str = str
		:gsub("%%a", WEEKDAYS_SHORT[d.wday])
		:gsub("%%A", WEEKDAYS[d.wday])
		:gsub("%%b", MONTHS_SHORT[d.month])
		:gsub("%%B", MONTHS[d.month])
		:gsub("%%c", self:ToString())
		:gsub("%%C", ((d.year - (d.year % 1000)) / 100) + 1)
		:gsub("%%d", ("%.2i"):format(d.day))
		:gsub("%%D", ("%.2i/%.2i/%s"):format(d.month, d.day, tostring(d.year):sub(-2)))
		:gsub("%%F", ("%i-%.2i-%.2i"):format(d.year, d.month, d.day))
		:gsub("%%H", ("%.2i"):format(d.hour))
		:gsub("%%k", ("%.2i"):format(d.hour))
		:gsub("%%I", ("%.2i"):format(h12))
		:gsub("%%l", ("%.2i"):format(h12))
		:gsub("%%j", ("%.3i"):format(d.yday))
		:gsub("%%m", ("%.2i"):format(d.month))
		:gsub("%%M", ("%.2i"):format(d.min))
		:gsub("%%n", "\n")
		:gsub("%%p", (d.hour >= 12 and "PM" or "AM"))
		:gsub("%%P", (d.hour >= 12 and "pm" or "am"))
		:gsub("%%r", ("%.2i:%.2i:%.2i %s"):format(h12, d.min, d.sec, (d.hour >= 12 and "PM" or "AM")))
		:gsub("%%R", ("%.2i:%.2i"):format(d.hour, d.min))
		:gsub("%%s", math.floor(self._s))
		:gsub("%%S", ("%.2i"):format(d.sec))
		:gsub("%%t", "\t")
		:gsub("%%T", ("%.2i:%.2i:%.2i"):format(d.hour, d.min, d.sec))
		:gsub("%%w", ("%.2i"):format(d.wday))
		:gsub("%%y", tostring(d.year):sub(-2))
		:gsub("%%Y", tostring(d.year))
	return str

end


Date.New = Date.new
Date.FromJSON = Date.fromJSON
Date.__tostring = Date.ToString
Date.__metatable = "locked"


function Date.__lt(d1, d2)
	return (d1._s < d2._s)
end


function Date.__le(d1, d2)
	return (d1._s <= d2._s)
end


function Date.__eq(d1, d2)
	return (d1._s == d2._s)
end


function Date.__unm(d)
	return Date.new(-d._s)
end


return Date