Module:Header/year

require('strict')

local p = {} --p stands for package

local yesno = require('Module:Yesno') local tableTools = require('Module:TableTools')

local current_title = mw.title.getCurrentTitle

--[=[ Construct the year span --]=] local function getYearFromTimestamp(timestamp) -- example timestamps: +2016-10-05T00:00:00Z, -1752-00-00T00:00:00Z local split = mw.text.split(timestamp, '-', true) local year local bce = '' if split[1] == '' then year = tonumber(split[2]) bce = ' BCE' else year = tonumber(split[1]) end return {year = year, bce = bce} end

local function formatYear(year, precision) if precision == 7 then local suffixes = { [1] = 'st', [2] = 'nd', [3] = 'rd', default = 'th' }		local century = (year - year % 100)/100 + 1 return century .. (suffixes[century] or suffixes['default']) .. ' century' elseif precision == 8 then return year .. 's'	else return year end end

local function getYearFromSingleStatement(statement, args) local snak = statement.mainsnak if not snak or not snak.datavalue or not snak.datavalue.value or not snak.datavalue.value.time then return nil end if args['noprint'] and args['nocat'] then return nil end local cats = {} -- tracking categories --[=[ Precision: 0 - billion years 1 - hundred million years, 2 - ten million years, 3 - million years, 4 - hundred thousand years, 5 - ten thousand years, 6 - millenia, 7 - centuries, 8 - decades, 9 - years, 10 - months, 11 - days 12 - hours 13 - minutes 14 - seconds ]=]	local precision = math.min(snak.datavalue.value.precision, 9) local year local circa = '' local start_times = {} local end_times = {} local start_years = {} local end_years = {} local start_year local end_year if statement.qualifiers then -- Check if date is approximate -- P1480 = sourcing circumstances, Q5727902 = circa if statement.qualifiers.P1480 then for _, qualifier in pairs(statement.qualifiers.P1480) do				if qualifier.datavalue and qualifier.datavalue.value.id == 'Q5727902' then precision = precision - 1 circa = 'c. ' break end end end -- P580 = start time if statement.qualifiers.P580 then for k, v in pairs(statement.qualifiers.P580) do				local startt = getYearFromSingleStatement({mainsnak = v}, {nocat = true, noprint = false, ['table'] = true}) if startt then table.insert(start_times, startt) end end for k, v in pairs(start_times) do				table.insert(start_years, v.year) end start_years = tableTools.removeDuplicates(start_years) table.sort(start_years) if #start_years > 1 then table.insert(cats, '') end start_year = start_years[1] end -- P582 = end time if statement.qualifiers.P582 then for k, v in pairs(statement.qualifiers.P582) do				local endt = getYearFromSingleStatement({mainsnak = v}, {nocat = true, noprint = false, ['table'] = true}) if endt then table.insert(end_times, endt) end end for k, v in pairs(end_times) do				table.insert(end_years, v.year) end end_years = tableTools.removeDuplicates(end_years) table.sort(end_years) if #end_years > 1 then table.insert(cats, '') end end_year = end_years[1] end end if precision < 9 then table.insert(cats, '') end if precision < 7 then if args['noprint'] then year = '' else year = string.gsub(string.gsub(mw.wikibase.formatValue(statement.mainsnak), '^ ', ), ' $', ) end return circa .. year .. table.concat(cats) end local timestamp_info = getYearFromTimestamp(snak.datavalue.value.time) year = timestamp_info.year local bce = timestamp_info.bce -- approximate year, precision 8: year - year % 10 for decade -- approximate decade, precision 7: year - year % 100 for century if circa ~= '' then year = year - (year % math.pow(10, 9 - precision)) end year = formatYear(year, precision) if start_year and start_year ~= year then table.insert(cats, '') end local printed_year = circa .. year .. bce if (start_year or end_year) and start_year ~= end_year then printed_year = circa .. (start_year or '') .. '–' .. (end_year or '') .. bce end if start_year and end_year and start_year ~= end_year then local start_decade = start_year - start_year % 10 local end_decade = end_year - end_year % 10 local start_century = start_year - start_year % 100 local end_century = end_year - end_year % 100 if start_decade == end_decade then table.insert(cats, '') elseif start_century == end_century then table.insert(cats, '') end else table.insert(cats, '') end if args['noprint'] then printed_year = '' end if not args['nocat'] then cats = tableTools.removeDuplicates(cats) printed_year = printed_year .. table.concat(cats) end if args['table'] then return {year = year, printed_year = printed_year, circa = circa, bce = bce} end return printed_year end

local function parse_wikidata_year_and_categorise(args) -- Fetch entity object for Wikidata item connected to the current page -- Let manually-specified Wikidata ID override if given and valid if not (args.wikidata and mw.wikibase.isValidEntityId(args.wikidata)) then args.wikidata = mw.wikibase.getEntityIdForCurrentPage end if not args.wikidata then return nil end local item = mw.wikibase.getEntity(args.wikidata) if not item then return nil end local statements = item:getBestStatements('P577') -- publication date if #statements == 0 then return nil end local years = {} for _, statement in pairs(statements) do		local year = getYearFromSingleStatement(statement, args) if year then table.insert(years, year) end end local cat = '' years = tableTools.removeDuplicates(years) if #years == 0 then return nil elseif #years > 1 and not args['nocat'] then cat = '' end return table.concat(years, '/') .. cat end

