Bu Lua modülü 123.000+ sayfada kullanılmaktadır. Bundan dolayı üzerinde yapılan değişiklikler hemen fark edilecektir. Lütfen yapacağınız değişiklikleri öncelikle şablonun ile alt sayfalarında ya da kendi test ediniz. Eğer yapmayı planladığınız değişiklikler ile istediğiniz sonucu elde edemiyorsanız görüş alışverişinde bulununuz. |
Bu modül, Modül:Kategori ve {{}} şablonunda kullanılmaktadır. Lütfen modülde değişiklik yapmadan önce test ediniz.
-- Date functions for use by other modules. -- I18N and time zones are not supported. local MINUS = '−' -- Unicode U+2212 MINUS SIGN local floor = math.floor local Date, DateDiff, diffmt -- forward declarations local uniq = { 'unique identifier' } --bu fonksiyon girilen bir yazıda birinci değeri bulup --onu ikinci değer ile değiştirmeye yarar local function bulvedeg(yazi, bir, iki) return mw.ustring.sub(mw.ustring.gsub(yazi, bir, iki), 1, -1) end local aylar = { ["January"] = "Ocak", ["February"] = "Şubat", ["March"] = "Mart", ["April"] = "Nisan", ["May"] = "Mayıs", ["June"] = "Haziran", ["July"] = "Temmuz", ["August"] = "Ağustos", ["September"] = "Eylül", ["October"] = "Ekim", ["November"] = "Kasım", ["December"] = "Aralık", } local function _Ceviri(frame) local args = frame.args[1] and frame.args or frame:getParent().args local sonuc = args[1] or "" for i,v in pairs(aylar) do sonuc = bulvedeg(sonuc, i, v) end return sonuc end --yerelde kod kısaltmak için local bul = mw.ustring.find local function ayarlaMO(yil) return bulvedeg(yil, "-([%d])", "MÖ %1") end local function temizle(yil) local son = bulvedeg(yil, "^MÖ ", "-") son = bulvedeg(son, "%'l[ea]r$", "") son = bulvedeg(son, ". yüzyıl", "") son = bulvedeg(son, ". binyıl", "") return son end --bu fonksiyon girilen bir yılın bulunduğu on yılı saptayarak --onun çıktısını verir, mesela 2019 girilirse 2010 --1888 girilirse de 1880 çıktısını verir local function onYil(yil, arti) yil = temizle(yil) local tire = bul(yil, "^-") and "-" or nil local oy = bulvedeg(yil, "^-", "") local bas = string.len(oy) == 1 and "0" or mw.ustring.sub(oy, 1, -2) .. 0 --eğer yılın sondan bir önceki sayısı "1,3,4,6,9" ise --lar eki kullanılacak, bunların dışında ler local harf = (bul(bas, "[13469][%d]$") or bas == "0") and "a" or "e" --ve eğer yılın bulunduğu onyılı bulmak istiyorsak son rakamı --her zaman 0 isteriz, mesela 2015 yılı giriliyse bu yıl, --2010 onyılına aittir local hesap = (tire and tire or "") .. bas if arti then local hesap2 = tonumber(hesap) + arti if tonumber(hesap2) < 0 and not tire then hesap2 = (tonumber(hesap2) + 10 == 0 and "-" or "") .. (tonumber(hesap2) + 10) elseif tonumber(hesap2) > 0 and tire then hesap2 = (tonumber(hesap2) - 10) elseif tonumber(hesap2) == 0 and tire then hesap2 = "-0'lar" end return onYil(hesap2) else return hesap .. "'l" .. harf .. "r" end end local function yuzYil(yil, arti) local kademe = bul(yil, ". yüzyıl") and "yüzyıl" or "yıl" yil = temizle(yil) local tire = bul(yil, "^-") and "-" or nil yy = bulvedeg(yil, "^-", "") local bas = (string.len(yy) == 1 or string.len(yy) == 2) and 1 or tonumber(mw.ustring.sub(yy, 1, -3)+1) local hesap = (tire and tire or "") .. (kademe == "yüzyıl" and tostring(yy) or tostring(bas)) if arti then local hesap2 = tonumber(hesap) + arti if tonumber(hesap2) <= 0 and not tire then hesap2 = (tonumber(hesap2) - 1) elseif tonumber(hesap2) >= 0 and tire then hesap2 = (tonumber(hesap2) + 1) end return hesap2 .. ". yüzyıl" else return hesap .. ". yüzyıl" end end local function binYil(yil, arti, kademe) kademe = kademe and kademe or (bul(yil, ". binyıl") and "binyıl" or ( bul(yil, ". yüzyıl") and "yüzyıl" or ( bul(yil, "%'l[ea]r$") and "onyıl" or "yıl"))) yil = temizle(yil) local tire = bul(yil, "^-") and "-" or nil local sonuc = bulvedeg(yil, "^-", "") if kademe == "yıl" then sonuc = math.ceil(sonuc / 1000) end if kademe == "onyıl" then sonuc = math.ceil((sonuc+9) / 1000) end if kademe == "yüzyıl" then sonuc = math.ceil(sonuc / 10) end local hesap = (tire and tire or "") .. tostring(sonuc) if arti then local hesap2 = tonumber(hesap) + arti if tonumber(hesap2) <= 0 and not tire then hesap2 = (tonumber(hesap2) - 1) elseif tonumber(hesap2) >= 0 and tire then hesap2 = (tonumber(hesap2) + 1) end return hesap2 .. ". binyıl" else return hesap .. ". binyıl" end end local function YIL(frame) local args = frame.args[1] and frame.args or frame:getParent().args args[1] = temizle(args[1]) if args[2] then args[1] = tonumber(args[1])+tonumber(args[2]) end return args[1] == "0" and "Milat" or ayarlaMO(args[1]) end local function ONYIL(frame) local args = frame.args[1] and frame.args or frame:getParent().args return ayarlaMO(onYil(args[1], args[2])) end local function YUZYIL(frame) local args = frame.args[1] and frame.args or frame:getParent().args return ayarlaMO(yuzYil(args[1], args[2])) end local function BINYIL(frame) local args = frame.args[1] and frame.args or frame:getParent().args return ayarlaMO(binYil(args[1], args[2], args[3])) end local function is_date(t) -- The system used to make a date read-only means there is no unique -- metatable that is conveniently accessible to check. return type(t) == 'table' and t._id == uniq end local function is_diff(t) return type(t) == 'table' and getmetatable(t) == diffmt end local function _list_join(list, sep) return table.concat(list, sep) end local function collection() -- Return a table to hold items. return { n = 0, add = function (self, item) self.n = self.n + 1 self[self.n] = item end, join = _list_join, } end local function strip_to_nil(text) -- If text is a string, return its trimmed content, or nil if empty. -- Otherwise return text (convenient when Date fields are provided from -- another module which may pass a string, a number, or another type). if type(text) == 'string' then text = text:match('(%S.-)%s*$') end return text end local function is_leap_year(year, calname) -- Return true if year is a leap year. if calname == 'Julian' then return year % 4 == 0 end return (year % 4 == 0 and year % 100 ~= 0) or year % 400 == 0 end local function days_in_month(year, month, calname) -- Return number of days (1..31) in given month (1..12). if month == 2 and is_leap_year(year, calname) then return 29 end return ({ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 })[month] end local function h_m_s(time) -- Return hour, minute, second extracted from fraction of a day. time = floor(time * 24 * 3600 + 0.5) -- number of seconds local second = time % 60 time = floor(time / 60) return floor(time / 60), time % 60, second end local function hms(date) -- Return fraction of a day from date's time, where (0 <= fraction < 1) -- if the values are valid, but could be anything if outside range. return (date.hour + (date.minute + date.second / 60) / 60) / 24 end local function julian_date(date) -- Return jd, jdz from a Julian or Gregorian calendar date where -- jd = Julian date and its fractional part is zero at noon -- jdz = same, but assume time is 00:00:00 if no time given -- http://www.tondering.dk/claus/cal/julperiod.php#formula -- Testing shows this works for all dates from year -9999 to 9999! -- JDN 0 is the 24-hour period starting at noon UTC on Monday -- 1 Ocak 4713 BC = (-4712, 1, 1) Julian calendar -- 24 Kasım 4714 BC = (-4713, 11, 24) Gregorian calendar local offset local a = floor((14 - date.month)/12) local y = date.year + 4800 - a if date.calendar == 'Julian' then offset = floor(y/4) - 32083 else offset = floor(y/4) - floor(y/100) + floor(y/400) - 32045 end local m = date.month + 12*a - 3 local jd = date.day + floor((153*m + 2)/5) + 365*y + offset if date.hastime then jd = jd + hms(date) - 0.5 return jd, jd end return jd, jd - 0.5 end local function set_date_from_jd(date) -- Set the fields of table date from its Julian date field. -- Return true if date is valid. -- http://www.tondering.dk/claus/cal/julperiod.php#formula -- This handles the proleptic Julian and Gregorian calendars. -- Negative Julian dates are not defined but they work. local calname = date.calendar local low, high -- min/max limits for date ranges −9999-01-01 to 9999-12-31 if calname == 'Gregorian' then low, high = -1930999.5, 5373484.49999 elseif calname == 'Julian' then low, high = -1931076.5, 5373557.49999 else return end local jd = date.jd if not (type(jd) == 'number' and low <= jd and jd <= high) then return end local jdn = floor(jd) if date.hastime then local time = jd - jdn -- 0 <= time < 1 if time >= 0.5 then -- if at or after midnight of next day jdn = jdn + 1 time = time - 0.5 else time = time + 0.5 end date.hour, date.minute, date.second = h_m_s(time) else date.second = 0 date.minute = 0 date.hour = 0 end local b, c if calname == 'Julian' then b = 0 c = jdn + 32082 else -- Gregorian local a = jdn + 32044 b = floor((4*a + 3)/146097) c = a - floor(146097*b/4) end local d = floor((4*c + 3)/1461) local e = c - floor(1461*d/4) local m = floor((5*e + 2)/153) date.day = e - floor((153*m + 2)/5) + 1 date.month = m + 3 - 12*floor(m/10) date.year = 100*b + d - 4800 + floor(m/10) return true end local function fix_numbers(numbers, y, m, d, H, M, S, partial, hastime, calendar) -- Put the result of normalizing the given values in table numbers. -- The result will have valid m, d values if y is valid; caller checks y. -- The logic of PHP mktime is followed where m or d can be zero to mean -- the previous unit, and -1 is the one before that, etc. -- Positive values carry forward. local date if not (1 <= m and m <= 12) then date = Date(y, 1, 1) if not date then return end date = date + ((m - 1) .. 'm') y, m = date.year, date.month end local days_hms if not partial then if hastime and H and M and S then if not (0 <= H and H <= 23 and 0 <= M and M <= 59 and 0 <= S and S <= 59) then days_hms = hms({ hour = H, minute = M, second = S }) end end if days_hms or not (1 <= d and d <= days_in_month(y, m, calendar)) then date = date or Date(y, m, 1) if not date then return end date = date + (d - 1 + (days_hms or 0)) y, m, d = date.year, date.month, date.day if days_hms then H, M, S = date.hour, date.minute, date.second end end end numbers.year = y numbers.month = m numbers.day = d if days_hms then -- Don't set H unless it was valid because a valid H will set hastime. numbers.hour = H numbers.minute = M numbers.second = S end end local function set_date_from_numbers(date, numbers, options) -- Set the fields of table date from numeric values. -- Return true if date is valid. if type(numbers) ~= 'table' then return end local y = numbers.year or date.year local m = numbers.month or date.month local d = numbers.day or date.day local H = numbers.hour local M = numbers.minute or date.minute or 0 local S = numbers.second or date.second or 0 local need_fix if y and m and d then date.partial = nil if not (-9999 <= y and y <= 9999 and 1 <= m and m <= 12 and 1 <= d and d <= days_in_month(y, m, date.calendar)) then if not date.want_fix then return end need_fix = true end elseif y and date.partial then if d or not (-9999 <= y and y <= 9999) then return end if m and not (1 <= m and m <= 12) then if not date.want_fix then return end need_fix = true end else return end if date.partial then H = nil -- ignore any time M = nil S = nil else if H then -- It is not possible to set M or S without also setting H. date.hastime = true else H = 0 end if not (0 <= H and H <= 23 and 0 <= M and M <= 59 and 0 <= S and S <= 59) then if date.want_fix then need_fix = true else return end end end date.want_fix = nil if need_fix then fix_numbers(numbers, y, m, d, H, M, S, date.partial, date.hastime, date.calendar) return set_date_from_numbers(date, numbers, options) end date.year = y -- -9999 to 9999 ('n BC' → year = 1 - n) date.month = m -- 1 to 12 (may be nil if partial) date.day = d -- 1 to 31 (* = nil if partial) date.hour = H -- 0 to 59 (*) date.minute = M -- 0 to 59 (*) date.second = S -- 0 to 59 (*) if type(options) == 'table' then for _, k in ipairs({ 'am', 'era', 'format' }) do if options[k] then date.options[k] = options[k] end end end return true end local function make_option_table(options1, options2) -- If options1 is a string, return a table with its settings, or -- if it is a table, use its settings. -- Missing options are set from table options2 or defaults. -- If a default is used, a flag is set so caller knows the value was not intentionally set. -- Valid option settings are: -- am: 'am', 'a.m.', 'AM', 'A.M.' -- 'pm', 'p.m.', 'PM', 'P.M.' (each has same meaning as corresponding item above) -- era: 'BCMINUS', 'BCNEGATIVE', 'BC', 'B.C.', 'BCE', 'B.C.E.', 'AD', 'A.D.', 'CE', 'C.E.' -- Option am = 'am' does not mean the hour is AM; it means 'am' or 'pm' is used, depending on the hour, -- and am = 'pm' has the same meaning. -- Similarly, era = 'BC' means 'BC' is used if year <= 0. -- BCMINUS displays a MINUS if year < 0 and the display format does not include %{era}. -- BCNEGATIVE is similar but displays a hyphen. local result = { bydefault = {} } if type(options1) == 'table' then result.am = options1.am result.era = options1.era elseif type(options1) == 'string' then -- Example: 'am:AM era:BC' or 'am=AM era=BC'. for item in options1:gmatch('%S+') do local lhs, rhs = item:match('^(%w+)[:=](.+)$') if lhs then result[lhs] = rhs end end end options2 = type(options2) == 'table' and options2 or {} local defaults = { am = 'am', era = 'BC' } for k, v in pairs(defaults) do if not result[k] then if options2[k] then result[k] = options2[k] else result[k] = v result.bydefault[k] = true end end end return result end local ampm_options = { -- lhs = input text accepted as an am/pm option -- rhs = code used internally ['am'] = 'am', ['AM'] = 'AM', ['a.m.'] = 'a.m.', ['A.M.'] = 'A.M.', ['pm'] = 'am', -- same as am ['PM'] = 'AM', ['p.m.'] = 'a.m.', ['P.M.'] = 'A.M.', } local era_text = { -- Text for displaying an era with a positive year (after adjusting -- by replacing year with 1 - year if date.year <= 0). -- options.era = { year<=0 , year>0 } ['BCMINUS'] = { 'BC' , '' , isbc = true, sign = MINUS }, ['BCNEGATIVE'] = { 'BC' , '' , isbc = true, sign = '-' }, ['BC'] = { 'BC' , '' , isbc = true }, ['B.C.'] = { 'B.C.' , '' , isbc = true }, ['BCE'] = { 'BCE' , '' , isbc = true }, ['B.C.E.'] = { 'B.C.E.', '' , isbc = true }, ['AD'] = { 'BC' , 'AD' }, ['A.D.'] = { 'B.C.' , 'A.D.' }, ['CE'] = { 'BCE' , 'CE' }, ['C.E.'] = { 'B.C.E.', 'C.E.' }, -- Türkçe kısaltmalar. ['M.Ö.'] = { 'M.Ö.' , '' , isbc = true }, ['M.S.'] = { 'M.Ö.' , 'M.S.' }, } local function get_era_for_year(era, year) return (era_text[era] or era_text['BC'])[year > 0 and 2 or 1] or '' end local function strftime(date, format, options) -- Return date formatted as a string using codes similar to those -- in the C strftime library function. local sformat = string.format local shortcuts = { ['%c'] = '%-I:%M %p %-d %B %-Y %{era}', -- date and time: 2:30 pm 1 Nisan 2016 ['%x'] = '%-d %B %-Y %{era}', -- date: 1 Nisan 2016 ['%X'] = '%-I:%M %p', -- time: 2:30 pm } if shortcuts[format] then format = shortcuts[format] end local codes = { a = { field = 'dayabbr' }, A = { field = 'dayname' }, b = { field = 'monthabbr' }, B = { field = 'monthname' }, u = { fmt = '%d' , field = 'dowiso' }, w = { fmt = '%d' , field = 'dow' }, d = { fmt = '%02d', fmt2 = '%d', field = 'day' }, m = { fmt = '%02d', fmt2 = '%d', field = 'month' }, Y = { fmt = '%04d', fmt2 = '%d', field = 'year' }, H = { fmt = '%02d', fmt2 = '%d', field = 'hour' }, M = { fmt = '%02d', fmt2 = '%d', field = 'minute' }, S = { fmt = '%02d', fmt2 = '%d', field = 'second' }, j = { fmt = '%03d', fmt2 = '%d', field = 'dayofyear' }, I = { fmt = '%02d', fmt2 = '%d', field = 'hour', special = 'hour12' }, p = { field = 'hour', special = 'am' }, } options = make_option_table(options, date.options) local amopt = options.am local eraopt = options.era local function replace_code(spaces, modifier, id) local code = codes[id] if code then local fmt = code.fmt if modifier == '-' and code.fmt2 then fmt = code.fmt2 end local value = date[code.field] if not value then return nil -- an undefined field in a partial date end local special = code.special if special then if special == 'hour12' then value = value % 12 value = value == 0 and 12 or value elseif special == 'am' then local ap = ({ ['a.m.'] = { 'a.m.', 'p.m.' }, ['AM'] = { 'AM', 'PM' }, ['A.M.'] = { 'A.M.', 'P.M.' }, })[ampm_options[amopt]] or { 'am', 'pm' } return (spaces == '' and '' or ' ') .. (value < 12 and ap[1] or ap[2]) end end if code.field == 'year' then local sign = (era_text[eraopt] or {}).sign if not sign or format:find('%{era}', 1, true) then sign = '' if value <= 0 then value = 1 - value end else if value >= 0 then sign = '' else value = -value end end return spaces .. sign .. sformat(fmt, value) end return spaces .. (fmt and sformat(fmt, value) or value) end end local function replace_property(spaces, id) if id == 'era' then -- Special case so can use local era option. local result = get_era_for_year(eraopt, date.year) if result == '' then return '' end return (spaces == '' and '' or ' ') .. result end local result = date[id] if type(result) == 'string' then return spaces .. result end if type(result) == 'number' then return spaces .. tostring(result) end if type(result) == 'boolean' then return spaces .. (result and '1' or '0') end -- This occurs if id is an undefined field in a partial date, or is the name of a function. return nil end local PERCENT = '\127PERCENT\127' return (format :gsub('%%%%', PERCENT) :gsub('(%s*)%%{(%w+)}', replace_property) :gsub('(%s*)%%(%-?)(%a)', replace_code) :gsub(PERCENT, '%%') ) end local function _date_text(date, fmt, options) -- Return a formatted string representing the given date. if not is_date(date) then error('date:text: need a date (use "date:text()" with a colon)', 2) end if type(fmt) == 'string' and fmt:match('%S') then if fmt:find('%', 1, true) then return strftime(date, fmt, options) end elseif date.partial then fmt = date.month and 'my' or 'y' else fmt = 'dmy' if date.hastime then fmt = (date.second > 0 and 'hms ' or 'hm ') .. fmt end end local function bad_format() -- For consistency with other format processing, return given format -- (or cleaned format if original was not a string) if invalid. return mw.text.nowiki(fmt) end if date.partial then -- Ignore days in standard formats like 'ymd'. if fmt == 'ym' or fmt == 'ymd' then fmt = date.month and '%Y-%m %{era}' or '%Y %{era}' elseif fmt == 'my' or fmt == 'dmy' or fmt == 'mdy' then fmt = date.month and '%B %-Y %{era}' or '%-Y %{era}' elseif fmt == 'y' then fmt = date.month and '%-Y %{era}' or '%-Y %{era}' else return bad_format() end return strftime(date, fmt, options) end local function hm_fmt() local plain = make_option_table(options, date.options).bydefault.am return plain and '%H:%M' or '%-I:%M %p' end local need_time = date.hastime local t = collection() for item in fmt:gmatch('%S+') do local f if item == 'hm' then f = hm_fmt() need_time = false elseif item == 'hms' then f = '%H:%M:%S' need_time = false elseif item == 'ymd' then f = '%Y-%m-%d %{era}' elseif item == 'mdy' then f = '%B %-d, %-Y %{era}' elseif item == 'dmy' then f = '%-d %B %-Y %{era}' else return bad_format() end t:add(f) end fmt = t:join(' ') if need_time then fmt = hm_fmt() .. ' ' .. fmt end return strftime(date, fmt, options) end local day_info = { -- 0=Paz to 6=Cts [0] = { 'Paz', 'Pazar' }, { 'Pzt', 'Pazartesi' }, { 'Sal', 'Salı' }, { 'Çar', 'Çarşamba' }, { 'Per', 'Perşembe' }, { 'Cum', 'Cuma' }, { 'Cts', 'Cumartesi' }, } local month_info = { -- 1=Oca to 12=Ara { 'Oca', 'Ocak' }, { 'Şub', 'Şubat' }, { 'Mar', 'Mart' }, { 'Nis', 'Nisan' }, { 'May', 'Mayıs' }, { 'Haz', 'Haziran' }, { 'Tem', 'Temmuz' }, { 'Agu', 'Ağustos' }, { 'Eyl', 'Eylül' }, { 'Eki', 'Ekim' }, { 'Kas', 'Kasım' }, { 'Ara', 'Aralık' }, } local function name_to_number(text, translate) if type(text) == 'string' then return translate[text:lower()] end end local function day_number(text) return name_to_number(text, { paz = 0, pazar = 0, pzt = 1, pazartesi = 1, sal = 2, sali = 2, car = 3, carsamba = 3, per = 4, persembe = 4, cum = 5, cuma = 5, cts = 6, cumartesi = 6, }) end -- Türkçe karakterleri tanıtalım local function month_number(text) local turkish_chars = { ["ş"] = "s", ["ı"] = "i", ["ç"] = "c", ["ğ"] = "g", ["ö"] = "o", ["ü"] = "u", } local cleaned_text = text:lower() :gsub("[%z\1-\127\194-\244][\128-\191]*", turkish_chars) -- Türkçe karakterleri değiştir :gsub(" ", "") -- Boşlukları kaldır return name_to_number(cleaned_text, { oca = 1, ocak = 1, sub = 2, subat = 2, mar = 3, mart = 3, nis = 4, nisan = 4, may = 5, mayis = 5, haz = 6, haziran = 6, tem = 7, temmuz = 7, agu = 8, agustos = 8, eyl = 9, eylul = 9, eki = 10, ekim = 10, kas = 11, kasim = 11, ara = 12, aralik = 12, }) end local function _list_text(list, fmt) -- Return a list of formatted strings from a list of dates. if not type(list) == 'table' then error('date:list:text: need "list:text()" with a colon', 2) end local result = { join = _list_join } for i, date in ipairs(list) do result[i] = date:text(fmt) end return result end local function _date_list(date, spec) -- Return a possibly empty numbered table of dates meeting the specification. -- Dates in the list are in ascending order (oldest date first). -- The spec should be a string of form "<count> <day> <op>" -- where each item is optional and -- count = number of items wanted in list -- day = abbreviation or name such as Mon or Monday -- op = >, >=, <, <= (default is > meaning after date) -- If no count is given, the list is for the specified days in date's month. -- The default day is date's day. -- The spec can also be a positive or negative number: -- -5 is equivalent to '5 <' -- 5 is equivalent to '5' which is '5 >' if not is_date(date) then error('date:list: need a date (use "date:list()" with a colon)', 2) end local list = { text = _list_text } if date.partial then return list end local count, offset, operation local ops = { ['>='] = { before = false, include = true }, ['>'] = { before = false, include = false }, ['<='] = { before = true , include = true }, ['<'] = { before = true , include = false }, } if spec then if type(spec) == 'number' then count = floor(spec + 0.5) if count < 0 then count = -count operation = ops['<'] end elseif type(spec) == 'string' then local num, day, op = spec:match('^%s*(%d*)%s*(%a*)%s*([<>=]*)%s*$') if not num then return list end if num ~= '' then count = tonumber(num) end if day ~= '' then local dow = day_number(day:gsub('[sS]$', '')) -- accept plural days if not dow then return list end offset = dow - date.dow end operation = ops[op] else return list end end offset = offset or 0 operation = operation or ops['>'] local datefrom, dayfirst, daylast if operation.before then if offset > 0 or (offset == 0 and not operation.include) then offset = offset - 7 end if count then if count > 1 then offset = offset - 7*(count - 1) end datefrom = date + offset else daylast = date.day + offset dayfirst = daylast % 7 if dayfirst == 0 then dayfirst = 7 end end else if offset < 0 or (offset == 0 and not operation.include) then offset = offset + 7 end if count then datefrom = date + offset else dayfirst = date.day + offset daylast = date.monthdays end end if not count then if daylast < dayfirst then return list end count = floor((daylast - dayfirst)/7) + 1 datefrom = Date(date, {day = dayfirst}) end for i = 1, count do if not datefrom then break end -- exceeds date limits list[i] = datefrom datefrom = datefrom + 7 end return list end -- A table to get the current date/time (UTC), but only if needed. local current = setmetatable({}, { __index = function (self, key) local d = os.date('!*t') self.year = d.year self.month = d.month self.day = d.day self.hour = d.hour self.minute = d.min self.second = d.sec return rawget(self, key) end }) local function extract_date(newdate, text) -- Parse the date/time in text and return n, o where -- n = table of numbers with date/time fields -- o = table of options for AM/PM or AD/BC or format, if any -- or return nothing if date is known to be invalid. -- Caller determines if the values in n are valid. -- A year must be positive ('1' to '9999'); use 'BC' for BC. -- In a y-m-d string, the year must be four digits to avoid ambiguity -- ('0001' to '9999'). The only way to enter year <= 0 is by specifying -- the date as three numeric parameters like ymd Date(-1, 1, 1). -- Dates of form d/m/y, m/d/y, y/m/d are rejected as potentially ambiguous. local date, options = {}, {} if text:sub(-1) == 'Z' then -- Extract date/time from a Wikidata timestamp. -- The year can be 1 to 16 digits but this module handles 1 to 4 digits only. -- Examples: '+2016-06-21T14:30:00Z', '-0000000180-00-00T00:00:00Z'. local sign, y, m, d, H, M, S = text:match('^([+%-])(%d+)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)Z$') if sign then y = tonumber(y) if sign == '-' and y > 0 then y = -y end if y <= 0 then options.era = 'BCE' end date.year = y m = tonumber(m) d = tonumber(d) H = tonumber(H) M = tonumber(M) S = tonumber(S) if m == 0 then newdate.partial = true return date, options end date.month = m if d == 0 then newdate.partial = true return date, options end date.day = d if H > 0 or M > 0 or S > 0 then date.hour = H date.minute = M date.second = S end return date, options end return end local function extract_ymd(item) -- Called when no day or month has been set. local y, m, d = item:match('^(%d%d%d%d)%-(%w+)%-(%d%d?)$') if y then if date.year then return end if m:match('^%d%d?$') then m = tonumber(m) else m = month_number(m) end if m then date.year = tonumber(y) date.month = m date.day = tonumber(d) return true end end end local function extract_day_or_year(item) -- Called when a day would be valid, or -- when a year would be valid if no year has been set and partial is set. local number, suffix = item:match('^(%d%d?%d?%d?)(.*)$') if number then local n = tonumber(number) if #number <= 2 and n <= 31 then suffix = suffix:lower() if suffix == '' or suffix == 'st' or suffix == 'nd' or suffix == 'rd' or suffix == 'th' then date.day = n return true end elseif suffix == '' and newdate.partial and not date.year then date.year = n return true end end end local function extract_month(item) -- A month must be given as a name or abbreviation; a number could be ambiguous. local m = month_number(item) if m then date.month = m return true end end local function extract_time(item) local h, m, s = item:match('^(%d%d?):(%d%d)(:?%d*)$') if date.hour or not h then return end if s ~= '' then s = s:match('^:(%d%d)$') if not s then return end end date.hour = tonumber(h) date.minute = tonumber(m) date.second = tonumber(s) -- nil if empty string return true end local item_count = 0 local index_time local function set_ampm(item) local H = date.hour if H and not options.am and index_time + 1 == item_count then options.am = ampm_options[item] -- caller checked this is not nil if item:match('^[Aa]') then if not (1 <= H and H <= 12) then return end if H == 12 then date.hour = 0 end else if not (1 <= H and H <= 23) then return end if H <= 11 then date.hour = H + 12 end end return true end end for item in text:gsub(',', ' '):gsub(' ', ' '):gmatch('%S+') do item_count = item_count + 1 if era_text[item] then -- Era is accepted in peculiar places. if options.era then return end options.era = item elseif ampm_options[item] then if not set_ampm(item) then return end elseif item:find(':', 1, true) then if not extract_time(item) then return end index_time = item_count elseif date.day and date.month then if date.year then return -- should be nothing more so item is invalid end if not item:match('^(%d%d?%d?%d?)$') then return end date.year = tonumber(item) elseif date.day then if not extract_month(item) then return end elseif date.month then if not extract_day_or_year(item) then return end elseif extract_month(item) then options.format = 'mdy' elseif extract_ymd(item) then options.format = 'ymd' elseif extract_day_or_year(item) then if date.day then options.format = 'dmy' end else return end end if not date.year or date.year == 0 then return end local era = era_text[options.era] if era and era.isbc then date.year = 1 - date.year end return date, options end local function autofill(date1, date2) -- Fill any missing month or day in each date using the -- corresponding component from the other date, if present, -- or with 1 if both dates are missing the month or day. -- This gives a good result for calculating the difference -- between two partial dates when no range is wanted. -- Return filled date1, date2 (two full dates). local function filled(a, b) local fillmonth, fillday if not a.month then fillmonth = b.month or 1 end if not a.day then fillday = b.day or 1 end if fillmonth or fillday then -- need to create a new date a = Date(a, { month = fillmonth, day = fillday }) end return a end return filled(date1, date2), filled(date2, date1) end local function date_add_sub(lhs, rhs, is_sub) -- Return a new date from calculating (lhs + rhs) or (lhs - rhs), -- or return nothing if invalid. -- The result is nil if the calculated date exceeds allowable limits. -- Caller ensures that lhs is a date; its properties are copied for the new date. if lhs.partial then -- Adding to a partial is not supported. -- Can subtract a date or partial from a partial, but this is not called for that. return end local function is_prefix(text, word, minlen) local n = #text return (minlen or 1) <= n and n <= #word and text == word:sub(1, n) end local function do_days(n) local forcetime, jd if floor(n) == n then jd = lhs.jd else forcetime = not lhs.hastime jd = lhs.jdz end jd = jd + (is_sub and -n or n) if forcetime then jd = tostring(jd) if not jd:find('.', 1, true) then jd = jd .. '.0' end end return Date(lhs, 'juliandate', jd) end if type(rhs) == 'number' then -- Add/subtract days, including fractional days. return do_days(rhs) end if type(rhs) == 'string' then -- rhs is a single component like '26m' or '26 months' (with optional sign). -- Fractions like '3.25d' are accepted for the units which are handled as days. local sign, numstr, id = rhs:match('^%s*([+-]?)([%d%.]+)%s*(%a+)$') if sign then if sign == '-' then is_sub = not (is_sub and true or false) end local y, m, days local num = tonumber(numstr) if not num then return end id = id:lower() if is_prefix(id, 'years') then y = num m = 0 elseif is_prefix(id, 'months') then y = floor(num / 12) m = num % 12 elseif is_prefix(id, 'weeks') then days = num * 7 elseif is_prefix(id, 'days') then days = num elseif is_prefix(id, 'hours') then days = num / 24 elseif is_prefix(id, 'minutes', 3) then days = num / (24 * 60) elseif is_prefix(id, 'seconds') then days = num / (24 * 3600) else return end if days then return do_days(days) end if numstr:find('.', 1, true) then return end if is_sub then y = -y m = -m end assert(-11 <= m and m <= 11) y = lhs.year + y m = lhs.month + m if m > 12 then y = y + 1 m = m - 12 elseif m < 1 then y = y - 1 m = m + 12 end local d = math.min(lhs.day, days_in_month(y, m, lhs.calendar)) return Date(lhs, y, m, d) end end if is_diff(rhs) then local days = rhs.age_days if (is_sub or false) ~= (rhs.isnegative or false) then days = -days end return lhs + days end end local full_date_only = { dayabbr = true, dayname = true, dow = true, dayofweek = true, dowiso = true, dayofweekiso = true, dayofyear = true, gsd = true, juliandate = true, jd = true, jdz = true, jdnoon = true, } -- Metatable for a date's calculated fields. local datemt = { __index = function (self, key) if rawget(self, 'partial') then if full_date_only[key] then return end if key == 'monthabbr' or key == 'monthdays' or key == 'monthname' then if not self.month then return end end end local value if key == 'dayabbr' then value = day_info[self.dow][1] elseif key == 'dayname' then value = day_info[self.dow][2] elseif key == 'dow' then value = (self.jdnoon + 1) % 7 -- day-of-week 0=Sun to 6=Sat elseif key == 'dayofweek' then value = self.dow elseif key == 'dowiso' then value = (self.jdnoon % 7) + 1 -- ISO day-of-week 1=Mon to 7=Sun elseif key == 'dayofweekiso' then value = self.dowiso elseif key == 'dayofyear' then local first = Date(self.year, 1, 1, self.calendar).jdnoon value = self.jdnoon - first + 1 -- day-of-year 1 to 366 elseif key == 'era' then -- Era text (never a negative sign) from year and options. value = get_era_for_year(self.options.era, self.year) elseif key == 'format' then value = self.options.format or 'dmy' elseif key == 'gsd' then -- GSD = 1 from 00:00:00 to 23:59:59 on 1 Ocak 1 AD Gregorian calendar, -- which is from jd 1721425.5 to 1721426.49999. value = floor(self.jd - 1721424.5) elseif key == 'juliandate' or key == 'jd' or key == 'jdz' then local jd, jdz = julian_date(self) rawset(self, 'juliandate', jd) rawset(self, 'jd', jd) rawset(self, 'jdz', jdz) return key == 'jdz' and jdz or jd elseif key == 'jdnoon' then -- Julian date at noon (an integer) on the calendar day when jd occurs. value = floor(self.jd + 0.5) elseif key == 'isleapyear' then value = is_leap_year(self.year, self.calendar) elseif key == 'monthabbr' then value = month_info[self.month][1] elseif key == 'monthdays' then value = days_in_month(self.year, self.month, self.calendar) elseif key == 'monthname' then value = month_info[self.month][2] end if value ~= nil then rawset(self, key, value) return value end end, } -- Date operators. local function mt_date_add(lhs, rhs) if not is_date(lhs) then lhs, rhs = rhs, lhs -- put date on left (it must be a date for this to have been called) end return date_add_sub(lhs, rhs) end local function mt_date_sub(lhs, rhs) if is_date(lhs) then if is_date(rhs) then return DateDiff(lhs, rhs) end return date_add_sub(lhs, rhs, true) end end local function mt_date_concat(lhs, rhs) return tostring(lhs) .. tostring(rhs) end local function mt_date_tostring(self) return self:text() end local function mt_date_eq(lhs, rhs) -- Return true if dates identify same date/time where, for example, -- Date(-4712, 1, 1, 'Julian') == Date(-4713, 11, 24, 'Gregorian') is true. -- This is called only if lhs and rhs have the same type and the same metamethod. if lhs.partial or rhs.partial then -- One date is partial; the other is a partial or a full date. -- The months may both be nil, but must be the same. return lhs.year == rhs.year and lhs.month == rhs.month and lhs.calendar == rhs.calendar end return lhs.jdz == rhs.jdz end local function mt_date_lt(lhs, rhs) -- Return true if lhs < rhs, for example, -- Date('1 Oca 2016') < Date('06:00 1 Oca 2016') is true. -- This is called only if lhs and rhs have the same type and the same metamethod. if lhs.partial or rhs.partial then -- One date is partial; the other is a partial or a full date. if lhs.calendar ~= rhs.calendar then return lhs.calendar == 'Julian' end if lhs.partial then lhs = lhs.partial.first end if rhs.partial then rhs = rhs.partial.first end end return lhs.jdz < rhs.jdz end --[[ Examples of syntax to construct a date: Date(y, m, d, 'julian') default calendar is 'gregorian' Date(y, m, d, H, M, S, 'julian') Date('juliandate', jd, 'julian') if jd contains "." text output includes H:M:S Date('currentdate') Date('currentdatetime') Date('1 Nisan 1995', 'julian') parse date from text Date('1 Nisan 1995 AD', 'julian') using an era sets a flag to do the same for output Date('04:30:59 1 Nisan 1995', 'julian') Date(date) copy of an existing date Date(date, t) same, updated with y,m,d,H,M,S fields from table t Date(t) date with y,m,d,H,M,S fields from table t ]] function Date(...) -- for forward declaration above -- Return a table holding a date assuming a uniform calendar always applies -- (proleptic Gregorian calendar or proleptic Julian calendar), or -- return nothing if date is invalid. -- A partial date has a valid year, however its month may be nil, and -- its day and time fields are nil. -- Field partial is set to false (if a full date) or a table (if a partial date). local calendars = { julian = 'Julian', gregorian = 'Gregorian' } local newdate = { _id = uniq, calendar = 'Gregorian', -- default is Gregorian calendar hastime = false, -- true if input sets a time hour = 0, -- always set hour/minute/second so don't have to handle nil minute = 0, second = 0, options = {}, list = _date_list, subtract = function (self, rhs, options) return DateDiff(self, rhs, options) end, text = _date_text, } local argtype, datetext, is_copy, jd_number, tnums local numindex = 0 local numfields = { 'year', 'month', 'day', 'hour', 'minute', 'second' } local numbers = {} for _, v in ipairs({...}) do v = strip_to_nil(v) local vlower = type(v) == 'string' and v:lower() or nil if v == nil then -- Ignore empty arguments after stripping so modules can directly pass template parameters. elseif calendars[vlower] then newdate.calendar = calendars[vlower] elseif vlower == 'partial' then newdate.partial = true elseif vlower == 'fix' then newdate.want_fix = true elseif is_date(v) then -- Copy existing date (items can be overridden by other arguments). if is_copy or tnums then return end is_copy = true newdate.calendar = v.calendar newdate.partial = v.partial newdate.hastime = v.hastime newdate.options = v.options newdate.year = v.year newdate.month = v.month newdate.day = v.day newdate.hour = v.hour newdate.minute = v.minute newdate.second = v.second elseif type(v) == 'table' then if tnums then return end tnums = {} local tfields = { year=1, month=1, day=1, hour=2, minute=2, second=2 } for tk, tv in pairs(v) do if tfields[tk] then tnums[tk] = tonumber(tv) end if tfields[tk] == 2 then newdate.hastime = true end end else local num = tonumber(v) if not num and argtype == 'setdate' and numindex == 1 then num = month_number(v) end if num then if not argtype then argtype = 'setdate' end if argtype == 'setdate' and numindex < 6 then numindex = numindex + 1 numbers[numfields[numindex]] = num elseif argtype == 'juliandate' and not jd_number then jd_number = num if type(v) == 'string' then if v:find('.', 1, true) then newdate.hastime = true end elseif num ~= floor(num) then -- The given value was a number. The time will be used -- if the fractional part is nonzero. newdate.hastime = true end else return end elseif argtype then return elseif type(v) == 'string' then if v == 'currentdate' or v == 'currentdatetime' or v == 'juliandate' then argtype = v else argtype = 'datetext' datetext = v end else return end end end if argtype == 'datetext' then if tnums or not set_date_from_numbers(newdate, extract_date(newdate, datetext)) then return end elseif argtype == 'juliandate' then newdate.partial = nil newdate.jd = jd_number if not set_date_from_jd(newdate) then return end elseif argtype == 'currentdate' or argtype == 'currentdatetime' then newdate.partial = nil newdate.year = current.year newdate.month = current.month newdate.day = current.day if argtype == 'currentdatetime' then newdate.hour = current.hour newdate.minute = current.minute newdate.second = current.second newdate.hastime = true end newdate.calendar = 'Gregorian' -- ignore any given calendar name elseif argtype == 'setdate' then if tnums or not set_date_from_numbers(newdate, numbers) then return end elseif not (is_copy or tnums) then return end if tnums then newdate.jd = nil -- force recalculation in case jd was set before changes from tnums if not set_date_from_numbers(newdate, tnums) then return end end if newdate.partial then local year = newdate.year local month = newdate.month local first = Date(year, month or 1, 1, newdate.calendar) month = month or 12 local last = Date(year, month, days_in_month(year, month), newdate.calendar) newdate.partial = { first = first, last = last } else newdate.partial = false -- avoid index lookup end setmetatable(newdate, datemt) local readonly = {} local mt = { __index = newdate, __newindex = function(t, k, v) error('date.' .. tostring(k) .. ' is read-only', 2) end, __add = mt_date_add, __sub = mt_date_sub, __concat = mt_date_concat, __tostring = mt_date_tostring, __eq = mt_date_eq, __lt = mt_date_lt, } return setmetatable(readonly, mt) end local function _diff_age(diff, code, options) -- Return a tuple of integer values from diff as specified by code, except that -- each integer may be a list of two integers for a diff with a partial date, or -- return nil if the code is not supported. -- If want round, the least significant unit is rounded to nearest whole unit. -- For a duration, an extra day is added. local wantround, wantduration, wantrange if type(options) == 'table' then wantround = options.round wantduration = options.duration wantrange = options.range else wantround = options end if not is_diff(diff) then local f = wantduration and 'duration' or 'age' error(f .. ': need a date difference (use "diff:' .. f .. '()" with a colon)', 2) end if diff.partial then -- Ignore wantround, wantduration. local function choose(v) if type(v) == 'table' then if not wantrange or v[1] == v[2] then -- Example: Date('partial', 2005) - Date('partial', 2001) gives -- diff.years = { 3, 4 } to show the range of possible results. -- If do not want a range, choose the second value as more expected. return v[2] end end return v end if code == 'ym' or code == 'ymd' then if not wantrange and diff.iszero then -- This avoids an unexpected result such as -- Date('partial', 2001) - Date('partial', 2001) -- giving diff = { years = 0, months = { 0, 11 } } -- which would be reported as 0 years and 11 months. return 0, 0 end return choose(diff.partial.years), choose(diff.partial.months) end if code == 'y' then return choose(diff.partial.years) end if code == 'm' or code == 'w' or code == 'd' then return choose({ diff.partial.mindiff:age(code), diff.partial.maxdiff:age(code) }) end return nil end local extra_days = wantduration and 1 or 0 if code == 'wd' or code == 'w' or code == 'd' then local offset = wantround and 0.5 or 0 local days = diff.age_days + extra_days if code == 'wd' or code == 'd' then days = floor(days + offset) if code == 'd' then return days end return floor(days/7), days % 7 end return floor(days/7 + offset) end local H, M, S = diff.hours, diff.minutes, diff.seconds if code == 'dh' or code == 'dhm' or code == 'dhms' or code == 'h' or code == 'hm' or code == 'hms' then local days = floor(diff.age_days + extra_days) local inc_hour if wantround then if code == 'dh' or code == 'h' then if M >= 30 then inc_hour = true end elseif code == 'dhm' or code == 'hm' then if S >= 30 then M = M + 1 if M >= 60 then M = 0 inc_hour = true end end else -- Nothing needed because S is an integer. end if inc_hour then H = H + 1 if H >= 24 then H = 0 days = days + 1 end end end if code == 'dh' or code == 'dhm' or code == 'dhms' then if code == 'dh' then return days, H elseif code == 'dhm' then return days, H, M else return days, H, M, S end end local hours = days * 24 + H if code == 'h' then return hours elseif code == 'hm' then return hours, M end return hours, M, S end if wantround then local inc_hour if code == 'ymdh' or code == 'ymwdh' then if M >= 30 then inc_hour = true end elseif code == 'ymdhm' or code == 'ymwdhm' then if S >= 30 then M = M + 1 if M >= 60 then M = 0 inc_hour = true end end elseif code == 'ymd' or code == 'ymwd' or code == 'yd' or code == 'md' then if H >= 12 then extra_days = extra_days + 1 end end if inc_hour then H = H + 1 if H >= 24 then H = 0 extra_days = extra_days + 1 end end end local y, m, d = diff.years, diff.months, diff.days if extra_days > 0 then d = d + extra_days if d > 28 or code == 'yd' then -- Recalculate in case have passed a month. diff = diff.date1 + extra_days - diff.date2 y, m, d = diff.years, diff.months, diff.days end end if code == 'ymd' then return y, m, d elseif code == 'yd' then if y > 0 then -- It is known that diff.date1 > diff.date2. diff = diff.date1 - (diff.date2 + (y .. 'y')) end return y, floor(diff.age_days) elseif code == 'md' then return y * 12 + m, d elseif code == 'ym' or code == 'm' then if wantround then if d >= 16 then m = m + 1 if m >= 12 then m = 0 y = y + 1 end end end if code == 'ym' then return y, m end return y * 12 + m elseif code == 'ymw' then local weeks = floor(d/7) if wantround then local days = d % 7 if days > 3 or (days == 3 and H >= 12) then weeks = weeks + 1 end end return y, m, weeks elseif code == 'ymwd' then return y, m, floor(d/7), d % 7 elseif code == 'ymdh' then return y, m, d, H elseif code == 'ymwdh' then return y, m, floor(d/7), d % 7, H elseif code == 'ymdhm' then return y, m, d, H, M elseif code == 'ymwdhm' then return y, m, floor(d/7), d % 7, H, M end if code == 'y' then if wantround and m >= 6 then y = y + 1 end return y end return nil end local function _diff_duration(diff, code, options) if type(options) ~= 'table' then options = { round = options } end options.duration = true return _diff_age(diff, code, options) end -- Metatable for some operations on date differences. diffmt = { -- for forward declaration above __concat = function (lhs, rhs) return tostring(lhs) .. tostring(rhs) end, __tostring = function (self) return tostring(self.age_days) end, __index = function (self, key) local value if key == 'age_days' then if rawget(self, 'partial') then local function jdz(date) return (date.partial and date.partial.first or date).jdz end value = jdz(self.date1) - jdz(self.date2) else value = self.date1.jdz - self.date2.jdz end end if value ~= nil then rawset(self, key, value) return value end end, } function DateDiff(date1, date2, options) -- for forward declaration above -- Return a table with the difference between two dates (date1 - date2). -- The difference is negative if date1 is older than date2. -- Return nothing if invalid. -- If d = date1 - date2 then -- date1 = date2 + d -- If date1 >= date2 and the dates have no H:M:S time specified then -- date1 = date2 + (d.years..'y') + (d.months..'m') + d.days -- where the larger time units are added first. -- The result of Date(2015,1,x) + '1m' is Date(2015,2,28) for -- x = 28, 29, 30, 31. That means, for example, -- d = Date(2015,3,3) - Date(2015,1,31) -- gives d.years, d.months, d.days = 0, 1, 3 (excluding date1). if not (is_date(date1) and is_date(date2) and date1.calendar == date2.calendar) then return end local wantfill if type(options) == 'table' then wantfill = options.fill end local isnegative = false local iszero = false if date1 < date2 then isnegative = true date1, date2 = date2, date1 elseif date1 == date2 then iszero = true end -- It is known that date1 >= date2 (period is from date2 to date1). if date1.partial or date2.partial then -- Two partial dates might have timelines: ---------------------A=================B--- date1 is from A to B inclusive --------C=======D-------------------------- date2 is from C to D inclusive -- date1 > date2 iff A > C (date1.partial.first > date2.partial.first) -- The periods can overlap ('Nisan 2001' - '2001'): -------------A===B------------------------- A=2001-04-01 B=2001-04-30 --------C=====================D------------ C=2001-01-01 D=2001-12-31 if wantfill then date1, date2 = autofill(date1, date2) else local function zdiff(date1, date2) local diff = date1 - date2 if diff.isnegative then return date1 - date1 -- a valid diff in case we call its methods end return diff end local function getdate(date, which) return date.partial and date.partial[which] or date end local maxdiff = zdiff(getdate(date1, 'last'), getdate(date2, 'first')) local mindiff = zdiff(getdate(date1, 'first'), getdate(date2, 'last')) local years, months if maxdiff.years == mindiff.years then years = maxdiff.years if maxdiff.months == mindiff.months then months = maxdiff.months else months = { mindiff.months, maxdiff.months } end else years = { mindiff.years, maxdiff.years } end return setmetatable({ date1 = date1, date2 = date2, partial = { years = years, months = months, maxdiff = maxdiff, mindiff = mindiff, }, isnegative = isnegative, iszero = iszero, age = _diff_age, duration = _diff_duration, }, diffmt) end end local y1, m1 = date1.year, date1.month local y2, m2 = date2.year, date2.month local years = y1 - y2 local months = m1 - m2 local d1 = date1.day + hms(date1) local d2 = date2.day + hms(date2) local days, time if d1 >= d2 then days = d1 - d2 else months = months - 1 -- Get days in previous month (before the "to" date) given Aralık has 31 days. local dpm = m1 > 1 and days_in_month(y1, m1 - 1, date1.calendar) or 31 if d2 >= dpm then days = d1 - hms(date2) else days = dpm - d2 + d1 end end if months < 0 then years = years - 1 months = months + 12 end days, time = math.modf(days) local H, M, S = h_m_s(time) return setmetatable({ date1 = date1, date2 = date2, partial = false, -- avoid index lookup years = years, months = months, days = days, hours = H, minutes = M, seconds = S, isnegative = isnegative, iszero = iszero, age = _diff_age, duration = _diff_duration, }, diffmt) end return { YIL = YIL, ONYIL = ONYIL, YUZYIL = YUZYIL, BINYIL = BINYIL, _Ceviri = _Ceviri, _current = current, _Date = Date, _days_in_month = days_in_month, }
wikipedia, wiki, viki, vikipedia, oku, kitap, kütüphane, kütübhane, ara, ara bul, bul, herşey, ne arasanız burada,hikayeler, makale, kitaplar, öğren, wiki, bilgi, tarih, yukle, izle, telefon için, turk, türk, türkçe, turkce, nasıl yapılır, ne demek, nasıl, yapmak, yapılır, indir, ücretsiz, ücretsiz indir, bedava, bedava indir, mp3, video, mp4, 3gp, jpg, jpeg, gif, png, resim, müzik, şarkı, film, film, oyun, oyunlar, mobil, cep telefonu, telefon, android, ios, apple, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, pc, web, computer, bilgisayar
Modul belgelemesi gor degistir gecmis temizle Bu Lua modulu 123 000 sayfada kullanilmaktadir Bundan dolayi uzerinde yapilan degisiklikler hemen fark edilecektir Lutfen yapacaginiz degisiklikleri oncelikle sablonun ile alt sayfalarinda ya da kendi kullanici sayfanizda test ediniz Eger yapmayi planladiginiz degisiklikler ile istediginiz sonucu elde edemiyorsaniz gorus alisverisinde bulununuz Bu modul Modul Kategori ve otomatik kategori sablonunda kullanilmaktadir Lutfen modulde degisiklik yapmadan once test ediniz Yukaridaki belgeleme icerigi Modul Tarih belge sayfasindan yansitilmaktadir degistir gecmis Kullanicilar denemelerini bu sablonun deneme tahtasi olustur yansitma ve test senaryosu olustur sayfalarinda yapabilirler Lutfen kategorileri belge alt sayfasina ekleyin Bu modul ile ilgili alt sayfalar icin buraya tiklayiniz Date functions for use by other modules I18N and time zones are not supported local MINUS Unicode U 2212 MINUS SIGN local floor math floor local Date DateDiff diffmt forward declarations local uniq unique identifier bu fonksiyon girilen bir yazida birinci degeri bulup onu ikinci deger ile degistirmeye yarar local function bulvedeg yazi bir iki return mw ustring sub mw ustring gsub yazi bir iki 1 1 end local aylar January Ocak February Subat March Mart April Nisan May Mayis June Haziran July Temmuz August Agustos September Eylul October Ekim November Kasim December Aralik local function Ceviri frame local args frame args 1 and frame args or frame getParent args local sonuc args 1 or for i v in pairs aylar do sonuc bulvedeg sonuc i v end return sonuc end yerelde kod kisaltmak icin local bul mw ustring find local function ayarlaMO yil return bulvedeg yil d MO 1 end local function temizle yil local son bulvedeg yil MO son bulvedeg son l ea r son bulvedeg son yuzyil son bulvedeg son binyil return son end bu fonksiyon girilen bir yilin bulundugu on yili saptayarak onun ciktisini verir mesela 2019 girilirse 2010 1888 girilirse de 1880 ciktisini verir local function onYil yil arti yil temizle yil local tire bul yil and or nil local oy bulvedeg yil local bas string len oy 1 and 0 or mw ustring sub oy 1 2 0 eger yilin sondan bir onceki sayisi 1 3 4 6 9 ise lar eki kullanilacak bunlarin disinda ler local harf bul bas 13469 d or bas 0 and a or e ve eger yilin bulundugu onyili bulmak istiyorsak son rakami her zaman 0 isteriz mesela 2015 yili giriliyse bu yil 2010 onyilina aittir local hesap tire and tire or bas if arti then local hesap2 tonumber hesap arti if tonumber hesap2 lt 0 and not tire then hesap2 tonumber hesap2 10 0 and or tonumber hesap2 10 elseif tonumber hesap2 gt 0 and tire then hesap2 tonumber hesap2 10 elseif tonumber hesap2 0 and tire then hesap2 0 lar end return onYil hesap2 else return hesap l harf r end end local function yuzYil yil arti local kademe bul yil yuzyil and yuzyil or yil yil temizle yil local tire bul yil and or nil yy bulvedeg yil local bas string len yy 1 or string len yy 2 and 1 or tonumber mw ustring sub yy 1 3 1 local hesap tire and tire or kademe yuzyil and tostring yy or tostring bas if arti then local hesap2 tonumber hesap arti if tonumber hesap2 lt 0 and not tire then hesap2 tonumber hesap2 1 elseif tonumber hesap2 gt 0 and tire then hesap2 tonumber hesap2 1 end return hesap2 yuzyil else return hesap yuzyil end end local function binYil yil arti kademe kademe kademe and kademe or bul yil binyil and binyil or bul yil yuzyil and yuzyil or bul yil l ea r and onyil or yil yil temizle yil local tire bul yil and or nil local sonuc bulvedeg yil if kademe yil then sonuc math ceil sonuc 1000 end if kademe onyil then sonuc math ceil sonuc 9 1000 end if kademe yuzyil then sonuc math ceil sonuc 10 end local hesap tire and tire or tostring sonuc if arti then local hesap2 tonumber hesap arti if tonumber hesap2 lt 0 and not tire then hesap2 tonumber hesap2 1 elseif tonumber hesap2 gt 0 and tire then hesap2 tonumber hesap2 1 end return hesap2 binyil else return hesap binyil end end local function YIL frame local args frame args 1 and frame args or frame getParent args args 1 temizle args 1 if args 2 then args 1 tonumber args 1 tonumber args 2 end return args 1 0 and Milat or ayarlaMO args 1 end local function ONYIL frame local args frame args 1 and frame args or frame getParent args return ayarlaMO onYil args 1 args 2 end local function YUZYIL frame local args frame args 1 and frame args or frame getParent args return ayarlaMO yuzYil args 1 args 2 end local function BINYIL frame local args frame args 1 and frame args or frame getParent args return ayarlaMO binYil args 1 args 2 args 3 end local function is date t The system used to make a date read only means there is no unique metatable that is conveniently accessible to check return type t table and t id uniq end local function is diff t return type t table and getmetatable t diffmt end local function list join list sep return table concat list sep end local function collection Return a table to hold items return n 0 add function self item self n self n 1 self self n item end join list join end local function strip to nil text If text is a string return its trimmed content or nil if empty Otherwise return text convenient when Date fields are provided from another module which may pass a string a number or another type if type text string then text text match S s end return text end local function is leap year year calname Return true if year is a leap year if calname Julian then return year 4 0 end return year 4 0 and year 100 0 or year 400 0 end local function days in month year month calname Return number of days 1 31 in given month 1 12 if month 2 and is leap year year calname then return 29 end return 31 28 31 30 31 30 31 31 30 31 30 31 month end local function h m s time Return hour minute second extracted from fraction of a day time floor time 24 3600 0 5 number of seconds local second time 60 time floor time 60 return floor time 60 time 60 second end local function hms date Return fraction of a day from date s time where 0 lt fraction lt 1 if the values are valid but could be anything if outside range return date hour date minute date second 60 60 24 end local function julian date date Return jd jdz from a Julian or Gregorian calendar date where jd Julian date and its fractional part is zero at noon jdz same but assume time is 00 00 00 if no time given http www tondering dk claus cal julperiod php formula Testing shows this works for all dates from year 9999 to 9999 JDN 0 is the 24 hour period starting at noon UTC on Monday 1 Ocak 4713 BC 4712 1 1 Julian calendar 24 Kasim 4714 BC 4713 11 24 Gregorian calendar local offset local a floor 14 date month 12 local y date year 4800 a if date calendar Julian then offset floor y 4 32083 else offset floor y 4 floor y 100 floor y 400 32045 end local m date month 12 a 3 local jd date day floor 153 m 2 5 365 y offset if date hastime then jd jd hms date 0 5 return jd jd end return jd jd 0 5 end local function set date from jd date Set the fields of table date from its Julian date field Return true if date is valid http www tondering dk claus cal julperiod php formula This handles the proleptic Julian and Gregorian calendars Negative Julian dates are not defined but they work local calname date calendar local low high min max limits for date ranges 9999 01 01 to 9999 12 31 if calname Gregorian then low high 1930999 5 5373484 49999 elseif calname Julian then low high 1931076 5 5373557 49999 else return end local jd date jd if not type jd number and low lt jd and jd lt high then return end local jdn floor jd if date hastime then local time jd jdn 0 lt time lt 1 if time gt 0 5 then if at or after midnight of next day jdn jdn 1 time time 0 5 else time time 0 5 end date hour date minute date second h m s time else date second 0 date minute 0 date hour 0 end local b c if calname Julian then b 0 c jdn 32082 else Gregorian local a jdn 32044 b floor 4 a 3 146097 c a floor 146097 b 4 end local d floor 4 c 3 1461 local e c floor 1461 d 4 local m floor 5 e 2 153 date day e floor 153 m 2 5 1 date month m 3 12 floor m 10 date year 100 b d 4800 floor m 10 return true end local function fix numbers numbers y m d H M S partial hastime calendar Put the result of normalizing the given values in table numbers The result will have valid m d values if y is valid caller checks y The logic of PHP mktime is followed where m or d can be zero to mean the previous unit and 1 is the one before that etc Positive values carry forward local date if not 1 lt m and m lt 12 then date Date y 1 1 if not date then return end date date m 1 m y m date year date month end local days hms if not partial then if hastime and H and M and S then if not 0 lt H and H lt 23 and 0 lt M and M lt 59 and 0 lt S and S lt 59 then days hms hms hour H minute M second S end end if days hms or not 1 lt d and d lt days in month y m calendar then date date or Date y m 1 if not date then return end date date d 1 days hms or 0 y m d date year date month date day if days hms then H M S date hour date minute date second end end end numbers year y numbers month m numbers day d if days hms then Don t set H unless it was valid because a valid H will set hastime numbers hour H numbers minute M numbers second S end end local function set date from numbers date numbers options Set the fields of table date from numeric values Return true if date is valid if type numbers table then return end local y numbers year or date year local m numbers month or date month local d numbers day or date day local H numbers hour local M numbers minute or date minute or 0 local S numbers second or date second or 0 local need fix if y and m and d then date partial nil if not 9999 lt y and y lt 9999 and 1 lt m and m lt 12 and 1 lt d and d lt days in month y m date calendar then if not date want fix then return end need fix true end elseif y and date partial then if d or not 9999 lt y and y lt 9999 then return end if m and not 1 lt m and m lt 12 then if not date want fix then return end need fix true end else return end if date partial then H nil ignore any time M nil S nil else if H then It is not possible to set M or S without also setting H date hastime true else H 0 end if not 0 lt H and H lt 23 and 0 lt M and M lt 59 and 0 lt S and S lt 59 then if date want fix then need fix true else return end end end date want fix nil if need fix then fix numbers numbers y m d H M S date partial date hastime date calendar return set date from numbers date numbers options end date year y 9999 to 9999 n BC year 1 n date month m 1 to 12 may be nil if partial date day d 1 to 31 nil if partial date hour H 0 to 59 date minute M 0 to 59 date second S 0 to 59 if type options table then for k in ipairs am era format do if options k then date options k options k end end end return true end local function make option table options1 options2 If options1 is a string return a table with its settings or if it is a table use its settings Missing options are set from table options2 or defaults If a default is used a flag is set so caller knows the value was not intentionally set Valid option settings are am am a m AM A M pm p m PM P M each has same meaning as corresponding item above era BCMINUS BCNEGATIVE BC B C BCE B C E AD A D CE C E Option am am does not mean the hour is AM it means am or pm is used depending on the hour and am pm has the same meaning Similarly era BC means BC is used if year lt 0 BCMINUS displays a MINUS if year lt 0 and the display format does not include era BCNEGATIVE is similar but displays a hyphen local result bydefault if type options1 table then result am options1 am result era options1 era elseif type options1 string then Example am AM era BC or am AM era BC for item in options1 gmatch S do local lhs rhs item match w if lhs then result lhs rhs end end end options2 type options2 table and options2 or local defaults am am era BC for k v in pairs defaults do if not result k then if options2 k then result k options2 k else result k v result bydefault k true end end end return result end local ampm options lhs input text accepted as an am pm option rhs code used internally am am AM AM a m a m A M A M pm am same as am PM AM p m a m P M A M local era text Text for displaying an era with a positive year after adjusting by replacing year with 1 year if date year lt 0 options era year lt 0 year gt 0 BCMINUS BC isbc true sign MINUS BCNEGATIVE BC isbc true sign BC BC isbc true B C B C isbc true BCE BCE isbc true B C E B C E isbc true AD BC AD A D B C A D CE BCE CE C E B C E C E Turkce kisaltmalar M O M O isbc true M S M O M S local function get era for year era year return era text era or era text BC year gt 0 and 2 or 1 or end local function strftime date format options Return date formatted as a string using codes similar to those in the C strftime library function local sformat string format local shortcuts c I M p d B Y era date and time 2 30 pm 1 Nisan 2016 x d B Y era date 1 Nisan 2016 X I M p time 2 30 pm if shortcuts format then format shortcuts format end local codes a field dayabbr A field dayname b field monthabbr B field monthname u fmt d field dowiso w fmt d field dow d fmt 02d fmt2 d field day m fmt 02d fmt2 d field month Y fmt 04d fmt2 d field year H fmt 02d fmt2 d field hour M fmt 02d fmt2 d field minute S fmt 02d fmt2 d field second j fmt 03d fmt2 d field dayofyear I fmt 02d fmt2 d field hour special hour12 p field hour special am options make option table options date options local amopt options am local eraopt options era local function replace code spaces modifier id local code codes id if code then local fmt code fmt if modifier and code fmt2 then fmt code fmt2 end local value date code field if not value then return nil an undefined field in a partial date end local special code special if special then if special hour12 then value value 12 value value 0 and 12 or value elseif special am then local ap a m a m p m AM AM PM A M A M P M ampm options amopt or am pm return spaces and or amp nbsp value lt 12 and ap 1 or ap 2 end end if code field year then local sign era text eraopt or sign if not sign or format find era 1 true then sign if value lt 0 then value 1 value end else if value gt 0 then sign else value value end end return spaces sign sformat fmt value end return spaces fmt and sformat fmt value or value end end local function replace property spaces id if id era then Special case so can use local era option local result get era for year eraopt date year if result then return end return spaces and or amp nbsp result end local result date id if type result string then return spaces result end if type result number then return spaces tostring result end if type result boolean then return spaces result and 1 or 0 end This occurs if id is an undefined field in a partial date or is the name of a function return nil end local PERCENT 127 PERCENT 127 return format gsub PERCENT gsub s w replace property gsub s a replace code gsub PERCENT end local function date text date fmt options Return a formatted string representing the given date if not is date date then error date text need a date use date text with a colon 2 end if type fmt string and fmt match S then if fmt find 1 true then return strftime date fmt options end elseif date partial then fmt date month and my or y else fmt dmy if date hastime then fmt date second gt 0 and hms or hm fmt end end local function bad format For consistency with other format processing return given format or cleaned format if original was not a string if invalid return mw text nowiki fmt end if date partial then Ignore days in standard formats like ymd if fmt ym or fmt ymd then fmt date month and Y m era or Y era elseif fmt my or fmt dmy or fmt mdy then fmt date month and B Y era or Y era elseif fmt y then fmt date month and Y era or Y era else return bad format end return strftime date fmt options end local function hm fmt local plain make option table options date options bydefault am return plain and H M or I M p end local need time date hastime local t collection for item in fmt gmatch S do local f if item hm then f hm fmt need time false elseif item hms then f H M S need time false elseif item ymd then f Y m d era elseif item mdy then f B d Y era elseif item dmy then f d B Y era else return bad format end t add f end fmt t join if need time then fmt hm fmt fmt end return strftime date fmt options end local day info 0 Paz to 6 Cts 0 Paz Pazar Pzt Pazartesi Sal Sali Car Carsamba Per Persembe Cum Cuma Cts Cumartesi local month info 1 Oca to 12 Ara Oca Ocak Sub Subat Mar Mart Nis Nisan May Mayis Haz Haziran Tem Temmuz Agu Agustos Eyl Eylul Eki Ekim Kas Kasim Ara Aralik local function name to number text translate if type text string then return translate text lower end end local function day number text return name to number text paz 0 pazar 0 pzt 1 pazartesi 1 sal 2 sali 2 car 3 carsamba 3 per 4 persembe 4 cum 5 cuma 5 cts 6 cumartesi 6 end Turkce karakterleri tanitalim local function month number text local turkish chars s s i i c c g g o o u u local cleaned text text lower gsub z 1 127 194 244 128 191 turkish chars Turkce karakterleri degistir gsub Bosluklari kaldir return name to number cleaned text oca 1 ocak 1 sub 2 subat 2 mar 3 mart 3 nis 4 nisan 4 may 5 mayis 5 haz 6 haziran 6 tem 7 temmuz 7 agu 8 agustos 8 eyl 9 eylul 9 eki 10 ekim 10 kas 11 kasim 11 ara 12 aralik 12 end local function list text list fmt Return a list of formatted strings from a list of dates if not type list table then error date list text need list text with a colon 2 end local result join list join for i date in ipairs list do result i date text fmt end return result end local function date list date spec Return a possibly empty numbered table of dates meeting the specification Dates in the list are in ascending order oldest date first The spec should be a string of form lt count gt lt day gt lt op gt where each item is optional and count number of items wanted in list day abbreviation or name such as Mon or Monday op gt gt lt lt default is gt meaning after date If no count is given the list is for the specified days in date s month The default day is date s day The spec can also be a positive or negative number 5 is equivalent to 5 lt 5 is equivalent to 5 which is 5 gt if not is date date then error date list need a date use date list with a colon 2 end local list text list text if date partial then return list end local count offset operation local ops gt before false include true gt before false include false lt before true include true lt before true include false if spec then if type spec number then count floor spec 0 5 if count lt 0 then count count operation ops lt end elseif type spec string then local num day op spec match s d s a s lt gt s if not num then return list end if num then count tonumber num end if day then local dow day number day gsub sS accept plural days if not dow then return list end offset dow date dow end operation ops op else return list end end offset offset or 0 operation operation or ops gt local datefrom dayfirst daylast if operation before then if offset gt 0 or offset 0 and not operation include then offset offset 7 end if count then if count gt 1 then offset offset 7 count 1 end datefrom date offset else daylast date day offset dayfirst daylast 7 if dayfirst 0 then dayfirst 7 end end else if offset lt 0 or offset 0 and not operation include then offset offset 7 end if count then datefrom date offset else dayfirst date day offset daylast date monthdays end end if not count then if daylast lt dayfirst then return list end count floor daylast dayfirst 7 1 datefrom Date date day dayfirst end for i 1 count do if not datefrom then break end exceeds date limits list i datefrom datefrom datefrom 7 end return list end A table to get the current date time UTC but only if needed local current setmetatable index function self key local d os date t self year d year self month d month self day d day self hour d hour self minute d min self second d sec return rawget self key end local function extract date newdate text Parse the date time in text and return n o where n table of numbers with date time fields o table of options for AM PM or AD BC or format if any or return nothing if date is known to be invalid Caller determines if the values in n are valid A year must be positive 1 to 9999 use BC for BC In a y m d string the year must be four digits to avoid ambiguity 0001 to 9999 The only way to enter year lt 0 is by specifying the date as three numeric parameters like ymd Date 1 1 1 Dates of form d m y m d y y m d are rejected as potentially ambiguous local date options if text sub 1 Z then Extract date time from a Wikidata timestamp The year can be 1 to 16 digits but this module handles 1 to 4 digits only Examples 2016 06 21T14 30 00Z 0000000180 00 00T00 00 00Z local sign y m d H M S text match d d d d d T d d d d d d Z if sign then y tonumber y if sign and y gt 0 then y y end if y lt 0 then options era BCE end date year y m tonumber m d tonumber d H tonumber H M tonumber M S tonumber S if m 0 then newdate partial true return date options end date month m if d 0 then newdate partial true return date options end date day d if H gt 0 or M gt 0 or S gt 0 then date hour H date minute M date second S end return date options end return end local function extract ymd item Called when no day or month has been set local y m d item match d d d d w d d if y then if date year then return end if m match d d then m tonumber m else m month number m end if m then date year tonumber y date month m date day tonumber d return true end end end local function extract day or year item Called when a day would be valid or when a year would be valid if no year has been set and partial is set local number suffix item match d d d d if number then local n tonumber number if number lt 2 and n lt 31 then suffix suffix lower if suffix or suffix st or suffix nd or suffix rd or suffix th then date day n return true end elseif suffix and newdate partial and not date year then date year n return true end end end local function extract month item A month must be given as a name or abbreviation a number could be ambiguous local m month number item if m then date month m return true end end local function extract time item local h m s item match d d d d d if date hour or not h then return end if s then s s match d d if not s then return end end date hour tonumber h date minute tonumber m date second tonumber s nil if empty string return true end local item count 0 local index time local function set ampm item local H date hour if H and not options am and index time 1 item count then options am ampm options item caller checked this is not nil if item match Aa then if not 1 lt H and H lt 12 then return end if H 12 then date hour 0 end else if not 1 lt H and H lt 23 then return end if H lt 11 then date hour H 12 end end return true end end for item in text gsub gsub amp nbsp gmatch S do item count item count 1 if era text item then Era is accepted in peculiar places if options era then return end options era item elseif ampm options item then if not set ampm item then return end elseif item find 1 true then if not extract time item then return end index time item count elseif date day and date month then if date year then return should be nothing more so item is invalid end if not item match d d d d then return end date year tonumber item elseif date day then if not extract month item then return end elseif date month then if not extract day or year item then return end elseif extract month item then options format mdy elseif extract ymd item then options format ymd elseif extract day or year item then if date day then options format dmy end else return end end if not date year or date year 0 then return end local era era text options era if era and era isbc then date year 1 date year end return date options end local function autofill date1 date2 Fill any missing month or day in each date using the corresponding component from the other date if present or with 1 if both dates are missing the month or day This gives a good result for calculating the difference between two partial dates when no range is wanted Return filled date1 date2 two full dates local function filled a b local fillmonth fillday if not a month then fillmonth b month or 1 end if not a day then fillday b day or 1 end if fillmonth or fillday then need to create a new date a Date a month fillmonth day fillday end return a end return filled date1 date2 filled date2 date1 end local function date add sub lhs rhs is sub Return a new date from calculating lhs rhs or lhs rhs or return nothing if invalid The result is nil if the calculated date exceeds allowable limits Caller ensures that lhs is a date its properties are copied for the new date if lhs partial then Adding to a partial is not supported Can subtract a date or partial from a partial but this is not called for that return end local function is prefix text word minlen local n text return minlen or 1 lt n and n lt word and text word sub 1 n end local function do days n local forcetime jd if floor n n then jd lhs jd else forcetime not lhs hastime jd lhs jdz end jd jd is sub and n or n if forcetime then jd tostring jd if not jd find 1 true then jd jd 0 end end return Date lhs juliandate jd end if type rhs number then Add subtract days including fractional days return do days rhs end if type rhs string then rhs is a single component like 26m or 26 months with optional sign Fractions like 3 25d are accepted for the units which are handled as days local sign numstr id rhs match s d s a if sign then if sign then is sub not is sub and true or false end local y m days local num tonumber numstr if not num then return end id id lower if is prefix id years then y num m 0 elseif is prefix id months then y floor num 12 m num 12 elseif is prefix id weeks then days num 7 elseif is prefix id days then days num elseif is prefix id hours then days num 24 elseif is prefix id minutes 3 then days num 24 60 elseif is prefix id seconds then days num 24 3600 else return end if days then return do days days end if numstr find 1 true then return end if is sub then y y m m end assert 11 lt m and m lt 11 y lhs year y m lhs month m if m gt 12 then y y 1 m m 12 elseif m lt 1 then y y 1 m m 12 end local d math min lhs day days in month y m lhs calendar return Date lhs y m d end end if is diff rhs then local days rhs age days if is sub or false rhs isnegative or false then days days end return lhs days end end local full date only dayabbr true dayname true dow true dayofweek true dowiso true dayofweekiso true dayofyear true gsd true juliandate true jd true jdz true jdnoon true Metatable for a date s calculated fields local datemt index function self key if rawget self partial then if full date only key then return end if key monthabbr or key monthdays or key monthname then if not self month then return end end end local value if key dayabbr then value day info self dow 1 elseif key dayname then value day info self dow 2 elseif key dow then value self jdnoon 1 7 day of week 0 Sun to 6 Sat elseif key dayofweek then value self dow elseif key dowiso then value self jdnoon 7 1 ISO day of week 1 Mon to 7 Sun elseif key dayofweekiso then value self dowiso elseif key dayofyear then local first Date self year 1 1 self calendar jdnoon value self jdnoon first 1 day of year 1 to 366 elseif key era then Era text never a negative sign from year and options value get era for year self options era self year elseif key format then value self options format or dmy elseif key gsd then GSD 1 from 00 00 00 to 23 59 59 on 1 Ocak 1 AD Gregorian calendar which is from jd 1721425 5 to 1721426 49999 value floor self jd 1721424 5 elseif key juliandate or key jd or key jdz then local jd jdz julian date self rawset self juliandate jd rawset self jd jd rawset self jdz jdz return key jdz and jdz or jd elseif key jdnoon then Julian date at noon an integer on the calendar day when jd occurs value floor self jd 0 5 elseif key isleapyear then value is leap year self year self calendar elseif key monthabbr then value month info self month 1 elseif key monthdays then value days in month self year self month self calendar elseif key monthname then value month info self month 2 end if value nil then rawset self key value return value end end Date operators local function mt date add lhs rhs if not is date lhs then lhs rhs rhs lhs put date on left it must be a date for this to have been called end return date add sub lhs rhs end local function mt date sub lhs rhs if is date lhs then if is date rhs then return DateDiff lhs rhs end return date add sub lhs rhs true end end local function mt date concat lhs rhs return tostring lhs tostring rhs end local function mt date tostring self return self text end local function mt date eq lhs rhs Return true if dates identify same date time where for example Date 4712 1 1 Julian Date 4713 11 24 Gregorian is true This is called only if lhs and rhs have the same type and the same metamethod if lhs partial or rhs partial then One date is partial the other is a partial or a full date The months may both be nil but must be the same return lhs year rhs year and lhs month rhs month and lhs calendar rhs calendar end return lhs jdz rhs jdz end local function mt date lt lhs rhs Return true if lhs lt rhs for example Date 1 Oca 2016 lt Date 06 00 1 Oca 2016 is true This is called only if lhs and rhs have the same type and the same metamethod if lhs partial or rhs partial then One date is partial the other is a partial or a full date if lhs calendar rhs calendar then return lhs calendar Julian end if lhs partial then lhs lhs partial first end if rhs partial then rhs rhs partial first end end return lhs jdz lt rhs jdz end Examples of syntax to construct a date Date y m d julian default calendar is gregorian Date y m d H M S julian Date juliandate jd julian if jd contains text output includes H M S Date currentdate Date currentdatetime Date 1 Nisan 1995 julian parse date from text Date 1 Nisan 1995 AD julian using an era sets a flag to do the same for output Date 04 30 59 1 Nisan 1995 julian Date date copy of an existing date Date date t same updated with y m d H M S fields from table t Date t date with y m d H M S fields from table t function Date for forward declaration above Return a table holding a date assuming a uniform calendar always applies proleptic Gregorian calendar or proleptic Julian calendar or return nothing if date is invalid A partial date has a valid year however its month may be nil and its day and time fields are nil Field partial is set to false if a full date or a table if a partial date local calendars julian Julian gregorian Gregorian local newdate id uniq calendar Gregorian default is Gregorian calendar hastime false true if input sets a time hour 0 always set hour minute second so don t have to handle nil minute 0 second 0 options list date list subtract function self rhs options return DateDiff self rhs options end text date text local argtype datetext is copy jd number tnums local numindex 0 local numfields year month day hour minute second local numbers for v in ipairs do v strip to nil v local vlower type v string and v lower or nil if v nil then Ignore empty arguments after stripping so modules can directly pass template parameters elseif calendars vlower then newdate calendar calendars vlower elseif vlower partial then newdate partial true elseif vlower fix then newdate want fix true elseif is date v then Copy existing date items can be overridden by other arguments if is copy or tnums then return end is copy true newdate calendar v calendar newdate partial v partial newdate hastime v hastime newdate options v options newdate year v year newdate month v month newdate day v day newdate hour v hour newdate minute v minute newdate second v second elseif type v table then if tnums then return end tnums local tfields year 1 month 1 day 1 hour 2 minute 2 second 2 for tk tv in pairs v do if tfields tk then tnums tk tonumber tv end if tfields tk 2 then newdate hastime true end end else local num tonumber v if not num and argtype setdate and numindex 1 then num month number v end if num then if not argtype then argtype setdate end if argtype setdate and numindex lt 6 then numindex numindex 1 numbers numfields numindex num elseif argtype juliandate and not jd number then jd number num if type v string then if v find 1 true then newdate hastime true end elseif num floor num then The given value was a number The time will be used if the fractional part is nonzero newdate hastime true end else return end elseif argtype then return elseif type v string then if v currentdate or v currentdatetime or v juliandate then argtype v else argtype datetext datetext v end else return end end end if argtype datetext then if tnums or not set date from numbers newdate extract date newdate datetext then return end elseif argtype juliandate then newdate partial nil newdate jd jd number if not set date from jd newdate then return end elseif argtype currentdate or argtype currentdatetime then newdate partial nil newdate year current year newdate month current month newdate day current day if argtype currentdatetime then newdate hour current hour newdate minute current minute newdate second current second newdate hastime true end newdate calendar Gregorian ignore any given calendar name elseif argtype setdate then if tnums or not set date from numbers newdate numbers then return end elseif not is copy or tnums then return end if tnums then newdate jd nil force recalculation in case jd was set before changes from tnums if not set date from numbers newdate tnums then return end end if newdate partial then local year newdate year local month newdate month local first Date year month or 1 1 newdate calendar month month or 12 local last Date year month days in month year month newdate calendar newdate partial first first last last else newdate partial false avoid index lookup end setmetatable newdate datemt local readonly local mt index newdate newindex function t k v error date tostring k is read only 2 end add mt date add sub mt date sub concat mt date concat tostring mt date tostring eq mt date eq lt mt date lt return setmetatable readonly mt end local function diff age diff code options Return a tuple of integer values from diff as specified by code except that each integer may be a list of two integers for a diff with a partial date or return nil if the code is not supported If want round the least significant unit is rounded to nearest whole unit For a duration an extra day is added local wantround wantduration wantrange if type options table then wantround options round wantduration options duration wantrange options range else wantround options end if not is diff diff then local f wantduration and duration or age error f need a date difference use diff f with a colon 2 end if diff partial then Ignore wantround wantduration local function choose v if type v table then if not wantrange or v 1 v 2 then Example Date partial 2005 Date partial 2001 gives diff years 3 4 to show the range of possible results If do not want a range choose the second value as more expected return v 2 end end return v end if code ym or code ymd then if not wantrange and diff iszero then This avoids an unexpected result such as Date partial 2001 Date partial 2001 giving diff years 0 months 0 11 which would be reported as 0 years and 11 months return 0 0 end return choose diff partial years choose diff partial months end if code y then return choose diff partial years end if code m or code w or code d then return choose diff partial mindiff age code diff partial maxdiff age code end return nil end local extra days wantduration and 1 or 0 if code wd or code w or code d then local offset wantround and 0 5 or 0 local days diff age days extra days if code wd or code d then days floor days offset if code d then return days end return floor days 7 days 7 end return floor days 7 offset end local H M S diff hours diff minutes diff seconds if code dh or code dhm or code dhms or code h or code hm or code hms then local days floor diff age days extra days local inc hour if wantround then if code dh or code h then if M gt 30 then inc hour true end elseif code dhm or code hm then if S gt 30 then M M 1 if M gt 60 then M 0 inc hour true end end else Nothing needed because S is an integer end if inc hour then H H 1 if H gt 24 then H 0 days days 1 end end end if code dh or code dhm or code dhms then if code dh then return days H elseif code dhm then return days H M else return days H M S end end local hours days 24 H if code h then return hours elseif code hm then return hours M end return hours M S end if wantround then local inc hour if code ymdh or code ymwdh then if M gt 30 then inc hour true end elseif code ymdhm or code ymwdhm then if S gt 30 then M M 1 if M gt 60 then M 0 inc hour true end end elseif code ymd or code ymwd or code yd or code md then if H gt 12 then extra days extra days 1 end end if inc hour then H H 1 if H gt 24 then H 0 extra days extra days 1 end end end local y m d diff years diff months diff days if extra days gt 0 then d d extra days if d gt 28 or code yd then Recalculate in case have passed a month diff diff date1 extra days diff date2 y m d diff years diff months diff days end end if code ymd then return y m d elseif code yd then if y gt 0 then It is known that diff date1 gt diff date2 diff diff date1 diff date2 y y end return y floor diff age days elseif code md then return y 12 m d elseif code ym or code m then if wantround then if d gt 16 then m m 1 if m gt 12 then m 0 y y 1 end end end if code ym then return y m end return y 12 m elseif code ymw then local weeks floor d 7 if wantround then local days d 7 if days gt 3 or days 3 and H gt 12 then weeks weeks 1 end end return y m weeks elseif code ymwd then return y m floor d 7 d 7 elseif code ymdh then return y m d H elseif code ymwdh then return y m floor d 7 d 7 H elseif code ymdhm then return y m d H M elseif code ymwdhm then return y m floor d 7 d 7 H M end if code y then if wantround and m gt 6 then y y 1 end return y end return nil end local function diff duration diff code options if type options table then options round options end options duration true return diff age diff code options end Metatable for some operations on date differences diffmt for forward declaration above concat function lhs rhs return tostring lhs tostring rhs end tostring function self return tostring self age days end index function self key local value if key age days then if rawget self partial then local function jdz date return date partial and date partial first or date jdz end value jdz self date1 jdz self date2 else value self date1 jdz self date2 jdz end end if value nil then rawset self key value return value end end function DateDiff date1 date2 options for forward declaration above Return a table with the difference between two dates date1 date2 The difference is negative if date1 is older than date2 Return nothing if invalid If d date1 date2 then date1 date2 d If date1 gt date2 and the dates have no H M S time specified then date1 date2 d years y d months m d days where the larger time units are added first The result of Date 2015 1 x 1m is Date 2015 2 28 for x 28 29 30 31 That means for example d Date 2015 3 3 Date 2015 1 31 gives d years d months d days 0 1 3 excluding date1 if not is date date1 and is date date2 and date1 calendar date2 calendar then return end local wantfill if type options table then wantfill options fill end local isnegative false local iszero false if date1 lt date2 then isnegative true date1 date2 date2 date1 elseif date1 date2 then iszero true end It is known that date1 gt date2 period is from date2 to date1 if date1 partial or date2 partial then Two partial dates might have timelines A B date1 is from A to B inclusive C D date2 is from C to D inclusive date1 gt date2 iff A gt C date1 partial first gt date2 partial first The periods can overlap Nisan 2001 2001 A B A 2001 04 01 B 2001 04 30 C D C 2001 01 01 D 2001 12 31 if wantfill then date1 date2 autofill date1 date2 else local function zdiff date1 date2 local diff date1 date2 if diff isnegative then return date1 date1 a valid diff in case we call its methods end return diff end local function getdate date which return date partial and date partial which or date end local maxdiff zdiff getdate date1 last getdate date2 first local mindiff zdiff getdate date1 first getdate date2 last local years months if maxdiff years mindiff years then years maxdiff years if maxdiff months mindiff months then months maxdiff months else months mindiff months maxdiff months end else years mindiff years maxdiff years end return setmetatable date1 date1 date2 date2 partial years years months months maxdiff maxdiff mindiff mindiff isnegative isnegative iszero iszero age diff age duration diff duration diffmt end end local y1 m1 date1 year date1 month local y2 m2 date2 year date2 month local years y1 y2 local months m1 m2 local d1 date1 day hms date1 local d2 date2 day hms date2 local days time if d1 gt d2 then days d1 d2 else months months 1 Get days in previous month before the to date given Aralik has 31 days local dpm m1 gt 1 and days in month y1 m1 1 date1 calendar or 31 if d2 gt dpm then days d1 hms date2 else days dpm d2 d1 end end if months lt 0 then years years 1 months months 12 end days time math modf days local H M S h m s time return setmetatable date1 date1 date2 date2 partial false avoid index lookup years years months months days days hours H minutes M seconds S isnegative isnegative iszero iszero age diff age duration diff duration diffmt end return YIL YIL ONYIL ONYIL YUZYIL YUZYIL BINYIL BINYIL Ceviri Ceviri current current Date Date days in month days in month