Time utility module feedback and ideas!

Hello Developers,

I’m currently working on a time manipulation module. This module will provide you with a few basic functions that you can use to handle time in-game and in real life. I’m just wondering that is there is anything I can do to improve this module, what should I add to the module to make it more useful, and what is a good name for this module. I know that the module is bad for some of you so that is why I’m posting this topic to get more ideas so that I can improve the module so that it can be more useful for developers when scripting anything that is related to in-game and real-life time.

Small API:

(Generated by ChatGPT, because I’m too lazy to write all of it)

  1. GetDateUnit
  • Parameters: GetType (string)
  • Parameter Type: GetType must be a string.
  • Functionality: Retrieves specific date components based on the value of GetType. It returns the day of the week, month, day of the month, year, or a combination of all date components.
  1. FormatSecondToTime
  • Parameters: Seconds (number)
  • Parameter Type: Seconds must be a number.
  • Functionality: Formats the given number of seconds into a human-readable format, representing the duration in terms of years, months, weeks, days, hours, minutes, and seconds.
  1. ConvertTimeUnitToSecond
  • Parameters: Time (number), UnitName (string)
  • Parameter Type: Time must be a number, UnitName must be a string.
  • Functionality: Converts the given time value from the specified unit (UnitName) to seconds. Supported units include seconds, minutes, hours, days, weeks, months, years, decades, centuries, and millennia.
  1. MarkTimestamp
  • Parameters: TimeMarkName (string)
  • Parameter Type: TimeMarkName must be a string.
  • Functionality: Marks the current timestamp with the specified name (TimeMarkName) for future reference.
  1. GetMarkedTimestamp
  • Parameters: TimeMarkName (string)
  • Parameter Type: TimeMarkName must be a string.
  • Functionality: Retrieves the timestamp marked with the specified name (TimeMarkName).
  1. UpdateMarkedTimestamp
  • Parameters: TimeMarkName (string)
  • Parameter Type: TimeMarkName must be a string.
  • Functionality: Updates the timestamp marked with the specified name (TimeMarkName) to the current time.
  1. ViewMarkedTimestamps
  • Parameters: None
  • Parameter Type: N/A
  • Functionality: Prints out all marked timestamps along with their corresponding names.
  1. GetElapsedSecondSinceTimestamp
  • Parameters: TimeMarkName (string)
  • Parameter Type: TimeMarkName must be a string.
  • Functionality: Calculates the number of seconds elapsed since the timestamp marked with the specified name (TimeMarkName).
  1. UnmarkTimestamp
  • Parameters: TimeMarkName (string)
  • Parameter Type: TimeMarkName must be a string.
  • Functionality: Removes the timestamp marked with the specified name (TimeMarkName) from the list of marked timestamps.

Module Source:

local TimeModule = {}

local MarkedTimestampNames = {}
local MarkedTimestamps = {}
local MarkTimestampDebounces = {}
local GetTimestampDebounces = {}

local SecondUnit = 1
local MinuteUnit = SecondUnit * 60
local HourUnit = MinuteUnit * 60
local DayUnit = HourUnit * 24
local WeekUnit = DayUnit * 7
local MonthUnit = DayUnit * 30
local YearUnit = DayUnit * 365
local DecadeUnit = YearUnit * 10
local CenturyUnit = DecadeUnit * 10
local MillenniumUnit = CenturyUnit * 10

local OutputMark = '[TimeUtilityModule]: '

local function CheckForYearAndMonthUnitUpdate()
	local Month = os.date('%B')
	local Year = os.date('%Y')

	local function isLeapYear()
		return Year % 4 == 0 and (Year % 100 ~= 0 or Year % 400 == 0)
	end

	local DaysInMonth = {
		['January'] = 31,
		['February'] = isLeapYear() and 29 or 28,
		['March'] = 31,
		['April'] = 30,
		['May'] = 31,
		['June'] = 30,
		['July'] = 31,
		['August'] = 31,
		['September'] = 30,
		['October'] = 31,
		['November'] = 30,
		['December'] = 31,
	}

	local TotalDaysInYear = 0
	for month, days in pairs(DaysInMonth) do
		if month == Month then
				break
		end
		TotalDaysInYear = TotalDaysInYear + days
	end

	local DaysInMonthResult = DaysInMonth[Month]
	local DaysInYearResult = TotalDaysInYear

	if DaysInMonthResult and DaysInYearResult then
		YearUnit = DaysInYearResult
		MonthUnit = DaysInMonthResult
	else
		warn(OutputMark..'Failed to update YearUnit and MonthUnit -> DaysInMonthResult: '..tostring(DaysInMonthResult)..', DaysInYearResult: '..tostring(DaysInYearResult))
	end
	print(OutputMark..'Finished checking for YearUnit and MonthUnit update!!')
end

CheckForYearAndMonthUnitUpdate()

function TimeModule:GetDateUnit(GetType)

	GetType = string.lower(GetType)

	local DayOfWeek = os.date('%A')
	local Month = os.date('%B')
	local DayOfMonth = os.date('%d')
	local Year = os.date('%Y')

	if GetType == 'dayofweek' then
		return DayOfWeek
	elseif GetType == 'month' then
		return Month
	elseif GetType == 'dayofmonth' then
		return DayOfMonth
	elseif GetType == 'year' then
		return Year
	elseif GetType == 'all' then
		return DayOfWeek..' '..Month..' '..DayOfMonth..' '..Year
	else
		error(OutputMark..'Invalid GetType. Please try again!!')
	end