local function parse_year_and_categorise(args) local year = args['year'] -- Use Wikidata if year param is empty if not year then return parse_wikidata_year_and_categorise(args) end local cats = {} local bce = '' local circa = '' local ret = '' -- Extract common era info to make it easier to process if string.match(year, "BCE$") then bce = ' BCE' year = string.gsub(year, '%s*BCE', '') -- Also tag it as a non-numeric year table.insert(cats, '') end -- If the year provided is a plain year (all digits) if tonumber(year) then table.insert(cats, '') if not args['noprint'] then ret = year .. bce end if not args['nocat'] then ret = ret .. table.concat(cats) end -- For simple years we're done. return ret end -- Add tracking category for all non-numeric dates table.insert(cats, '') -- Explicitly tagged as being of unknown date if year == '?' or string.lower(year) == 'unknown' then table.insert(cats, '') if not args['noprint'] then ret = 'unknown' end if not args['nocat'] then ret = ret .. table.concat(cats) end -- For explicitly given unknown years we're done. return ret end -- Now figure out a complex date -- Year ranges year = string.gsub(string.gsub(year, '%-', '–'), '—', '–') -- Approximate years -- Lua patterns can't do ^c(irca)?( |%.|/)* because they don't do alternation or apply quantifiers to groups if string.match(year, '^circa') or string.match(year, '^c%s*%.') or string.match(year, '^c%s*/') then circa = 'c. ' year = string.gsub(string.gsub(string.gsub(year, '^circa%s*', ), '^c%s*%.%s*', ), '^c%s*/%s*', '') table.insert(cats, '') -- circa a specific year if tonumber(year) then year = tonumber(year) local decade = (year - year % 10) .. 's'			table.insert(cats, '') if not args['noprint'] then ret = circa .. year .. bce end if not args['nocat'] then ret = ret .. table.concat(cats) end -- For approximate years with a known decade we're done return ret end end -- Check if it looks like a decade if string.match(year, '^%d*0s$') then table.insert(cats, '') table.insert(cats, '') -- For explicitly given decades we're done if not args['noprint'] then ret = circa .. year .. bce end if not args['nocat'] then ret = ret .. table.concat(cats) end return ret end -- Or a century -- Lua patterns can't do '^%d*(st|nd|rd|th) century$' if string.match(year, '^%d*st century$') or string.match(year, '^%d*nd century$') or string.match(year, '^%d*rd century$') or string.match(year, '^%d*th century$') then table.insert(cats, '') table.insert(cats, '') -- For explicitly given centuries we're done if not args['noprint'] then ret = circa .. year .. bce end if not args['nocat'] then ret = ret .. table.concat(cats) end return ret end -- Or a range of years local start_year, end_year if string.match(year, '^%d*–%d*$') then start_year, end_year = string.match(year, '^(%d*)–(%d*)$') elseif string.match(year, '^%d*/%d*$') then start_year, end_year = string.match(year, '^(%d*)/(%d*)$') table.insert(cats, '') end if start_year or end_year then start_year = tonumber(start_year) end_year = tonumber(end_year) if start_year and end_year and start_year == end_year and circa == '' then table.insert(cats, '') if not args['noprint'] then ret = circa .. start_year .. bce end if not args['nocat'] then ret = ret .. table.concat(cats) end return ret elseif start_year and end_year then local start_decade = start_year - start_year % 10 local end_decade = end_year - end_year % 10 local start_century = start_year - start_year % 100 local end_century = end_year - end_year % 100 if start_decade == end_decade then table.insert(cats, '') elseif start_century == end_century then table.insert(cats, '') end elseif end_year then table.insert(cats, '') end -- check isn't redundant since the year might just be – if start_year or end_year then if not args['noprint'] then ret = circa .. year .. bce end if not args['nocat'] then ret = ret .. table.concat(cats) end return ret end end -- If we're here we didn't manage to parse it. table.insert(cats, '') if not args['noprint'] then ret = circa .. year .. bce end if not args['nocat'] then ret = ret .. table.concat(cats) end -- Return whatever we have and the tracking cats return ret end

function p.construct_year(args) local year = mw.html.create('span'):attr('id', 'header-year-text') local year_args = { ['year'] = args['year'], noprint = yesno(args['noyear']) or false, wikidata = args.wikidata }	year_args['nocat'] = yesno(args['noyearcat']) if year_args['nocat'] == nil then if args.testing then year_args['nocat'] = false else year_args['nocat'] = (				yesno(args['disambiguation']) -- Disambiguations never categorise				or not current_title:inNamespaces(0, 114) -- Only categorize in mainspace and Translation				or current_title.isSubpage -- Only categorise if this is a base page			) end end local year_text = parse_year_and_categorise(year_args) if year_text then year:wikitext(year_text) if year_args['noprint'] then return tostring(year) else return ' (' .. tostring(year) .. ')' end elseif year_args['nocat'] then return '' else return '' end end

return p