Module:Date time
Appearance
![]() | This module is rated as ready for general use. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
![]() | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
![]() | This Lua module is used on approximately 521,000 pages, or roughly 1% of all pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
![]() | This module depends on the following other modules: |
Module:Date time provides functions for validating and formatting dates in templates such as {{Start date}}, {{End date}}, {{Start date and age}}, and {{End date and age}}.
It handles:
- Validation of date components (year, month, day)
- Validation of time components (hour, minute, second)
- Timezone formatting and validation
- Generation of appropriate hCalendar microformat markup
- "time-ago" calculations
Returns an error message and category if validation fails.
Usage
{{#invoke:Date time|generate_date}}
{{#invoke:Date time|validate_date_time}}
Tracking categories
require("strict")
local p = {}
-- Declare `helpLink` and `errorCategory` as local variables outside of the functions
local helpLink
local errorCategory
-- This function checks if a given year is a leap year.
-- A year is a leap year if:
-- 1. It is divisible by 4, and
-- 2. It is not divisible by 100, unless
-- 3. It is also divisible by 400.
--
-- These rules ensure that a leap year occurs every 4 years, except for years divisible by 100,
-- unless they are also divisible by 400 (e.g., the year 2000 was a leap year, but 1900 was not).
--
-- Parameters:
-- year (number): The year to check for leap year status.
local function isLeapYear(year)
return (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)
end
-- This function returns the number of days in a given month of a specified year.
-- It handles leap years for the month of February.
--
-- The function uses the following logic:
-- 1. An array `daysInMonth` is defined to hold the typical number of days for each month (1 to 12).
-- 2. If the month is February (month == 2), the function checks if the year is a leap year.
-- If it's a leap year, February will have 29 days instead of 28.
-- 3. For other months, the function simply returns the days as defined in the `daysInMonth` array.
-- 4. If an invalid month is passed (i.e., not between 1 and 12), the function returns 0.
--
-- Parameters:
-- year (number): The year to check for leap year conditions.
-- month (number): The month (1-12) for which to return the number of days.
local function getDaysInMonth(year, month)
-- Days in each month: index corresponds to the month number (1 = January, 12 = December).
local daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
-- Check if February (month 2) and if it's a leap year to return 29 days
if month == 2 and isLeapYear(year) then
return 29
end
-- Return the number of days in the specified month, or 0 if the month is invalid
return daysInMonth[month] or 0
end
-- This function checks if a given value has unnecessary leading zeros.
-- It rejects values like "001", "005", etc., but allows "01", "05", "09", etc.
-- The function works by converting the value to a string and checking if it starts
-- with one or more zeros followed by one or more digits. Only values with more
-- than one leading zero (e.g., "001") are considered invalid.
--
-- Parameters:
-- value (string or number): The value to check for leading zeros.
local function hasLeadingZeros(value)
return tostring(value):match("^0%d%d") ~= nil
end
-- This function checks if a given value is an integer.
-- It returns true if the value is either nil (optional value) or a valid integer.
-- A valid integer is determined by converting the value to a number and checking if
-- the number is equal to its floor value (i.e., it has no decimal part).
--
-- Parameters:
-- value (string or number): The value to check if it's an integer.
local function isInteger(value)
if not value then
return false
end
-- Check if the value is nil or a valid number.
local numValue = tonumber(value)
return numValue and numValue == math.floor(numValue)
end
-- This helper function generates an error message wrapped in HTML.
-- It formats the error message in red text and appends both a help link
-- and an error category tag to the message. The help link and error category
-- are dynamic and based on the provided `templateName`.
--
-- Parameters:
-- message (string): The error message to format and return.
local function generateError(message)
return '<strong class="error">Error: ' .. message .. '</span> ' .. helpLink .. errorCategory
end
-- This function validates the date and time values provided in the `frame` argument.
-- It checks if the provided `year`, `month`, `day`, `hour`, `minute`, and `second` values
-- are integers, within valid ranges, and if they follow proper formatting.
-- It also ensures that the `df` parameter (if provided) is either "yes" or "y".
-- Additionally, it generates error messages for invalid inputs, including a help link
-- and an error category based on the `templateName` parameter (e.g., "start date" or "end date").
-- If all values are valid, it returns an empty string, indicating no errors.
function p.main(frame)
local getArgs = require("Module:Arguments").getArgs
local args = getArgs(frame)
-- Default the templateName to "start date" if not provided.
local templateName = args.template or "start date"
helpLink = string.format("<small>[[:Template:%s|(help)]]</small>", templateName)
errorCategory = string.format("[[Category:Pages using %s with invalid values]]", templateName)
-- Store all values in a table for later processing.
local dateTimeValues = {
year = args[1], month = args[2], day = args[3],
hour = args[4], minute = args[5], second = args[6]
}
-- Check if all values are integers (if they exist) and convert them to numbers only if necessary.
for key, value in pairs(dateTimeValues) do
-- Check if value is an integer, skip conversion if not valid.
if value and not isInteger(value) then
return generateError("All values must be integers")
end
-- If it's an integer, convert it to a number.
if value then
dateTimeValues[key] = tonumber(value)
end
end
-- A year value is always required.
if not dateTimeValues.year then
return generateError("Year value is required")
end
-- Check for leading zeros (only flag if inappropriate).
for _, value in pairs(dateTimeValues) do
if hasLeadingZeros(value) then
return generateError("Values cannot have unnecessary leading zeros")
end
end
-- Validate month (if provided).
if dateTimeValues.month and (dateTimeValues.month < 1 or dateTimeValues.month > 12) then
return generateError("Value is not a valid month")
end
-- Validate day (if provided).
if dateTimeValues.day then
local maxDay = getDaysInMonth(dateTimeValues.year, dateTimeValues.month)
if dateTimeValues.day < 1 or dateTimeValues.day > maxDay then
return generateError(string.format("Value is not a valid day (Month %d has %d days)", dateTimeValues.month, maxDay))
end
end
-- Validate hour (if provided).
if dateTimeValues.hour and (dateTimeValues.hour < 0 or dateTimeValues.hour > 23) then
return generateError("Value is not a valid hour")
end
-- Validate minute (if provided).
if dateTimeValues.minute and (dateTimeValues.minute < 0 or dateTimeValues.minute > 59) then
return generateError("Value is not a valid minute")
end
-- Validate second (if provided).
if dateTimeValues.second and (dateTimeValues.second < 0 or dateTimeValues.second > 59) then
return generateError("Value is not a valid second")
end
-- Validate df parameter (if provided).
if args.df and not (args.df == "yes" or args.df == "y") then
return generateError('df must be either "yes" or "y"')
end
-- If everything is valid, return an empty string.
return nil
end
return p