לדלג לתוכן

יחידה:תאריך: הבדלים בין גרסאות בדף

מתוך צפונות ויקי
חופים הם לפעמים געגועים לנחל. ראיתי פעם חוף שנחל עזבו עם לב שבור של חול ואבן. והאדם, והאדם הוא לפעמים גם כן יכול להישאר נטוש ובלי כוחות ממש
מ 3 גרסאות של הדף wikipedia:he:יחידה:תאריך יובאו
 
(43 גרסאות ביניים של 6 משתמשים אינן מוצגות)
שורה 1: שורה 1:
local Date = {}
local Date = {}
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
--Internal functions
שורה 151: שורה 160:
         local parts = mw.text.split( time, ':' )
         local parts = mw.text.split( time, ':' )
         definition.hour = tonumber( parts[1] )
         definition.hour = tonumber( parts[1] )
         definition.minute = tonumber( parts[4] )
         definition.minute = tonumber( parts[2] )
         definition.second = tonumber( parts[3] )
         definition.second = tonumber( parts[3] )
     end
     end


     --ofset
     --offset
     if offset ~= nil then
     if offset ~= nil then
         if offset == 'Z' then
         if offset == 'Z' then
שורה 199: שורה 208:
     return ((year % 4) == 0) and
     return ((year % 4) == 0) and
             (not (((year % 100) == 0) and ((year % 400) ~= 0)))
             (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
end


שורה 247: שורה 263:
end
end


local function le(t1, t2)
-- 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 t1.calendar ~= t2.calendar then
error("Calanders dont match", 2)
if correct_calender then
t1 = Date.julianToGregorian(t1)
t2 = Date.julianToGregorian(t2)
else
error("Calendars don't match", 2)
end
end
end
if t1.year < t2.year then
if t1.year < t2.year then
שורה 355: שורה 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, "[%[%]]", "" )
wikitext = mw.ustring.gsub( wikitext, "[%[%]]", "" )
שורה 363: שורה 456:
wikitext = mw.ustring.gsub(wikitext, "&rlm;","")
wikitext = mw.ustring.gsub(wikitext, "&rlm;","")
wikitext = mw.ustring.gsub(wikitext, "&lrm;","")
wikitext = mw.ustring.gsub(wikitext, "&lrm;","")
mw.log('תאריך' .. ":" .. wikitext)
-- BC to minus
-- BC to minus
wikitext = mw.ustring.gsub( wikitext, "([0-9]+) לפנה\"ס" , "-%1")
wikitext = mw.ustring.gsub( wikitext, "([0-9]+) לפנה[\"״]ס" , "-%1")
for a in pairs(months) do
for a in pairs(months) do
wikitext = mw.ustring.gsub(wikitext, 'ב?'..a, months[a])  
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(wikitext, '%a'), "Unexpected format")
assert(not mw.ustring.find(wikitext, '%a'), "Unexpected format")
local parts = mw.text.split(mw.text.trim(wikitext),' ')
local parts = mw.text.split(wikitext,' ')


     local definition = {}
     local definition = {}
definition.calendar = calendar
if #parts==3 then -- DMY date
if #parts==3 then -- DMY date
definition.year = tonumber(parts[3])
definition.year = tonumber(parts[3])
שורה 451: שורה 548:
     end
     end
     return iso .. ':' .. prepend( self.second, '0', 2 ) .. formatUtcOffsetForIso( self.utcoffset )
     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
end


שורה 460: שורה 644:
function Date:toString( language )
function Date:toString( language )
     if language == nil then
     if language == nil then
         language = mw.language.getContentLanguage()
         return self:toIso8601()
     elseif type( language ) == 'string' then
    end
    if language == 'he' then
    return self:toHebrewString()
     end
    --[[if type( language ) == 'string' then
         language = mw.language.new( language )
         language = mw.language.new( language )
     end
     end
 
   
     --return language:formatDate( 'r', self:toIso8601() )
     return language:formatDate( 'r', self:toIso8601() )]]
     return self:toIso8601()
     return self:toIso8601()
end
end
שורה 517: שורה 705:
     return false
     return false
end
end
--[[
    Supported calendar models
]]--
Date.CALENDAR = {
GREGORIAN = 'Gregorian',
JULIAN    = 'Julian'
}