end

function TimeModule:FormatSecondToTime(Seconds)
	Seconds = tonumber(Seconds)

	if Seconds < 0 then
		error(OutputMark..'Cannot format number less than 0!!')
		return
	elseif Seconds == math.huge then
		error(OutputMark..'Cannot format math.huge to time!!')
		return
	elseif Seconds == nil then
		error(OutputMark..'Argument #1 is not a number!')
		return
	end

	local time_units = {
			{MillenniumUnit, "millennium", "millennia"},
			{CenturyUnit, "century", "centuries"},
			{DecadeUnit, "decade", "decades"},
			{YearUnit, "year", "years"},
			{MonthUnit, "month", "months"},
			{WeekUnit, "week", "weeks"},
			{DayUnit, "day", "days"},
			{HourUnit, "hour", "hours"},
			{MinuteUnit, "minute", "minutes"},
			{SecondUnit, "second", "seconds"}
	}

	local result = {}

	for _, unit in ipairs(time_units) do
			local value = math.floor(Seconds / unit[1])
			Seconds = Seconds % unit[1]
			if value ~= 0 then
					table.insert(result, string.format("%d %s%s", value, unit[2], value == 1 and "" or "s"))
			end
	end

	if #result ~= 0 then
		return table.concat(result, " ")
	else
		return '0 seconds'
	end
end

function TimeModule:ConvertTimeUnitToSecond(Time, UnitName)
		UnitName = string.lower(UnitName)
		local seconds = 0

		if UnitName == 'second' or UnitName == 'seconds' then
				seconds = Time
		elseif UnitName == 'minute' or UnitName == 'minutes' then
				seconds = Time * MinuteUnit
		elseif UnitName == 'hour' or UnitName == 'hours' then
				seconds = Time * HourUnit
		elseif UnitName == 'day' or UnitName == 'days' then
				seconds = Time * DayUnit
		elseif UnitName == 'week' or UnitName == 'weeks' then
				seconds = Time * WeekUnit
		elseif UnitName == 'month' or UnitName == 'months' then
				seconds = Time * MonthUnit
		elseif UnitName == 'year' or UnitName == 'years' then
				seconds = Time * YearUnit
		elseif UnitName == 'decade' or UnitName == 'decades' then
				seconds = Time * DecadeUnit
		elseif UnitName == 'century' or UnitName == 'centuries' then
				seconds = Time * CenturyUnit
		elseif UnitName == 'millennium' or UnitName == 'millennia' then
				seconds = Time * MillenniumUnit
		else
				warn(OutputMark.."Invalid time unit name!!")
		end

		return seconds
end

function TimeModule:MarkTimestamp(TimeMarkName)
	if type(TimeMarkName) ~= 'string' then
		error(OutputMark..'Argument #1 expect to be a string not anything else!!')
		return
	end

	if not MarkTimestampDebounces[TimeMarkName] then
		MarkedTimestamps[TimeMarkName] = os.time()
		table.insert(MarkedTimestampNames, TimeMarkName)
		MarkTimestampDebounces[TimeMarkName] = true
	end
end

function TimeModule:GetMarkedTimestamp(TimeMarkName)
	if type(TimeMarkName) ~= 'string' then
		error(OutputMark..'Argument #1 expect to be a string not anything else!!')
		return
	end
	
	return MarkedTimestamps[TimeMarkName]
end

function TimeModule:UpdateMarkedTimestamp(TimeMarkName)
	MarkedTimestamps[TimeMarkName] = os.time()
end

function TimeModule:ViewMarkedTimestamps()
	coroutine.wrap(function()
			for _, name in ipairs(MarkedTimestampNames) do
					print(name .. ': ' .. (MarkedTimestamps[name] or 'No timestamp found in list!!'))
			end
	end)()
end

function TimeModule:GetElapsedSecondSinceTimestamp(TimeMarkName)
		local CurrentTimestamp = os.time()
		local MarkedTimestamp = MarkedTimestamps[TimeMarkName]

		if MarkedTimestamp then
				return CurrentTimestamp - MarkedTimestamp
		else
				print(OutputMark..'Timestamp can not be found when calculating elapsed seconds!')
				return nil
		end
end

function TimeModule:UnmarkTimestamp(TimeMarkName)
		local Timestamp = MarkedTimestamps[TimeMarkName]
		local TimestampName = MarkedTimestampNames[TimeMarkName]
		local MarkTimestampDebounce = MarkTimestampDebounces[TimeMarkName]
		local GetTimestampDebounce = MarkTimestampDebounces[TimeMarkName]

		if Timestamp then
			Timestamp = nil
			TimestampName = nil
			MarkTimestampDebounce = nil
			GetTimestampDebounce = nil
			print(OutputMark..'Successfully un-marked time stamp!!')
		else
			print(OutputMark..'Cannot find time stamp in the list to remove!!')
		end
end

return TimeModule