Module:Time ago: Difference between revisions

From NvWiki
Jump to navigation Jump to search
m Archiving 1 discussion(s) to Template talk:Time ago/Archive 1) (bot
m 1 revision imported: Import modules used with Template:Infobox software
Line 1: Line 1:
{{talk header}}
-- Implement [[Template:Time ago]]
{{WikiProject banner shell|
{{WikiProject Time}}
}}
{{User:MiszaBot/config
| algo=old(90d)
| archive=Template talk:Time ago/Archive %(counter)d
| counter=1
| maxarchivesize=900K
| archiveheader={{aan}}
| minthreadsleft=2
| minthreadstoarchive=1
}}
== Unnatural "0 days ago" and "0 days' time" ==


These are very small and short-lived problems, but I did notice the "0 days ago" on Benedict XVI's article today.
local numberSpell, yesno  -- lazy load


<code><nowiki>{{time ago|{{#time:Y|now}}-{{#time:n|now}}-{{#time:j|now}}|min_magnitude=days}}</nowiki></code> -> {{time ago|{{#time:Y|now}}-{{#time:n|now}}-{{#time:j|now}}|min_magnitude=days}}
function numberSpell(arg)
numberSpell = require('Module:NumberSpell')._main
return numberSpell(arg)
end


<code><nowiki>{{time ago|{{#time:Y|now+1 days}}-{{#time:n|now+1 days}}-{{#time:j|now+1 days}}|min_magnitude=days}}</nowiki></code> -> {{time ago|{{#time:Y|now+1 days}}-{{#time:n|now+1 days}}-{{#time:j|now+1 days}}|min_magnitude=days}}
function yesno(arg)
yesno = require('Module:Yesno')
return yesno(arg)
end


<code><nowiki>{{time ago|-0 years|min_magnitude=years}}</nowiki></code> -> {{time ago|-0 years|min_magnitude=years}}
local p = {}


<code><nowiki>{{time ago|-0 months|min_magnitude=months}}</nowiki></code> -> {{time ago|-0 months|min_magnitude=months}}
-- Table to convert entered text values to numeric values.
local timeText = {
['seconds'] = 1,
['minutes'] = 60,
['hours'] = 3600,
['days'] = 86400,
['weeks'] = 604800,
['months'] = 2629800,  -- 365.25 * 24 * 60 * 60 / 12
['years'] = 31557600
}


<code><nowiki>{{time ago|-0 hours|min_magnitude=hours}}</nowiki></code> -> {{time ago|-0 hours|min_magnitude=hours}}
-- Table containing tables of possible units to use in output.
local timeUnits = {
[1] = { 'second', 'seconds', "second's", "seconds'" },
[60] = { 'minute', 'minutes', "minutes'", "minutes'" },
[3600] = { 'hour', 'hours', "hour's", "hours'" },
[86400] = { 'day', 'days', "day's", "days'" },
[604800] = { 'week', 'weeks', "week's", "weeks'", unit = 'w' },
[2629800] = { 'month', 'months', "month's", "months'", unit = 'm'  },
[31557600] = { 'year', 'years', "year's", "years'", unit = 'y'  }
}


<code><nowiki>{{time ago|-0 minutes|min_magnitude=minutes}}</nowiki></code> -> {{time ago|-0 minutes|min_magnitude=minutes}}
function p._main( args )
-- Initialize variables
local lang = mw.language.getContentLanguage()
local auto_magnitude_num
local min_magnitude_num
local magnitude = args.magnitude
local min_magnitude = args.min_magnitude
local purge = args.purge


<code><nowiki>{{time ago|-0 seconds|min_magnitude=seconds}}</nowiki></code> -> {{time ago|-0 seconds|min_magnitude=seconds}}
-- Add a purge link if something (usually "yes") is entered into the purge parameter
if purge then
purge = ' <span class="plainlinks">([' .. mw.title.getCurrentTitle():fullUrl('action=purge') .. ' purge])</span>'
else
purge = ''
end


I think all these "0 X's ago"s are unnatural, like they seem very robotic or procedural.
-- Check that the entered timestamp is valid. If it isn't, then give an error message.
local success, inputTime = pcall( lang.formatDate, lang, 'xnU', args[1] )
if not success then
return '<strong class="error">Error: first parameter cannot be parsed as a date or time.</strong>'
end


So I propose this template simply prints out "today" and "tomorrow". And for completeness, do "this year" for 0 years ago, "this month" for 0 months, etc. And then maybe "right now" for 0 seconds ago. <span style="color: #0645AD;">[[User:Brightgalrs|'''Brightgalrs''']] (/braɪtˈɡæl.ərˌɛs/)<sup>[[User talk:Brightgalrs|[ᴛ]]]</sup></span> 17:22, 31 December 2022 (UTC)
-- Store the difference between the current time and the inputted time, as well as its absolute value.
local timeDiff = lang:formatDate( 'xnU' ) - inputTime
local absTimeDiff = math.abs( timeDiff )


:Closer to the programmer mindset is the complete omission of output in case of 0. What do the maintainers have to say ({{reply to|Mr._Stradivarius|Johnuniq|Thryduulf}})? [[User:Nxavar|Nxavar]] ([[User talk:Nxavar|talk]]) 14:37, 19 July 2024 (UTC)
if magnitude then
::And even closer to the programmer mindset is leaving it as-in. Having no output would look even weirder. Take the [[Pope Benedict XVI]] example, you'd have "Died" and then an empty space in his infobox. <span style="color: #0645AD;">[[User:Brightgalrs|'''Brightgalrs''']] (/braɪtˈɡæl.ərˌɛs/)<sup>[[User talk:Brightgalrs|[ᴛ]]]</sup></span> 13:58, 20 July 2024 (UTC)
auto_magnitude_num = 0
:Perhaps something like "less than a (unit)" (with appropriate adjustment to the article for units starting with a vowel sound). [[User:Isaacl|isaacl]] ([[User talk:Isaacl|talk]]) 14:12, 20 July 2024 (UTC)
min_magnitude_num = timeText[magnitude]
::Yeah that might be best. But I think you'd still get some weird things. For instance, imagine you set it to display years, and then someone dies, and you look at the article the same day. You'd get "Died" and then "Less than a year ago". Technically true, but extremely awkward, no one would ever say that. On the other hand, "this year" feels more universal - you could say that the day after someone died without any raised eyebrows. There's something with English semantics going on here, it's very hard to wrap my head around. <span style="color: #0645AD;">[[User:Brightgalrs|'''Brightgalrs''']] (/braɪtˈɡæl.ərˌɛs/)<sup>[[User talk:Brightgalrs|[ᴛ]]]</sup></span> 14:26, 24 July 2024 (UTC)
else
:::The {{para|min_magnitude}} parameter can be set to days for that case. [[User:Isaacl|isaacl]] ([[User talk:Isaacl|talk]]) 14:33, 24 July 2024 (UTC)
-- Calculate the appropriate unit of time if it was not specified as an argument.
::::My point is "this year", and the like, is a general case that would cover all cases, even ones that can't be foreseen. I've got birthdays on my mind since it's what sparked this whole discussion, but this template is used for many things. <span style="color: #0645AD;">[[User:Brightgalrs|'''Brightgalrs''']] (/braɪtˈɡæl.ərˌɛs/)<sup>[[User talk:Brightgalrs|[ᴛ]]]</sup></span> 12:26, 8 August 2024 (UTC)
local autoMagnitudeData = {
:::::@[[User:Brightgalrs|Brightgalrs]], I like the "this year" idea.  It might also be possible to adjust the output based on the precision of the date you're given:  If you're given the <nowiki>{{CURRENTYEAR}}</nowiki>, then zero produces an output of "this year", but if you're given the date down to <nowiki>{{CURRENTMONTH}}</nowiki>, then it's "this month", and a <nowiki>{{CURRENTDAY}}</nowiki>-level of precision could return "today". [[User:WhatamIdoing|WhatamIdoing]] ([[User talk:WhatamIdoing|talk]]) 04:27, 20 October 2024 (UTC)
{ factor = 2, amn = 31557600 },
{ factor = 2, amn = 2629800 },
{ factor = 2, amn = 86400 },
{ factor = 2, amn = 3600 },
{ factor = 2, amn = 60 }
}
for _, t in ipairs( autoMagnitudeData ) do
if absTimeDiff / t.amn >= t.factor then
auto_magnitude_num = t.amn
break
end
end
auto_magnitude_num = auto_magnitude_num or 1
if min_magnitude then
min_magnitude_num = timeText[min_magnitude]
else
min_magnitude_num = -1
end
end


== Use of this template in article body text? ==
if not min_magnitude_num then
-- Default to seconds if an invalid magnitude is entered.
min_magnitude_num = 1
end


I just encountered this in the body of [[Valley Children's Stadium|an article]] for the first time. I had already spotted several [[WP:RELTIME]] problems in the article, so my first assumption before I opened it for editing was that this was another one (even though the math checked out):
local result_num
local magnitude_num = math.max( min_magnitude_num, auto_magnitude_num )
local unit = timeUnits[magnitude_num].unit
if unit and absTimeDiff >= 864000 then
local Date = require('Module:Date')._Date
local input = lang:formatDate('Y-m-d H:i:s', args[1])  -- Date needs a clean date
input = Date(input)
if input then
local id
if input.hour == 0 and input.minute == 0 then
id = 'currentdate'
else
id = 'currentdatetime'
end
result_num = (Date(id) - input):age(unit)
end
end
result_num = result_num or math.floor ( absTimeDiff / magnitude_num )


: ''FieldTurf was installed {{time ago|2011}} in the summer of 2011...''
local punctuation_key, suffix
if timeDiff >= 0 then -- Past
if result_num == 1 then
punctuation_key = 1
else
punctuation_key = 2
end
if args.ago == '' then
suffix = ''
else
suffix = ' ' .. (args.ago or 'ago')
end
else -- Future
if args.ago == '' then
suffix = ''
if result_num == 1 then
punctuation_key = 1
else
punctuation_key = 2
end
else
suffix = ' time'
if result_num == 1 then
punctuation_key = 3
else
punctuation_key = 4
end
end
end
local result_unit = timeUnits[ magnitude_num ][ punctuation_key ]


I feel like using this template in this way is problematic; it's a little jarring to have the article be self-aware like this, and I'm guessing it will bug other RELTIME-aware editors. It's at the very least redundant -- yes, 2011 was indeed {{time ago|2011}}, tyvm for doing the math for me.
-- Convert numerals to words if appropriate.
local spell_out = args.spellout
local spell_out_max = tonumber(args.spelloutmax)
local result_num_text
if spell_out and (
( spell_out == 'auto' and 1 <= result_num and result_num <= 9 and result_num <= ( spell_out_max or 9 ) ) or
( yesno( spell_out ) and 1 <= result_num and result_num <= 100 and result_num <= ( spell_out_max or 100 ) )
)
then
result_num_text = numberSpell( result_num )
else
result_num_text = tostring( result_num )
end
-- numeric or string
local numeric_out = args.numeric
local result = ""
if numeric_out then
result = tostring( result_num )
else
result = result_num_text .. ' ' .. result_unit .. suffix -- Spaces for suffix have been added in earlier.
end


Because of this I'd like to add something to the template doc discouraging its use in body prose (excluding well-defined areas such as infoboxes). Any objections? [[User:NapoliRoma|NapoliRoma]] ([[User talk:NapoliRoma|talk]]) 21:00, 23 November 2024 (UTC)
return result .. purge
end
 
function p.main( frame )
local args = require( 'Module:Arguments' ).getArgs( frame, {
valueFunc = function( k, v )
if v then
v = v:match( '^%s*(.-)%s*$' ) -- Trim whitespace.
if k == 'ago' or v ~= '' then
return v
end
end
return nil
end,
wrappers = 'Template:Time ago'
})
return p._main( args )
end
 
return p

Revision as of 16:56, 26 December 2025

Template:Module rating

The module “Time ago” implements {{Time ago}}. See the template's page for documentation. Test cases can be found at Template:Time ago/testcases.


-- Implement [[Template:Time ago]]

local numberSpell, yesno  -- lazy load

function numberSpell(arg)
	numberSpell = require('Module:NumberSpell')._main
	return numberSpell(arg)
end

function yesno(arg)
	yesno = require('Module:Yesno')
	return yesno(arg)
end

local p = {}

-- Table to convert entered text values to numeric values.
local timeText = {
	['seconds'] = 1,
	['minutes'] = 60,
	['hours'] = 3600,
	['days'] = 86400,
	['weeks'] = 604800,
	['months'] = 2629800,  -- 365.25 * 24 * 60 * 60 / 12
	['years'] = 31557600
}

-- Table containing tables of possible units to use in output.
local timeUnits = {
	[1] = { 'second', 'seconds', "second's", "seconds'" },
	[60] = { 'minute', 'minutes', "minutes'", "minutes'" },
	[3600] = { 'hour', 'hours', "hour's", "hours'" },
	[86400] = { 'day', 'days', "day's", "days'" },
	[604800] = { 'week', 'weeks', "week's", "weeks'", unit = 'w' },
	[2629800] = { 'month', 'months', "month's", "months'", unit = 'm'  },
	[31557600] = { 'year', 'years', "year's", "years'", unit = 'y'  }
}

function p._main( args )
	-- Initialize variables
	local lang = mw.language.getContentLanguage()
	local auto_magnitude_num
	local min_magnitude_num
	local magnitude = args.magnitude
	local min_magnitude = args.min_magnitude
	local purge = args.purge

	-- Add a purge link if something (usually "yes") is entered into the purge parameter
	if purge then
		purge = ' <span class="plainlinks">([' .. mw.title.getCurrentTitle():fullUrl('action=purge') .. ' purge])</span>'
	else
		purge = ''
	end

	-- Check that the entered timestamp is valid. If it isn't, then give an error message.
	local success, inputTime = pcall( lang.formatDate, lang, 'xnU', args[1] )
	if not success then
		return '<strong class="error">Error: first parameter cannot be parsed as a date or time.</strong>'
	end

	-- Store the difference between the current time and the inputted time, as well as its absolute value.
	local timeDiff = lang:formatDate( 'xnU' ) - inputTime
	local absTimeDiff = math.abs( timeDiff )

	if magnitude then
		auto_magnitude_num = 0
		min_magnitude_num = timeText[magnitude]
	else
		-- Calculate the appropriate unit of time if it was not specified as an argument.
		local autoMagnitudeData = {
			{ factor = 2, amn = 31557600 },
			{ factor = 2, amn = 2629800 },
			{ factor = 2, amn = 86400 },
			{ factor = 2, amn = 3600 },
			{ factor = 2, amn = 60 }
		}
		for _, t in ipairs( autoMagnitudeData ) do
			if absTimeDiff / t.amn >= t.factor then
				auto_magnitude_num = t.amn
				break
			end
		end
		auto_magnitude_num = auto_magnitude_num or 1
		if min_magnitude then
			min_magnitude_num = timeText[min_magnitude]
		else
			min_magnitude_num = -1
		end
	end

	if not min_magnitude_num then
		-- Default to seconds if an invalid magnitude is entered.
		min_magnitude_num = 1
	end

	local result_num
	local magnitude_num = math.max( min_magnitude_num, auto_magnitude_num )
	local unit = timeUnits[magnitude_num].unit
	if unit and absTimeDiff >= 864000 then
		local Date = require('Module:Date')._Date
		local input = lang:formatDate('Y-m-d H:i:s', args[1])  -- Date needs a clean date
		input = Date(input)
		if input then
			local id
			if input.hour == 0 and input.minute == 0 then
				id = 'currentdate'
			else
				id = 'currentdatetime'
			end
			result_num = (Date(id) - input):age(unit)
		end
	end
	result_num = result_num or math.floor ( absTimeDiff / magnitude_num )

	local punctuation_key, suffix
	if timeDiff >= 0 then -- Past
		if result_num == 1 then
			punctuation_key = 1
		else
			punctuation_key = 2
		end
		if args.ago == '' then
			suffix = ''
		else
			suffix = ' ' .. (args.ago or 'ago')
		end
	else -- Future
		if args.ago == '' then
			suffix = ''
			if result_num == 1 then
				punctuation_key = 1
			else
				punctuation_key = 2
			end
		else
			suffix = ' time'
			if result_num == 1 then
				punctuation_key = 3
			else
				punctuation_key = 4
			end
		end
	end
	local result_unit = timeUnits[ magnitude_num ][ punctuation_key ]

	-- Convert numerals to words if appropriate.
	local spell_out = args.spellout
	local spell_out_max = tonumber(args.spelloutmax)
	local result_num_text
	if spell_out and (
		( spell_out == 'auto' and 1 <= result_num and result_num <= 9 and result_num <= ( spell_out_max or 9 ) ) or
		( yesno( spell_out ) and 1 <= result_num and result_num <= 100 and result_num <= ( spell_out_max or 100 ) )
		)
	then
		result_num_text = numberSpell( result_num )
	else
		result_num_text = tostring( result_num )
	end
	
	-- numeric or string
	local numeric_out = args.numeric
	local result = ""
	if numeric_out then
		result = tostring( result_num )
	else
		result = result_num_text .. ' ' .. result_unit .. suffix -- Spaces for suffix have been added in earlier.
	end

	return result .. purge
end

function p.main( frame )
	local args = require( 'Module:Arguments' ).getArgs( frame, {
		valueFunc = function( k, v )
			if v then
				v = v:match( '^%s*(.-)%s*$' ) -- Trim whitespace.
				if k == 'ago' or v ~= '' then
					return v
				end
			end
			return nil
		end,
		wrappers = 'Template:Time ago'
	})
	return p._main( args )
end

return p