function Date.age(time1, time2)
function Date.age(time1, time2)
שורה 530: שורה 710:
         time2 = Date.newFromIso8601(mw.getContentLanguage():formatDate('c', nil, true), true)
         time2 = Date.newFromIso8601(mw.getContentLanguage():formatDate('c', nil, true), true)
     end
     end
    local age = time2.year - time1.year
local age = {year, month, day}
    if time2.month < time1.month or (time2.month == time1.month and time2.day < time1.day) then
age.year = time2.year - time1.year
        age = age - 1
age.month = time2.month - time1.month
    end
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
     if time1.year < 0 and time2.year > 0 then
         age = age - 1
         age.year = age.year - 1
     end
     end
    return age
 
return age
end
end


function Date:formatDate(options)
function Date:formatDate(options)
שורה 610: שורה 802:
function parseDateRange(dateRangeStr, diffFormat, inclusive )
function parseDateRange(dateRangeStr, diffFormat, inclusive )
-- remove footnotes
-- remove footnotes
dateRangeStr = mw.ustring.gsub(dateRangeStr, "(\127UNIQ[^\127]+QINU\127)", "")
dateRangeStr = mw.text.killMarkers(dateRangeStr)
dateRangeStr = mw.ustring.gsub(dateRangeStr, "&rlm;","")
dateRangeStr = mw.ustring.gsub(dateRangeStr, "&rlm;","")
   mw.log("טווח תאריכים:" .. dateRangeStr)
    
local outputPrefix = ''
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)")
שורה 620: שורה 812:
local t1 = Date.newFromWikitext( parts[1] )
local t1 = Date.newFromWikitext( parts[1] )
local t2
local t2
if #parts==2 then
local useCurrent = #parts<2 or (parts[2] == 'היום' or parts[2]=='הווה')
if not useCurrent then
t2 = Date.newFromWikitext( parts[2] )
t2 = Date.newFromWikitext( parts[2] )
else
else
שורה 637: שורה 831:
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
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
return '' -- Ambiguous date range
-- 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
שורה 647: שורה 846:
guessLevel = DAY_GUESS
guessLevel = DAY_GUESS
end
end
 
