Modul:DateUtils
Aspect

local p = {}
local maxDaysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
local roman = require('Modul:Roman')
local getArgs = require('Modul:Arguments').getArgs
local defaultPostfixYear = { bc = 'î.e.n.', ad = 'e.n.' }
local linkingPostfixYear = { bc = 'î.Hr.', ad = 'd.Hr.' }
local months = { ['ianuarie'] = 1, ['februarie'] = 2, ['martie'] = 3, ['aprilie'] = 4, ['mai'] = 5, ['iunie'] = 6, ['iulie'] = 7, ['august'] = 8, ['septembrie'] = 9, ['octombrie'] = 10, ['noiembrie'] = 11, ['decembrie'] = 12,
['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12,}
local suffixFormatYear = function(y, postFixYear)
local postFixYear = postFixYear or defaultPostfixYear
return (y < 0 and (' ' .. postFixYear.bc) or (y < 1000 and (' ' .. postFixYear.ad) or ''))
end
p.formatYear = function(y, link)
local out = ''
local yearLink = tostring(math.abs(y)) .. (y < 0 and suffixFormatYear(y, linkingPostfixYear) or '')
local yearLabel = tostring(math.abs(y)) .. suffixFormatYear(y, defaultPostfixYear)
if link then
out = out .. '[['
if yearLabel == yearLink then out = out .. yearLink
else out = out .. yearLink .. '|' .. yearLabel end
out = out .. ']]'
else
out = yearLabel
end
return out
end
p.formatDate = function(indate, link, notimetag)
if indate.precision == 6 then
local out = 'mileniul '
if indate.year >= 2000 and indate.year <= -2000 then out = out .. 'al ' end
out = out .. roman.main({tostring(1 + math.floor((math.abs(indate.year) - 1) / 1000))})
if indate.year >= 2000 and indate.year <= -2000 then out = out .. '-lea' end
out = out .. suffixFormatYear(indate.year)
return out
end
if indate.precision == 7 then
local out = 'secolul '
if indate.year >= 200 and indate.year <= -200 then out = out .. 'al ' end
out = out .. roman.main({tostring(1 + math.floor((math.abs(indate.year) - 1) / 100))})
if indate.year >= 200 and indate.year <= -200 then out = out .. '-lea' end
out = out .. suffixFormatYear(indate.year)
return out
end
if indate.precision == 8 then
return 'anii ' .. tostring(math.floor(math.abs(indate.year) / 10) * 10) .. suffixFormatYear(indate.year)
end
if indate.precision == 9 then
local timeTag = mw.html.create('time')
if indate.year > 0 then timeTag:attr('datetime', tostring(y)) end
timeTag:wikitext(p.formatYear(indate.year, link))
return tostring(timeTag)
end
if indate.precision and indate.precision > 9 then
local d1 = {}
d1.day = indate.day
if not d1.day or d1.day == 0 then d1.day = 1 end
d1.month = indate.month
d1.year = math.abs(indate.year)
local out = ''
local intermediateFormatDate = os.date('%d %B %Y', os.time(d1))
out = out .. mw.language.getContentLanguage():formatDate((indate.precision >= 11) and (link and '[[j F]]' or 'j F') or (link and '[[F]]' or 'F'), intermediateFormatDate)
out = out .. ' ' .. p.formatYear(indate.year, link)
if notimetag then return out end
local timeTag = mw.html.create('time')
if indate.year > 0 then
local datetimeFormat = 'Y-m-d'
if precision == 10 then datetimeFormat = 'Y-m' end
timeTag:attr('datetime', mw.language.getContentLanguage():formatDate(datetimeFormat, intermediateFormatDate))
end
timeTag:wikitext(out)
return tostring(timeTag)
end
end
p.formatDateFromFrame = function(frame)
local args = getArgs(frame)
local dateObj = {}
if args[1] then
local dateMatches = {}
dateMatches.year, dateMatches.month, dateMatches.day = mw.ustring.match(args[1], '(-?%d+)-(%d+)-(%d+)')
if dateMatches.year then
dateObj.year = tonumber(dateMatches.year)
dateObj.month = tonumber(dateMatches.month)
dateObj.day = tonumber(dateMatches.day)
else
local dateMatchSimple = mw.ustring.match(args[1], '-?%d+')
if dateMatchSimple then
dateObj.year = tonumber(dateMatchSimple)
end
end
end
if args[2] then
dateObj.month = tonumber(months[args[2]] or args[2])
if args[3] then dateObj.day = tonumber(args[3]) end
end
dateObj.calendar = 'gregorian'
dateObj.precision = 9
if dateObj.month ~= nil then
dateObj.precision = dateObj.precision + 1
if dateObj.day ~= nil then
dateObj.precision = dateObj.precision + 1
end
end
return p.formatDate(dateObj, args['link'] ~= nil)
end
p.isDateGregorian = function(indate)
return indate.calendarmodel == 'http://www.wikidata.org/entity/Q1985727' or indate.calendarmodel == 'http://www.wikidata.org/entity/Q12138' or indate.calendar == 'gregorian'
end
p.isDateJulian = function(indate)
return indate.calendarmodel == 'http://www.wikidata.org/entity/Q1985786' or indate.calendarmodel == 'http://www.wikidata.org/entity/Q11184' or indate.calendar == 'julian'
end
p.isLeapYearGregorian = function(year)
if (year % 4 ~= 0) then return false
elseif (year % 100 ~= 0) then return true
elseif (year % 400 ~= 0) then return false
end
return true
end
p.isDateInLeapYear = function(indate)
if p.isDateJulian(indate) then
return 0 == indate.year % 4
end
return p.isLeapYearGregorian(indate.year)
end
p.addDaysToDate = function(indate, days)
local outdate = mw.clone(indate)
outdate.day = outdate.day + days
local lastDayOfMonth = maxDaysInMonth[outdate.month]
while outdate.day > lastDayOfMonth do
lastDayOfMonth = maxDaysInMonth[outdate.month]
if outdate.month == 2 and p.isDateInLeapYear(outdate) then lastDayOfMonth = 29 end
outdate.month = outdate.month + 1
outdate.day = outdate.day - lastDayOfMonth
end
while outdate.month > 12 do
outdate.year = outdate.year + 1
outdate.month = outdate.month - 12
end
return outdate
end
p.parseCentury = function(datetext)
if datetext and mw.ustring.len(datetext) < 9 then return nil end
local centuryPrefixExpected = mw.ustring.sub(mw.ustring.lower(datetext), 1, 7)
if centuryPrefixExpected == 'secolul' then
local alLeaMatcherFunction = mw.ustring.gmatch(mw.ustring.lower(datetext), '%s+al%s+([xivlcm]+)%-lea')
local centNum = nil
local centStr = nil
if alLeaMatcherFunction then
centStr = alLeaMatcherFunction()
end
if not centStr then
local nonAlLEaMatcherFunction = mw.ustring.gmatch(mw.ustring.lower(datetext), '%s+([xivlcm]+)%s*')
if nonAlLEaMatcherFunction then
centStr = nonAlLEaMatcherFunction()
end
end
if not centStr then return nil end
local romanTestIdx = 1
while romanTestIdx < 30 do
if mw.ustring.lower(roman.main({tostring(romanTestIdx)})) == mw.ustring.lower(centStr) then
centNum = romanTestIdx
break
end
romanTestIdx = romanTestIdx + 1
end
if not centNum then return nil end
local bcPatterns = {}
table.insert(bcPatterns, 'î%.e%.n%.?')
table.insert(bcPatterns, 'î%.%s*Hr%.')
for _,eachBCPattern in ipairs(bcPatterns) do
local eraMatchFunction = mw.ustring.gmatch(datetext, eachBCPattern)
if eraMatchFunction then
local eraMatch = eraMatchFunction()
if eraMatch and eraMatch ~= '' then
if centNum > 0 then centNum = -centNum end
end
end
end
return centNum
end
return nil
end
p.parseYear = function(datetxt)
local yearPattern = '%d+'
local bcPatterns = {'î%.e%.n%.?', 'î%.%s*Hr%.', 'BC'}
local yearMatchFunction = mw.ustring.gmatch(datetxt, yearPattern)
if not yearMatchFunction then return nil end
local d = {}
local yearMatch = yearMatchFunction()
if not yearMatch or yearMatch == '' then return nil end
d.year = tonumber(yearMatch)
d.precision = 9
for _,eachBCPattern in ipairs(bcPatterns) do
local eraMatchFunction = mw.ustring.gmatch(datetxt, eachBCPattern)
if eraMatchFunction then
local eraMatch = eraMatchFunction()
if eraMatch and eraMatch ~= '' then
if d.year > 0 then d.year = -d.year end
end
end
end
return d
end
p.parseDate = function(datetxt)
if not datetxt then return nil end
local datePattern = '(%d+)%s+(%a+)%s+(%d+)'
local matchesIterator = mw.ustring.gmatch(datetxt, datePattern)
local englishDatePattern = '(%a+)%s+(%d+),%s+(%d+)'
local englishIterator = mw.ustring.gmatch(datetxt, englishDatePattern)
local monthOnlyPattern = '(%a+)%s+(%d+)'
local matchesMonthOnlyIterator = mw.ustring.gmatch(datetxt, monthOnlyPattern)
local dayStr, monthStr, yearStr = matchesIterator()
if dayStr and mw.ustring.len(mw.text.trim(dayStr)) > 0 and monthStr and months[monthStr] then
local d = {}
d.day = tonumber(dayStr)
d.month = tonumber(months[monthStr])
d.year = tonumber(yearStr)
d.precision = 11
return d
else
local dayStr, monthStr, yearStr = englishIterator()
if dayStr and mw.ustring.len(mw.text.trim(dayStr)) > 0 and monthStr and months[monthStr] then
local d = {}
d.day = tonumber(dayStr)
d.month = tonumber(months[monthStr])
d.year = tonumber(yearStr)
d.precision = 11
return d
else
monthStr, yearStr = matchesMonthOnlyIterator()
if monthStr and months[monthStr] ~= nil then
local d = {}
d.month = tonumber(months[monthStr])
d.year = tonumber(yearStr)
d.precision = 10
return d
end
local yr = p.parseYear(datetxt)
if yr ~= nil then
return yr
end
local cent = p.parseCentury(datetxt)
if cent ~= nil then
local d = {}
d.year = tonumber(cent * 100)
d.precision = 7
return d
end
end
end
return nil
end
p.parseWikidataDate = function(datetxt, precision)
if not datetxt then return nil end
if precision == nil then precision = 11 end
local iSOTimeSign = mw.ustring.sub(datetxt, 1, 1)
local datePattern = '(%d+)-(%d+)-(%d+)'
local matchesIterator = mw.ustring.gmatch(mw.ustring.sub(datetxt, 2), datePattern)
local yearSign = 1
--local timePattern = '(%d+):(%d+):(%d+)'
--local matchestimeIterator = mw.ustring.gmatch(datetxt, timePattern)
local yearStr, monthStr, dayStr = matchesIterator()
if dayStr and tonumber(dayStr) == 0 then dayStr = "01" end
if monthStr and tonumber(monthStr) == 0 then monthStr = "01" end
if iSOTimeSign == "-" then yearSign = -1 end
if precision >= 11 and dayStr and monthStr and yearStr then
local d = {}
d.day = tonumber(dayStr)
d.month = tonumber(monthStr)
d.year = tonumber(yearStr) * yearSign
d.precision = precision
return d
end
if precision == 10 and monthStr and yearStr then
local d = {}
d.day = 1 --this is a "valid 0"
d.month = tonumber(monthStr)
d.year = tonumber(yearStr) * yearSign
d.precision = precision
return d
end
if precision <= 9 and yearStr then
local d = {}
d.day = 1 --this is a "valid 0"
d.month = 1 --this is a "valid 0"
d.year = tonumber(yearStr) * yearSign
d.precision = precision
return d
end
return nil
end
p.compare = function(d1, d2)
if not d1.year and d2.year then return 1 end
if not d2.year and d1.year then return -1 end
if not d1.month and d2.month then
if d1.year == d2.year then
return 1
else
return d1.year - d2.year
end
end
if not d2.month and d1.month then
if d1.year == d2.year then
return -1
else
return d1.year - d2.year
end
end
if not d1.day and d2.day then
if d1.year == d2.year then
if d1.month == d2.month then
return 1
else
return d1.month - d2.month
end
else
return d1.year - d2.year
end
end
if not d2.day and d1.day then
if d1.year == d2.year then
if d1.month == d2.month then
return -1
else
return d1.month - d2.month
end
else
return d1.year - d2.year
end
end
if d1.year == d2.year then
if d1.month == d2.month then
if d1.day == d2.day then
return 0
else return (d1.day - d2.day) / math.abs(d1.day - d2.day)
end
else return (d1.month - d2.month) / math.abs(d1.month - d2.month)
end
else return (d1.year - d2.year) / math.abs(d1.year - d2.year)
end
end
p.extractDateFromWikidataSnak = function(snak)
if snak.snaktype ~= 'value' or not snak.datavalue then return nil end
local timestamp = snak.datavalue.value.time
local precision = snak.datavalue.value.precision
return p.parseWikidataDate(timestamp, precision)
end
return p