יחידה:תאריך: הבדלים בין גרסאות בדף
מאין תקציר עריכה |
מ 3 גרסאות של הדף wikipedia:he:יחידה:תאריך יובאו |
||
| (47 גרסאות ביניים של 9 משתמשים אינן מוצגות) | |||
| שורה 1: | שורה 1: | ||
function | local Date = {} | ||
local | local maxDaysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} | ||
--[[ | |||
Supported calendar models | |||
]]-- | |||
Date.CALENDAR = { | |||
GREGORIAN = 'Gregorian', | |||
JULIAN = 'Julian' | |||
} | |||
--Internal functions | |||
--[[ | |||
Check if a value is a number in the given range | |||
@param mixed value | |||
@param number min | |||
@param number max | |||
@return boolean | |||
]]-- | |||
local function validateNumberInRange( value, min, max ) | |||
return type( value ) == 'number' and value >= min and value <= max | |||
end | |||
--[[ | |||
Validate a time defintion | |||
@param table definition data | |||
@return boolean | |||
]]-- | |||
local function validate(definition) | |||
--Validate constants | |||
if not Date.knowsPrecision(definition.precision) or | |||
(definition.calendar ~= Date.CALENDAR.GREGORIAN and definition.calendar ~= Date.CALENDAR.JULIAN) then | |||
return false | |||
end | |||
--Validate year | |||
if not (type( definition.year ) == 'number' or (definition.year == nil and precision == Date.PRECISION.DAY)) then | |||
return false | |||
end | |||
if definition.precision <= Date.PRECISION.YEAR then | |||
return true | |||
end | |||
--Validate month | |||
if not validateNumberInRange( definition.month, 1, 12 ) then | |||
return false | |||
end | |||
if definition.precision <= Date.PRECISION.MONTH then | |||
return true | |||
end | |||
--Validate day | |||
if not validateNumberInRange( definition.day, 1, 31 ) then | |||
return false | |||
end | |||
if definition.precision <= Date.PRECISION.DAY then | |||
return true | |||
end | |||
--Validate hour | |||
if not validateNumberInRange( definition.hour, 0, 23 ) then | |||
return false | |||
end | |||
if definition.precision <= Date.PRECISION.HOUR then | |||
return true | |||
end | |||
--Validate minute | |||
if not validateNumberInRange( definition.minute, 0, 59 ) then | |||
return false | |||
end | |||
if definition.precision <= Date.PRECISION.MINUTE then | |||
return true | |||
end | |||
--Validate second | |||
if not validateNumberInRange( definition.second, 0, 60 ) then | |||
return false | |||
end | |||
return true | |||
end | |||
--[[ | |||
Try to find the relevant precision for a time definition | |||
@param table time definition | |||
@return number the precision | |||
]]-- | |||
local function guessPrecision(definition) | |||
if definition.month == nil or (definition.month == 0 and definition.day == 0) then | |||
return Date.PRECISION.YEAR | |||
elseif definition.day == nil or definition.day == 0 then | |||
return Date.PRECISION.MONTH | |||
elseif definition.hour == nil then | |||
return Date.PRECISION.DAY | |||
elseif definition.minute == nil then | |||
return Date.PRECISION.HOUR | |||
elseif definition.second == nil then | |||
return Date.PRECISION.MINUTE | |||
else | |||
return Date.PRECISION.SECOND | |||
end | |||
end | |||
--[[ | |||
Try to find the relevant calendar for a time definition | |||
@param table time definition | |||
@return string the calendar name | |||
]]-- | |||
local function guessCalendar( definition ) | |||
if definition.year ~= nil and definition.year < 1583 and definition.precision > Date.PRECISION.MONTH then | |||
return Date.CALENDAR.JULIAN | |||
else | |||
return Date.CALENDAR.GREGORIAN | |||
end | |||
end | |||
--[[ | |||
Parse an ISO 2061 string and return it as a time definition | |||
@param string iso the iso datetime | |||
@param boolean withoutRecurrence concider date in the format XX-XX as year-month and not month-day | |||
@return table | |||
]]-- | |||
local function parseIso8601( iso, withoutRecurrence ) | |||
local definition = {} | |||
--Split date and time | |||
iso = mw.text.trim( iso:upper() ) | |||
local beginMatch, endMatch, date, time, offset = iso:find( '([%+%-]?[%d%-]+)[T ]?([%d%.:]*)([Z%+%-]?[%d:]*)' ) | |||
if beginMatch ~= 1 or endMatch ~= iso:len() then --iso is not a valid ISO string | |||
return {} | |||
end | |||
--date | |||
if date ~= nil then | |||
local isBC = false | |||
if date:sub( 1, 1 ) == '-' then | |||
isBC = true | |||
date = date:sub( 2, date:len() ) | |||
end | |||
local parts = mw.text.split( date, '-' ) | |||
if not withoutRecurrence and table.maxn( parts ) == 2 and parts[1]:len() == 2 then | |||
--MM-DD case | |||
definition.month = tonumber( parts[1] ) | |||
definition.day = tonumber( parts[2] ) | |||
else | |||
if isBC then | |||
definition.year = -1 * tonumber( parts[1] ) --FIXME - 1 --Years BC are counted since 0 and not -1 | |||
else | |||
definition.year = tonumber( parts[1] ) | |||
end | |||
definition.month = tonumber( parts[2] ) | |||
definition.day = tonumber( parts[3] ) | |||
end | |||
end | |||
--time | |||
if time ~= nil then | |||
local parts = mw.text.split( time, ':' ) | |||
definition.hour = tonumber( parts[1] ) | |||
definition.minute = tonumber( parts[2] ) | |||
definition.second = tonumber( parts[3] ) | |||
end | |||
--offset | |||
if offset ~= nil then | |||
if offset == 'Z' then | |||
definition.utcoffset = '+00:00' | |||
else | |||
definition.utcoffset = offset | |||
end | |||
end | |||
return definition | |||
end | |||
--[[ | |||
Format UTC offset for ISO output | |||
@param string offset UTC offset | |||
@return string UTC offset for ISO | |||
]]-- | |||
local function formatUtcOffsetForIso( offset ) | |||
if offset == '+00:00' then | |||
return 'Z' | |||
else | |||
return offset | |||
end | |||
end | |||
--[[ | |||
Prepend as mutch as needed the character c to the string str in order to to have a string of length length | |||
@param mixed str | |||
@param string c | |||
@param number length | |||
@return string | |||
]]-- | |||
local function prepend(str, c, length) | |||
str = tostring( str ) | |||
while str:len() < length do | |||
str = c .. str | |||
end | |||
return str | |||
end | |||
-- LEAP_GREGORIAN -- Is a given year in the Gregorian calendar a leap year ? | |||
local function leapGregorian(year) | |||
return ((year % 4) == 0) and | |||
(not (((year % 100) == 0) and ((year % 400) ~= 0))) | |||
end | |||
local isDateInLeapYear = function(indate) | |||
if indate.calendar == Date.CALENDAR.JULIAN then | |||
return 0 == indate.year % 4 | |||
end | |||
return leapGregorian(indate.year) | |||
end | |||
-- GREGORIAN_TO_JD -- Determine Julian day number from Gregorian calendar date | |||
local GREGORIAN_EPOCH = 1721425.5 | |||
local function gregorianToJd(year, month, day) | |||
return (GREGORIAN_EPOCH - 1) + | |||
(365 * (year - 1)) + | |||
math.floor((year - 1) / 4) + | |||
(-math.floor((year - 1) / 100)) + | |||
math.floor((year - 1) / 400) + | |||
math.floor((((367 * month) - 362) / 12) + | |||
((month <= 2) and 0 or | |||
(leapGregorian(year) and -1 or -2) | |||
) + | |||
day) | |||
end | |||
-- JD_TO_JULIAN -- Calculate Julian calendar date from Julian day | |||
local function jdToJulian(td) | |||
local z, a, alpha, b, c, d, e, year, month, day | |||
td = td + 0.5 | |||
z = math.floor(td) | |||
a = z | |||
b = a + 1524 | |||
c = math.floor((b - 122.1) / 365.25) | |||
d = math.floor(365.25 * c) | |||
e = math.floor((b - d) / 30.6001) | |||
month = math.floor((e < 14) and (e - 1) or (e - 13)) | |||
year = math.floor((month > 2) and (c - 4716) or (c - 4715)) | |||
day = b - d - math.floor(30.6001 * e) | |||
--[[ | |||
If year is less than 1, subtract one to convert from | |||
a zero based date system to the common era system in | |||
which the year -1 (1 B.C.E) is followed by year 1 (1 C.E.). | |||
--]] | |||
if year < 1 then | |||
year = year - 1 | |||
end | |||
return year, month, day | |||
end | |||
-- adapted from ro:Modul:GregorianDate | |||
local initialOffset = -3 | |||
local limitDates = { | |||
{year = 4, month = 3, day = 3, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 100, month = 3, day = 2, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 200, month = 3, day = 1, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 300, month = 2, day = 29, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 500, month = 2, day = 28, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 600, month = 2, day = 27, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 700, month = 2, day = 26, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 900, month = 2, day = 25, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 1000, month = 2, day = 24, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 1100, month = 2, day = 23, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 1300, month = 2, day = 22, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 1400, month = 2, day = 21, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 1500, month = 2, day = 20, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 1700, month = 2, day = 19, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 1800, month = 2, day = 18, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 1900, month = 2, day = 17, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 2100, month = 2, day = 16, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 2200, month = 2, day = 15, calendar = Date.CALENDAR.JULIAN }, | |||
{year = 2300, month = 2, day = 14, calendar = Date.CALENDAR.JULIAN } | |||
} | |||
function Date.julianToGregorian(indate) | |||
if indate.calendar ~= Date.CALENDAR.JULIAN then | |||
return indate | |||
end | |||
local outputDate | |||
if indate.precision > Date.PRECISION.MONTH then | |||
local offset = initialOffset | |||
local limitDateIdx = 1 | |||
while limitDateIdx < #limitDates and Date.le(limitDates[limitDateIdx], indate) do | |||
limitDateIdx = limitDateIdx + 1 | |||
offset = offset + 1 | |||
end | |||
outputDate = Date.addDaysToDate(indate, offset) | |||
else | |||
outputDate = mw.clone(indate) | |||
end | |||
outputDate.calendar = Date.CALENDAR.GREGORIAN | |||
outputDate.calendarmodel = 'http://www.wikidata.org/entity/Q1985727' | |||
return Date.new(outputDate) | |||
end | |||
function Date.addDaysToDate(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 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 | |||
function Date.le(t1, t2, correct_calender) | |||
if t1.calendar ~= t2.calendar then | |||
if correct_calender then | |||
t1 = Date.julianToGregorian(t1) | |||
t2 = Date.julianToGregorian(t2) | |||
else | |||
error("Calendars don't match", 2) | |||
end | |||
end | |||
if t1.year < t2.year then | |||
return true | |||
end | |||
if t1.year == t2.year then | |||
if t1.month < t2.month then | |||
return true | |||
end | |||
if t1.month == t2.month and t1.day <= t2.day then | |||
return true | |||
end | |||
end | |||
return false | |||
end | |||
--Public interface | |||
--[[ | |||
Build a new Date | |||
@param table definition definition of the time | |||
@return Date|nil | |||
]]-- | |||
function Date.new( definition ) | |||
--Default values | |||
if definition.precision == nil then | |||
definition.precision = guessPrecision( definition ) | |||
end | |||
if definition.calendar == nil then | |||
definition.calendar = guessCalendar( definition ) | |||
end | |||
if not validate( definition ) then | |||
return nil | |||
end | |||
local time = { | |||
year = definition.year or nil, | |||
month = definition.month or 1, | |||
day = definition.day or 1, | |||
hour = definition.hour or 0, | |||
minute = definition.minute or 0, | |||
second = definition.second or 0, | |||
utcoffset = definition.utcoffset or '+00:00', | |||
calendar = definition.calendar or Date.CALENDAR.GREGORIAN, | |||
precision = definition.precision or 0 | |||
} | |||
setmetatable( time, { | |||
__index = Date, | |||
__le = le, | |||
__tostring = function( self ) return self:toString() end | |||
} ) | |||
return time | |||
end | |||
--[[ | |||
Build a new Date from an ISO 8601 datetime | |||
@param string iso the time as ISO string | |||
@param boolean withoutRecurrence concider date in the format XX-XX as year-month and not month-day | |||
@return Date|nil | |||
]]-- | |||
function Date.newFromIso8601( iso, withoutRecurrence ) | |||
return Date.new( parseIso8601( iso, withoutRecurrence ) ) | |||
end | |||
--[[ | |||
Build a new Date from a Wikidata time value | |||
@param table wikidataValue the time as represented by Wikidata | |||
@return Date|nil | |||
]]-- | |||
function Date.newFromWikidataValue( wikidataValue ) | |||
local definition = parseIso8601( wikidataValue.time ) | |||
definition.precision = wikidataValue.precision | |||
if wikidataValue.calendarmodel == 'http://www.wikidata.org/entity/Q1985727' then | |||
definition.calendar = Date.CALENDAR.GREGORIAN | |||
elseif wikidataValue.calendarmodel == 'http://www.wikidata.org/entity/Q1985786' then | |||
definition.calendar = Date.CALENDAR.JULIAN | |||
else | |||
return nil | |||
end | |||
return Date.new( definition ) | |||
end | |||
--[[ | |||
Build a new Date from a wiki string | |||
@param string wikitext string | |||
@return Date|nil | |||
]] | |||
function Date.newFromWikitext( wikitext ) | |||
local months = { | |||
['ינואר']= 1, | ['ינואר']= 1, | ||
['פברואר']= 2, | ['פברואר']= 2, | ||
| שורה 14: | שורה 444: | ||
['דצמבר']= 12 | ['דצמבר']= 12 | ||
} | } | ||
local calendar = nil | |||
if mw.ustring.find( wikitext, '<small>%(%[%[הלוח היוליאני%|יוליאני%]%]%)</small>' ) then | |||
calendar = Date.CALENDAR.JULIAN | |||
wikitext = mw.ustring.gsub( wikitext, "<small>%(%[%[הלוח היוליאני%|יוליאני%]%]%)</small>", "" ) | |||
end | |||
-- Remove instances of [ and ] | -- Remove instances of [ and ] | ||
wikitext = mw.ustring.gsub( wikitext, "[%[%]]", "" ) | |||
-- Remove footnotes & directionality markers | -- Remove footnotes & directionality markers | ||
wikitext = mw.text.killMarkers( wikitext ) | |||
wikitext = mw.ustring.gsub(wikitext, "‏","") | |||
wikitext = mw.ustring.gsub(wikitext, "‎","") | |||
-- BC to minus | -- BC to minus | ||
wikitext = mw.ustring.gsub( wikitext, "([0-9]+) לפנה[\"״]ס" , "-%1") | |||
for a in pairs(months) do | |||
for a in pairs( | wikitext = mw.ustring.gsub(wikitext, ' ?ב?'..a, ' ' .. months[a]) | ||
end | |||
if mw.ustring.match(wikitext, '^המאה ה[־-]%d+$') then | |||
local yearStr = mw.ustring.match(wikitext, '^המאה ה[־-](%d+)$') | |||
return Date.new( { year=tonumber(yearStr)*100, month=0, day=0, precision= Date.PRECISION.YEAR100 } ) | |||
end | end | ||
-- if there are alphabet chars return nil (unexpected character) | -- if there are alphabet chars return nil (unexpected character) | ||
assert(not mw.ustring.find( | assert(not mw.ustring.find(wikitext, '%a'), "Unexpected format") | ||
local parts = mw.text.split(mw.text.trim(wikitext),' ') | |||
local parts = mw.text.split( | |||
local definition = {} | |||
definition.calendar = calendar | |||
if #parts==3 then -- DMY date | if #parts==3 then -- DMY date | ||
definition.year = tonumber(parts[3]) | |||
definition.month = tonumber(parts[2]) | |||
definition.day = tonumber(parts[1]) | |||
assert( | assert(definition.year, "Could not recognize year") | ||
assert( | assert(definition.month<13 and definition.month>0, "Could not recognize month number") | ||
assert( | assert(definition.day<32 and definition.day>0, "Wrong date format") | ||
definition.precision = Date.PRECISION.DAY | |||
elseif #parts==2 then -- MY date | |||
definition.year = tonumber(parts[2]) | |||
definition.month = tonumber(parts[1]) | |||
definition.precision = Date.PRECISION.MONTH | |||
assert(definition.year<1e7, "Could not recognize year") | |||
assert(definition.month<13 and definition.month>0, "Could not recognize month number") | |||
elseif #parts==1 then --Y date | |||
definition.precision = Date.PRECISION.YEAR | |||
definition.year=tonumber(parts[1]) | |||
assert(definition.year<1e7, "Could not recognize year") | |||
else | |||
error("Unexpected date format") | |||
end | |||
return Date.new( definition ) | |||
end | |||
--[[ | |||
Return a Date as a ISO 8601 string | |||
@return string | |||
]]-- | |||
function Date:toIso8601() | |||
local iso = '' | |||
if self.year ~= nil then | |||
if self.year < 0 then | |||
--Years BC are counted since 0 and not -1 | |||
iso = '-' .. prepend(string.format('%.0f', -1 * self.year), '0', 4) | |||
else | |||
iso = prepend(string.format('%.0f', self.year), '0', 4) | |||
end | |||
end | |||
--month | |||
if self.precision < Date.PRECISION.MONTH then | |||
return iso | |||
end | |||
if self.iso ~= '' then | |||
iso = iso .. '-' | |||
end | |||
iso = iso .. prepend( self.month, '0', 2 ) | |||
--day | |||
if self.precision < Date.PRECISION.DAY then | |||
return iso | |||
end | |||
iso = iso .. '-' .. prepend( self.day, '0', 2 ) | |||
--hour | |||
if self.precision < Date.PRECISION.HOUR then | |||
return iso | |||
end | |||
iso = iso .. 'T' .. prepend( self.hour, '0', 2 ) | |||
--minute | |||
if self.precision < Date.PRECISION.MINUTE then | |||
return iso .. formatUtcOffsetForIso( self.utcoffset ) | |||
end | |||
iso = iso .. ':' .. prepend( self.minute, '0', 2 ) | |||
--second | |||
if self.precision < Date.PRECISION.SECOND then | |||
return iso .. formatUtcOffsetForIso( self.utcoffset ) | |||
end | |||
return iso .. ':' .. prepend( self.second, '0', 2 ) .. formatUtcOffsetForIso( self.utcoffset ) | |||
end | |||
--[[ | |||
Return a hebrew representation of Date as a string | |||
@return string | |||
]]-- | |||
function Date:toHebrewString() | |||
local hebrewStr = '' | |||
local year = self.year | |||
if (self.precision >= Date.PRECISION.MY100) and (self.precision <= Date.PRECISION.MY) then | |||
if self.year>0 then | |||
return (self.year/1000000) .. ' מיליון שנים לספירה' | |||
else | |||
return (-self.year/1000000) ..' מיליון שנים לפנה״ס' | |||
end | |||
elseif (self.precision >=Date.PRECISION.KY100) and (self.precision <= Date.PRECISION.KY) then | |||
if self.year>0 then | |||
return 'האלף ה־'.. (self.year/1000) | |||
else | |||
return 'האלף ה־'.. (-self.year/1000) ..' לפנה״ס' | |||
end | |||
elseif self.precision == Date.PRECISION.YEAR100 then | |||
if year>0 then | |||
return 'המאה ה־'.. math.ceil(self.year/100) | |||
else | |||
return 'המאה ה־'.. math.ceil(-self.year/100) ..' לפנה״ס' | |||
end | |||
elseif self.precision == Date.PRECISION.YEAR10 then | |||
local year = math.floor((self.year < 0 and -1 * self.year or self.year) / 10) * 10 | |||
if self.year>0 then | |||
if year%100==0 then | |||
return 'העשור הראשון של המאה ה־'.. tostring((year/100)+1) | |||
else | |||
return 'שנות ה־' .. tostring(year%100) .. ' של המאה ה־'.. tostring(math.ceil(year/100)) | |||
end | |||
else | |||
if year%100==0 then | |||
return 'העשור הראשון של המאה ה־'.. tostring((year/100))..' לפנה״ס' | |||
else | |||
return 'שנות ה־' .. tostring(year%100) .. ' של המאה ה־'.. tostring(math.ceil(year/100))..' לפנה״ס' | |||
end | |||
end | |||
end | |||
if self.year ~= nil then | |||
if self.year < 0 then | |||
hebrewStr = mw.ustring.format('%d לפנה״ס', (-1*self.year)) | |||
else | |||
if self.calendar == Date.CALENDAR.JULIAN and self.year > 1583 then | |||
hebrewStr = mw.ustring.format('%d <small>([[הלוח היוליאני|יוליאני]])</small>', (self.year)) | |||
else | |||
hebrewStr = mw.ustring.format('%d', self.year) | |||
end | |||
end | |||
end | |||
--month | |||
if self.precision>=Date.PRECISION.YEAR and self.precision < Date.PRECISION.MONTH then | |||
return hebrewStr | |||
end | |||
local months = { 'ינואר','פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר','דצמבר' } | |||
hebrewStr = months[self.month] .. ' ' .. hebrewStr | |||
--day | |||
if self.precision < Date.PRECISION.DAY then | |||
return hebrewStr | |||
end | |||
hebrewStr = mw.ustring.format('%d ב%s', self.day, hebrewStr) | |||
--hour | |||
if self.precision < Date.PRECISION.HOUR then | |||
return hebrewStr | |||
end | |||
hebrewStr = mw.ustring.format('%s בשעה %d', hebrewStr, self.hour) | |||
--minute | |||
if self.precision < Date.PRECISION.MINUTE then | |||
return hebrewStr .. formatUtcOffsetForIso( self.utcoffset ) | |||
end | |||
hebrewStr = hebrewStr .. ':' .. prepend( self.minute, '0', 2 ) | |||
--second | |||
if self.precision < Date.PRECISION.SECOND then | |||
return hebrewStr .. formatUtcOffsetForIso( self.utcoffset ) | |||
end | |||
return hebrewStr .. ':' .. prepend( self.second, '0', 2 ) .. formatUtcOffsetForIso( self.utcoffset ) | |||
end | |||
--[[ | |||
Return a Date as a string | |||
@param mw.language|string|nil language to use. By default the content language. | |||
@return string | |||
]]-- | |||
function Date:toString( language ) | |||
if language == nil then | |||
return self:toIso8601() | |||
end | |||
if language == 'he' then | |||
return self:toHebrewString() | |||
end | |||
--[[if type( language ) == 'string' then | |||
language = mw.language.new( language ) | |||
end | |||
return language:formatDate( 'r', self:toIso8601() )]] | |||
return self:toIso8601() | |||
end | |||
--[[ | |||
Return a Date in HTMl (with a <time> node) | |||
@param mw.language|string|nil language to use. By default the content language. | |||
@param table|nil attributes table of attributes to add to the <time> node. | |||
@return string | |||
]]-- | |||
function Date:toHtml( language, attributes ) | |||
if attributes == nil then | |||
attributes = {} | |||
end | |||
attributes['datetime'] = self:toIso8601() | |||
return mw.text.tag( 'time', attributes, self:toString( language ) ) | |||
end | |||
--[[ | |||
All possible precisions for a Date (same ids as Wikibase) | |||
]]-- | |||
Date.PRECISION = { | |||
GY = 0, --Gigayear | |||
MY100 = 1, --100 Megayears | |||
MY10 = 2, --10 Megayears | |||
MY = 3, --Megayear | |||
KY100 = 4, --100 Kiloyears | |||
KY10 = 5, --10 Kiloyears | |||
KY = 6, --Kiloyear | |||
YEAR100 = 7, --100 years | |||
YEAR10 = 8, --10 years | |||
YEAR = 9, | |||
MONTH = 10, | |||
DAY = 11, | |||
HOUR = 12, | |||
MINUTE = 13, | |||
SECOND = 14 | |||
} | |||
--[[ | |||
Check if the precision is known | |||
@param number precision ID | |||
@return boolean | |||
]]-- | |||
function Date.knowsPrecision( precision ) | |||
for _,id in pairs( Date.PRECISION ) do | |||
if id == precision then | |||
return true | |||
end | |||
end | |||
return false | |||
end | |||
function Date.age(time1, time2) | |||
if time2 == nil then | |||
time2 = Date.newFromIso8601(mw.getContentLanguage():formatDate('c', nil, true), true) | |||
end | |||
local age = {year, month, day} | |||
age.year = time2.year - time1.year | |||
age.month = time2.month - time1.month | |||
age.day = time2.day - time1.day | |||
if age.day < 0 then | |||
local lastMonth = time2.month - 1 | |||
if lastMonth == 0 then | |||
lastMonth = 12 | |||
end | |||
age.day = age.day + maxDaysInMonth[lastMonth] | |||
age.month = age.month - 1 | |||
end | |||
if age.month < 0 then | |||
age.month = age.month + 12 | |||
age.year = age.year - 1 | |||
end | |||
if time1.year < 0 and time2.year > 0 then | |||
age.year = age.year - 1 | |||
end | |||
return age | |||
end | |||
function Date:formatDate(options) | |||
options = options or {} | |||
local fd = '' | |||
if self.precision >= Date.PRECISION.DAY then | |||
fd = self.year < 0 and (-1 * self.year) .. ' לפנה"ס' or fd .. self.year | |||
if options.link then fd = '[[' .. fd .. ']]' end | |||
local d = '2000-' .. prepend(self.month, '0', 2) .. '-' .. prepend(self.day, '0', 2) | |||
local lang = mw.getContentLanguage() | |||
fd = fd .. '. ' .. lang:formatDate(options.link and '[[j xg]]' or 'j xg', d) | |||
elseif self.precision >= Date.PRECISION.MONTH then | |||
fd = self.year < 0 and (-1 * self.year) .. ' לפנה"ס' or fd .. self.year | |||
local month = mw.getContentLanguage():formatDate('F', '2000-' .. self.month) | |||
if options.link then fd = '[[' .. fd .. ']]' end | |||
fd = month .. ' ' .. fd | |||
elseif self.precision >= Date.PRECISION.YEAR then | |||
fd = self.year < 0 and (-1 * self.year) .. ' לפנה"ס' or fd .. self.year | |||
if options.link ~= 'nem' then fd = '[[' .. fd .. ']]' end | |||
elseif self.precision == Date.PRECISION.YEAR10 then | |||
local year = math.floor((self.year < 0 and -1 * self.year or self.year) / 10) * 10 | |||
fd = 'שנות ה-' .. tostring(year%100) .. ' של המאה ה-'.. tostring(ceil(year/100)) | |||
fd = self.year < 0 and year .. ' לפנה"ס' or tostring(year) | |||
elseif self.precision == Date.PRECISION.YEAR100 then | |||
if self.year < 0 then | |||
fd = 'המאה ה-' .. math.floor(-1 * self.year / 100) .. ' לפנה"ס' | |||
else | |||
fd = 'המאה ה-' ..math.floor(self.year / 100) | |||
end | |||
if options.link then fd = '[[' .. fd .. ']]' end | |||
else | |||
fd = tostring(self.year) | |||
end | |||
return fd | |||
end | |||
function parseStrDate(dateStr, dateType) | |||
local datetime = Date.newFromWikitext( dateStr ) | |||
if datetime.precision >= Date.PRECISION.DAY then -- DMY date | |||
if dateType=='Y' then | if dateType=='Y' then | ||
res = | res = datetime.year | ||
elseif dateType=='M' then | elseif dateType=='M' then | ||
res = | res = datetime.month | ||
elseif dateType=='D' then | elseif dateType=='D' then | ||
res = | res = datetime.day | ||
elseif dateType == 'TS' then | elseif dateType == 'TS' then | ||
res = | res = datetime:toIso8601() | ||
end | end | ||
elseif | elseif datetime.precision >= Date.PRECISION.MONTH then -- MY date | ||
if dateType=='Y' then | if dateType=='Y' then | ||
res = | res = datetime.year | ||
elseif dateType=='M' then | elseif dateType=='M' then | ||
res = | res = datetime.month | ||
elseif dateType == 'TS' then | elseif dateType == 'TS' then | ||
res = | res = datetime:toIso8601() | ||
end | end | ||
else --Y date | |||
if dateType=='Y' then | if dateType=='Y' then | ||
res = | res = datetime.year | ||
elseif dateType == 'TS' then | elseif dateType == 'TS' then | ||
res = | res = datetime:toIso8601() | ||
end | end | ||
end | end | ||
return res | return res | ||
end | end | ||
| שורה 79: | שורה 802: | ||
function parseDateRange(dateRangeStr, diffFormat, inclusive ) | function parseDateRange(dateRangeStr, diffFormat, inclusive ) | ||
-- remove footnotes | -- remove footnotes | ||
dateRangeStr = mw. | dateRangeStr = mw.text.killMarkers(dateRangeStr) | ||
dateRangeStr = mw.ustring.gsub(dateRangeStr, "‏","") | dateRangeStr = mw.ustring.gsub(dateRangeStr, "‏","") | ||
local outputPrefix = '' | |||
local parts = mw.text.split(dateRangeStr,' | local parts = mw.text.split(dateRangeStr,' *[–%-] *') | ||
assert(#parts==2 or #parts==1, "Date range expected format is from - to or from (e.g from - now)") | assert(#parts==2 or #parts==1, "Date range expected format is from - to or from (e.g from - now)") | ||
-- parse dates | -- parse dates | ||
local | local t1 = Date.newFromWikitext( parts[1] ) | ||
local | local t2 | ||
local useCurrent = #parts<2 or (parts[2] == 'היום' or parts[2]=='הווה') | |||
if not useCurrent then | |||
t2 = Date.newFromWikitext( parts[2] ) | |||
if | |||
else | else | ||
t2 = | t2 = Date.newFromIso8601(mw.getContentLanguage():formatDate('c', nil, true), true) | ||
end | end | ||
| שורה 123: | שורה 830: | ||
end | end | ||
if hasDays and (( | if hasDays and ((t1.precision>=Date.PRECISION.MONTH and t2.precision<Date.PRECISION.MONTH) or (t1.precision<Date.PRECISION.MONTH and t2.precision>=Date.PRECISION.MONTH)) then | ||
-- Ambiguous date range | |||
if t2.year - t1.year > 2 then | |||
diffFormat = {'years'} | |||
else | |||
return '' | |||
end | |||
end | end | ||
local NO_GUESS, MONTH_GUESS, DAY_GUESS = 0, 1, 2 | local NO_GUESS, MONTH_GUESS, DAY_GUESS = 0, 1, 2 | ||
local guessLevel = NO_GUESS | local guessLevel = NO_GUESS | ||
if | if t1.precision<Date.PRECISION.MONTH or t2.precision<Date.PRECISION.MONTH then | ||
guessLevel = MONTH_GUESS | guessLevel = MONTH_GUESS | ||
inclusive=true | inclusive=true | ||
elseif | elseif t1.precision<Date.PRECISION.DAY or t2.precision<Date.PRECISION.DAY then | ||
guessLevel = DAY_GUESS | guessLevel = DAY_GUESS | ||
end | end | ||
local t1 = os.time({ | local t1 = os.time({ | ||
year = | year = t1.year, | ||
month = | month = t1.month or 6, | ||
day = | day = t1.day or 16 | ||
}) | |||
t2= os.time({ | |||
year = t2.year, | |||
month = t2.month or 6, | |||
day = t2.day or 16 | |||
}) | }) | ||
local dif=os.difftime (t2, t1) | local dif = os.difftime (t2, t1) | ||
local lang = mw.getContentLanguage() | local lang = mw.getContentLanguage() | ||
local readableInterval = lang:getDurationIntervals(dif, {'years', 'days'}) | local readableInterval = lang:getDurationIntervals(dif, {'years', 'days'}) | ||
| שורה 164: | שורה 870: | ||
end | end | ||
if diffFormat=="auto" then | if diffFormat=="auto" then | ||
if dif<=60*60*24 then | |||
return '' -- Ambiguous date range - we arent handling preceision of less than 1 day (hours, minutes, seconds) | |||
end | |||
if guessLevel==MONTH_GUESS and readableInterval['years']==0 then | if guessLevel==MONTH_GUESS and readableInterval['years']==0 then | ||
return '' -- Ambiguous date range | return '' -- Ambiguous date range | ||
| שורה 172: | שורה 880: | ||
if readableInterval['years']>0 and (readableInterval['days']<30 or (365-readableInterval['days'])<30) then | if readableInterval['years']>0 and (readableInterval['days']<30 or (365-readableInterval['days'])<30) then | ||
-- around | -- around | ||
dif = dif- | if readableInterval['days']<30 then | ||
dif = dif - readableInterval['days']*(60*60*24) | |||
else | |||
dif = dif+((365-readableInterval['days'])*(60*60*24)) | |||
end | |||
diffFormat = {'years'} | diffFormat = {'years'} | ||
else | else | ||
| שורה 182: | שורה 894: | ||
end | end | ||
end | end | ||
end | end | ||
if diffFormat=="raw" then | if diffFormat=="raw" then | ||
return dif | return dif | ||
else | else | ||
local res = outputPrefix..lang:formatDuration(dif, diffFormat) | |||
-- post process formatDuration which is not good enough | |||
res= mw.ustring.gsub(res,"כ־([א-ת])","כ%1") | |||
res= mw.ustring.gsub(res,"וגם ([0-9])","ו־%1") | |||
res= mw.ustring.gsub(res,"וגם ([א-ת])","ו%1") | |||
return res | |||
end | end | ||
end | end | ||
| שורה 197: | שורה 914: | ||
elseif frame.args[2] == 'שנים' then | elseif frame.args[2] == 'שנים' then | ||
diffFormat = {'years'} | diffFormat = {'years'} | ||
elseif frame.args[2] == 'שנים וימים' then | |||
diffFormat = {'years', 'days'} | |||
elseif frame.args[2] == "הפרש" then | elseif frame.args[2] == "הפרש" then | ||
diffFormat = "raw" | diffFormat = "raw" | ||
| שורה 204: | שורה 923: | ||
diffFormat = {'years'} | diffFormat = {'years'} | ||
end | end | ||
local inclusive= (frame.args["כולל"]=="כן") | local inclusive = (frame.args["כולל"]=="כן") | ||
local success, res = pcall(parseDateRange, frame.args[1], diffFormat, inclusive) | local success, res = pcall(parseDateRange, frame.args[1], diffFormat, inclusive) | ||
if success then | if success then | ||
local str=res | local str=res | ||
-- the following translations are needed because the underline function | -- the following translations are needed because the underline function | ||
-- local format is wierd | -- local format is wierd | ||
str = mw.ustring.gsub(str, | str = mw.ustring.gsub(str, "(כ)־([א-ת])", "%1%2") | ||
if frame.args[2] == "גיל" then | if frame.args[2] == "גיל" then | ||
| שורה 223: | שורה 938: | ||
end | end | ||
-- This parameter returns the difference as number of years, without any text. | -- This parameter returns the difference as number of years, without any text. | ||
if frame.args[2] == "מספר" then | if frame.args[2] == "מספר" then | ||
str= mw.ustring.gsub(str,"כ(.+)","%1"); | str= mw.ustring.gsub(str,"כ(.+)","%1"); | ||
| שורה 234: | שורה 949: | ||
str = 1 | str = 1 | ||
end | end | ||
str = mw.ustring.gsub(str," שנה", "") | |||
if tonumber(str) > 0 and | if tonumber(str) > 0 and | ||
tonumber(parseDateRange(frame.args[1], "raw", inclusive)) < 0 then | tonumber(parseDateRange(frame.args[1], "raw", inclusive)) < 0 then | ||
| שורה 255: | שורה 971: | ||
dateType='D' | dateType='D' | ||
end | end | ||
local success, res = pcall( parseStrDate, frame.args[1], dateType ) | local success, res = pcall( parseStrDate, frame.args[1], dateType ) | ||
if success then | if success then | ||
| שורה 268: | שורה 984: | ||
end | end | ||
return | function linkStrDateUnsafe(frame) | ||
['חשב'] = parseStrDateSafe | local dateStr = frame.args[1] | ||
local linkedDateStr = dateStr | |||
-- Strip [ and ] chars | |||
linkedDateStr = mw.ustring.gsub(linkedDateStr, '%[', '') | |||
linkedDateStr = mw.ustring.gsub(linkedDateStr, '%]', '') | |||
-- Link D M Y, return [[D M]] [[Y]] | |||
linkedDateStr = mw.ustring.gsub(linkedDateStr, '^(%d+ %a+) (%d+)$', '[[%1]] [[%2]]') | |||
-- Link D M, return [[D M]] | |||
if mw.ustring.find(linkedDateStr, '%[') == nil then | |||
linkedDateStr = mw.ustring.gsub(linkedDateStr, '^(%d+) (%a+)$', '[[%1 %2]]') | |||
end | |||
-- Link M Y, return [[M]] [[Y]] | |||
if mw.ustring.find(linkedDateStr, '%[') == nil then | |||
linkedDateStr = mw.ustring.gsub(linkedDateStr, '^(%a+) (%d+)$', '[[%1]] [[%2]]') | |||
end | |||
-- Link Y, return [[Y]] | |||
if mw.ustring.find(linkedDateStr, '%[') == nil then | |||
linkedDateStr = mw.ustring.gsub(linkedDateStr, '^(%d+)$', '[[%1]]') | |||
end | |||
-- Unknown date string format, return the original string | |||
if mw.ustring.find(linkedDateStr, '%[') == nil then | |||
linkedDateStr = dateStr | |||
end | |||
return linkedDateStr | |||
end | |||
Date['חשב'] = parseStrDateSafe; | |||
Date['חשב טווח'] = parseDateRangeSafe; | |||
Date['parseDateRange'] = parseDateRange; | |||
Date['מקושר'] = linkStrDateUnsafe; | |||
return Date | |||