local t1 = os.time({
local t1 = os.time({
year = t1.year,
year = t1.year,
שורה 671: שורה 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
שורה 679: שורה 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-((readableInterval['days']<30 and 1) or -1)*readableInterval['days']*(60*60*24)
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
שורה 689: שורה 894:
end
end
end
end
end
end
 
if diffFormat=="raw" then
if diffFormat=="raw" then
return dif
return dif
else
else
return outputPrefix..lang:formatDuration(dif, diffFormat)
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
שורה 704: שורה 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"
שורה 711: שורה 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")
"(כ)־([א-ת])", "%1%2")
str= mw.ustring.gsub(str,"וגם ([0-9])","ו-%1")
str= mw.ustring.gsub(str,"וגם ([א-ת])","ו%1");


if frame.args[2] == "גיל" then
if frame.args[2] == "גיל" then
שורה 740: שורה 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
שורה 761: שורה 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
שורה 772: שורה 982:


end
end
end
function linkStrDateUnsafe(frame)
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
end


שורה 777: שורה 1,014:
Date['חשב טווח'] =  parseDateRangeSafe;
Date['חשב טווח'] =  parseDateRangeSafe;
Date['parseDateRange'] =  parseDateRange;
Date['parseDateRange'] =  parseDateRange;
Date['מקושר'] = linkStrDateUnsafe;


return Date
return Date

גרסה אחרונה מ־20:14, 18 ביוני 2023

יחידה זו מיועדת לביצוע פעולות נפוצות על תאריכים.

  • #חשב - מקבלת תאריך טקסטואלי ופורמט ומחלצת את מהתאריך את הפורמט הרצוי
  • #חשב טווח - מקבלת טווח תאריכים ומחשבת את ההפרש ביניהם
פורמט דוגמה תוצאה
יום {{#invoke:תאריך|חשב|[[3 בפברואר]] [[2013]]|יום}} 3
יום {{#invoke:תאריך|חשב|[[3 בפברואר]] [[2013]]{{הערה|א}}|יום}} 3
חודש {{#invoke:תאריך|חשב|[[3 בפברואר]] [[2013]]|חודש}} 2
שנה {{#invoke:תאריך|חשב|[[3 בפברואר]] [[2013]]|שנה}} 2013
שנה {{#invoke:תאריך|חשב|[[פברואר]] [[2013]]|שנה}} 2013
TS {{#invoke:תאריך|חשב|[[3 בפברואר]] [[2013]]|TS}} 2013-02-03

הערות:

  • פורמט TS ניתן להעביר לפונקציות זמן של הוראות תנאי.
  • ההמרה מתעלמת מסוגריים מרובעות של קישורים
  • ניתן להוסיף פרמטר error והתוכן שלו יוצג במקרה של שגיאה בתאריך. (רצוי להכניס במקרה כזה את הדף לקטגוריה כלשהי שתאפשר מעקב אחרי דפים לא תקינים. ברירת המחדל היא קטגוריה:דפים עם שגיאות בתאריך)
דוגמה תוצאה
פורמט ברירת מחדל: אוטומטי {{#invoke:תאריך|חשב טווח|[[3 בפברואר]] [[1947]] - [[15 בספטמבר]] [[2013]]}} 66 שנים
פורמט ברירת מחדל: אוטומטי (דוגמה עם שנים וסימן מינוס) {{#invoke:תאריך|חשב טווח|1947 - 2013}} כ־66 שנים
פורמט ברירת מחדל: אוטומטי (דוגמה עם שנים וסימן קו מפריד) {{#invoke:תאריך|חשב טווח|1947–2013}} כ־66 שנים
שנים {{#invoke:תאריך|חשב טווח|[[3 בפברואר]] [[1947]] - [[15 בספטמבר]] [[2013]]|שנים}} 66 שנים
גיל {{#invoke:תאריך|חשב טווח|1 במרץ 1922 - 4 בנובמבר 1995|גיל}} 73
גיל {{#invoke:תאריך|חשב טווח|1 במרץ 1922{{הערה|ב}} - 4 בנובמבר 1995|גיל}} 73
גיל {{#invoke:תאריך|חשב טווח|1 במרץ 1994{{הערה|ב}} - 4 בנובמבר 1995|גיל}} שנה
גיל {{#invoke:תאריך|חשב טווח|1 במרץ 1993{{הערה|ב}} - 4 בנובמבר 1995|גיל}} שנתיים
מספר {{#invoke:תאריך|חשב טווח|1 במרץ 1922 - 4 בנובמבר 1995|מספר}} 73
מספר {{#invoke:תאריך|חשב טווח|1 במרץ 1922{{הערה|ב}} - 4 בנובמבר 1995|מספר}} 73
מספר {{#invoke:תאריך|חשב טווח|1 במרץ 1994{{הערה|ב}} - 4 בנובמבר 1995|מספר}} 1
מספר {{#invoke:תאריך|חשב טווח|1 במרץ 1993{{הערה|ב}} - 4 בנובמבר 1995|מספר}} 2
שנים לפנה"ס {{#invoke:תאריך|חשב טווח|[[3 בפברואר]] 1900 לפנה"ס - [[15 בספטמבר]] [[2013]]|שנים}} 3,913 שנים
ימים {{#invoke:תאריך|חשב טווח|[[3 בפברואר]] [[1947]] - [[15 בספטמבר]] [[2013]]|ימים}} 24,331 ימים
הפרש {{#invoke:תאריך|חשב טווח|[[3 בפברואר]] [[1947]] - [[15 בספטמבר]] [[2013]]|הפרש}} 2102198400
ללא תאריך יעד {{#invoke:תאריך|חשב טווח|[[3 בפברואר]] [[1947]]|שנים}} 79 שנים
לא כולל היום האחרון {{#invoke:תאריך|חשב טווח|5 ביוני 1967 - 10 ביוני 1967}} 5 ימים
כולל היום האחרון {{#invoke:תאריך|חשב טווח|5 ביוני 1967 - 10 ביוני 1967|כולל=כן}} 6 ימים
כולל היום האחרון {{#invoke:תאריך|חשב טווח|14 ספטמבר 2014 - 14 ספטמבר 2014|כולל=כן}}
שנה ויום {{#invoke:תאריך|חשב טווח|14 ספטמבר 2014 - 15 ספטמבר 2015|כולל=כן}} שנה

הערות:

  • כאשר לא ניתן תאריך יעד תאריך היעד נקבע כזמן הנוכחי
  • הפרש - מציין את הפרש הזמנים בשניות וללא מילים. ניתן להשתמש בערך המוחזר בחלוקה מתאימה לקבלת יחידות זמן אחרות.
  • ניתן להוסיף פרמטר כולל=כן, ואז נלקח בחשבון גם היום האחרון. כשמדובר על טווח זמן ארוך והפורמט נבחר אוטומטי או שמכיל שנים, מתקבלת תוצאה מקורבת.
  • ניתן להוסיף פרמטר error והתוכן שלו יוצג במקרה של שגיאה בטווח התאריכים. (רצוי להכניס במקרה כזה את הדף לקטגוריה כלשהי שתאפשר מעקב אחרי דפים לא תקינים. ברירת המחדל היא קטגוריה:דפים עם שגיאות בתאריך)
  • אם לא מצוין פורמט, הוא נבחר אוטומטית:
    • עד ל-3 שבועות הוא מוצג בימים
    • עד שנה הוא מוצג בימים ושבועות
    • עד ל-10 שנים בשנים ושבועות
    • מעבר לזה רק בשנים

דוגמאות לשימוש ביחידה ופלט לדוגמה מודגמים ביחידה:תאריך/בדיקות.


  1. ^ א
  2. ^ ב
  3. ^ ב
  4. ^ ב
  5. ^ ב
  6. ^ ב
  7. ^ ב
local Date = {}
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,
		['פברואר']= 2,
		['מרץ']= 3,
		['אפריל']= 4,
		['מאי']= 5,
		['יוני']= 6,
		['יולי']= 7,
		['אוגוסט']= 8,
		['ספטמבר']= 9,
		['אוקטובר']= 10,
		['נובמבר']= 11,
		['דצמבר']= 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 ]
	wikitext = mw.ustring.gsub( wikitext, "[%[%]]", "" )
	-- Remove footnotes & directionality markers
	wikitext = mw.text.killMarkers( wikitext )
	wikitext = mw.ustring.gsub(wikitext, "&rlm;","")
	wikitext = mw.ustring.gsub(wikitext, "&lrm;","")
	
	-- BC to minus
	wikitext = mw.ustring.gsub( wikitext, "([0-9]+) לפנה[\"״]ס" , "-%1")
	
	for a in pairs(months) do
		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
	-- if there are alphabet chars return nil (unexpected character)
	assert(not mw.ustring.find(wikitext, '%a'), "Unexpected format")
	local parts = mw.text.split(mw.text.trim(wikitext),' ')

    local definition = {}
	definition.calendar = calendar
	if #parts==3 then -- DMY date
		definition.year = tonumber(parts[3])
		definition.month = tonumber(parts[2])
		definition.day = tonumber(parts[1])
		assert(definition.year, "Could not recognize year")
		assert(definition.month<13 and definition.month>0, "Could not recognize month number")
		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
			res = datetime.year
		elseif dateType=='M' then
			res = datetime.month 
		elseif dateType=='D' then
			res = datetime.day
		elseif dateType == 'TS' then
			res = datetime:toIso8601()
		end
	elseif  datetime.precision >= Date.PRECISION.MONTH then -- MY date

		if dateType=='Y' then
			res = datetime.year
		elseif dateType=='M' then
			res = datetime.month
		elseif dateType == 'TS' then
			res = datetime:toIso8601()
		end
	else --Y date
		if dateType=='Y' then
			res = datetime.year
		elseif dateType == 'TS' then
			res = datetime:toIso8601()
		end
	end
	return res
end

function parseDateRange(dateRangeStr, diffFormat, inclusive )
	-- remove footnotes
	dateRangeStr = mw.text.killMarkers(dateRangeStr)
	dateRangeStr = mw.ustring.gsub(dateRangeStr, "&rlm;","")
   
	local outputPrefix = ''
	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)")
	
	-- parse dates
	local t1 = Date.newFromWikitext( parts[1] )
	local t2
	local useCurrent = #parts<2 or (parts[2] == 'היום' or parts[2]=='הווה')
	
	if not useCurrent then
		t2 = Date.newFromWikitext( parts[2] )
	else
		t2 = Date.newFromIso8601(mw.getContentLanguage():formatDate('c', nil, true), true)
	end
	
	local hasYears = (diffFormat=='auto')
	local hasDays = (diffFormat=='auto')
	for i=1,#diffFormat do  
		if (diffFormat[i]=='years') then 
			hasYears=true
		elseif diffFormat[i]=='days' then
			hasDays =true
		end 
	end
		
	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
	local NO_GUESS, MONTH_GUESS, DAY_GUESS = 0, 1, 2
	local guessLevel = NO_GUESS
	if t1.precision<Date.PRECISION.MONTH or t2.precision<Date.PRECISION.MONTH then 
		guessLevel = MONTH_GUESS
		inclusive=true
	elseif t1.precision<Date.PRECISION.DAY or t2.precision<Date.PRECISION.DAY then
		guessLevel = DAY_GUESS
	end
	
	local t1 = os.time({
		year = t1.year,
		month = t1.month or 6,
		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 lang = mw.getContentLanguage()
	local readableInterval = lang:getDurationIntervals(dif, {'years', 'days'})
	readableInterval['days'] = readableInterval['days'] or 0
	readableInterval['years'] = readableInterval['years'] or 0
	if not (guessLevel==NO_GUESS) and not (guessLevel==DAY_GUESS and hasYears and #diffFormat==1 and readableInterval['days']>31) then 
		outputPrefix='כ־'
	end
	if inclusive then
		dif = dif+60*60*24 -- include last day
	end

	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
			return '' -- Ambiguous date range
		end
		--for intervals of around year 
		if readableInterval['years']>0 and (readableInterval['days']<30 or (365-readableInterval['days'])<30) then
			-- around
			if readableInterval['days']<30 then
				dif = dif - readableInterval['days']*(60*60*24)
			else
				dif = dif+((365-readableInterval['days'])*(60*60*24))
			end
			diffFormat = {'years'}
		else
			local diffDays = dif/(60*60*24)
			if diffDays<7*3 then diffFormat = { 'days' }
			elseif diffDays<364 then diffFormat = {'weeks', 'days'}
			elseif diffDays<10*365 then diffFormat = {'years', 'weeks'}
			else  diffFormat = {'years'} 
			end
		end
	end
	
	if diffFormat=="raw" then
		return dif
	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

function parseDateRangeSafe(frame)
	local diffFormat = 'auto'
	if frame.args[2] == 'ימים' then
		diffFormat = {'days'}
	elseif frame.args[2] == 'שנים' then
		diffFormat = {'years'}
	elseif frame.args[2] == 'שנים וימים' then
		diffFormat = {'years', 'days'}
	elseif frame.args[2] == "הפרש" then
		diffFormat = "raw"
	elseif frame.args[2] == "גיל" then
		diffFormat = {'years'}
	elseif frame.args[2] == "מספר" then
		diffFormat = {'years'}
	end

	local inclusive = (frame.args["כולל"]=="כן")
	local success, res = pcall(parseDateRange, frame.args[1], diffFormat, inclusive)

	if success then
		local str=res
		-- the following translations are needed because the underline function
		-- local format is wierd
		str = mw.ustring.gsub(str,  "(כ)־([א-ת])", "%1%2")

		if frame.args[2] == "גיל" then
			str= mw.ustring.gsub(str,"כ־(.+) שנים","%1 בערך")
			str= mw.ustring.gsub(str," שנים","")
		end
		
	-- This parameter returns the difference as number of years, without any text.
		if frame.args[2] == "מספר" then
			str= mw.ustring.gsub(str,"כ(.+)","%1");
			str= mw.ustring.gsub(str,"־(.+)","%1");
			str= mw.ustring.gsub(str," שנים","")
			if str == "שנתיים" then
				str = 2
			end
			if str == "שנה" then
				str = 1
			end
			str = mw.ustring.gsub(str," שנה", "")
			if tonumber(str) > 0 and
					tonumber(parseDateRange(frame.args[1], "raw", inclusive)) < 0 then
				str = -1 * str
			end
		end
		
		return str
	else
		return frame.args['error'] or '<span class="scribunto-error">שגיאת תאריך: '..res..'</span>[[קטגוריה:דפים עם שגיאות בתאריך]]'
	end
end

function parseStrDateSafe(frame)
	local dateType = frame.args[2]
	if dateType =='שנה' then
		dateType = 'Y'
	elseif dateType=='חודש' then
		dateType = 'M'
	elseif dateType=='יום' then
		dateType='D'
	end

	local success, res = pcall( parseStrDate, frame.args[1], dateType )
	if success then
		if dateType=='Y' and mw.ustring.sub( res, 0, 1)=='-' then
			res = mw.ustring.sub( res, 2).. ' לפנה"ס'
		end
		return res
	else
		return frame.args['error'] or '<span class="scribunto-error">שגיאת תאריך: '..res..'</span>[[קטגוריה:דפים עם שגיאות בתאריך]]'

	end
end

function linkStrDateUnsafe(frame)
	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