Module:Date: Difference between revisions

    From Nonbinary Wiki
    (major refactor with fixes; force Date to be read-only (error on write); list of dates in a month on a particular day of week)
    m (3 revisions imported from wikipedia:Module:Date: see Topic:Vtixlm0q28eo6jtf)
     
    (20 intermediate revisions by 4 users not shown)
    Line 3: Line 3:


    local MINUS = '−'  -- Unicode U+2212 MINUS SIGN
    local MINUS = '−'  -- Unicode U+2212 MINUS SIGN
    local floor = math.floor


    local Date, DateDiff, diffmt  -- forward declarations
    local Date, DateDiff, diffmt  -- forward declarations
    Line 34: Line 35:


    local function strip_to_nil(text)
    local function strip_to_nil(text)
    -- If text is a string, return its trimmed content, or nil.
    -- If text is a string, return its trimmed content, or nil if empty.
    -- Otherwise return text (convenient when Date fields are provided from
    -- Otherwise return text (convenient when Date fields are provided from
    -- another module which is able to pass, for example, a number).
    -- another module which may pass a string, a number, or another type).
    if type(text) == 'string' then
    if type(text) == 'string' then
    text = text:match('(%S.-)%s*$')
    text = text:match('(%S.-)%s*$')
    end
    end
    return text
    return text
    end
    local function number_name(number, singular, plural, sep)
    -- Return the given number, converted to a string, with the
    -- separator (default space) and singular or plural name appended.
    plural = plural or (singular .. 's')
    sep = sep or ' '
    return tostring(number) .. sep .. ((number == 1) and singular or plural)
    end
    end


    Line 65: Line 58:
    end
    end
    return ({ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 })[month]
    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
    end


    Line 76: Line 83:
    --    1 January 4713 BC  = (-4712, 1, 1)  Julian calendar
    --    1 January 4713 BC  = (-4712, 1, 1)  Julian calendar
    --  24 November 4714 BC = (-4713, 11, 24) Gregorian calendar
    --  24 November 4714 BC = (-4713, 11, 24) Gregorian calendar
    local floor = math.floor
    local offset
    local offset
    local a = floor((14 - date.month)/12)
    local a = floor((14 - date.month)/12)
    local y = date.year + 4800 - a
    local y = date.year + 4800 - a
    if date.calname == 'Julian' then
    if date.calendar == 'Julian' then
    offset = floor(y/4) - 32083
    offset = floor(y/4) - 32083
    else
    else
    Line 88: Line 94:
    local jd = date.day + floor((153*m + 2)/5) + 365*y + offset
    local jd = date.day + floor((153*m + 2)/5) + 365*y + offset
    if date.hastime then
    if date.hastime then
    jd = jd + (date.hour + (date.minute + date.second / 60) /60) / 24 - 0.5
    jd = jd + hms(date) - 0.5
    return jd, jd
    return jd, jd
    end
    end
    Line 100: Line 106:
    -- This handles the proleptic Julian and Gregorian calendars.
    -- This handles the proleptic Julian and Gregorian calendars.
    -- Negative Julian dates are not defined but they work.
    -- Negative Julian dates are not defined but they work.
    local floor = math.floor
    local calname = date.calendar
    local calname = date.calname
    local low, high -- min/max limits for date ranges −9999-01-01 to 9999-12-31
    local limits -- min/max limits for date ranges −9999-01-01 to 9999-12-31
    if calname == 'Gregorian' then
    if calname == 'Julian' then
    low, high = -1930999.5, 5373484.49999
    limits = { -1931076.5, 5373557.49999 }
    elseif calname == 'Julian' then
    elseif calname == 'Gregorian' then
    low, high = -1931076.5, 5373557.49999
    limits = { -1930999.5, 5373484.49999 }
    else
    else
    return
    return
    end
    end
    local jd = date.jd
    local jd = date.jd
    if not (type(jd) == 'number' and limits[1] <= jd and jd <= limits[2]) then
    if not (type(jd) == 'number' and low <= jd and jd <= high) then
    return
    return
    end
    end
    local jdn = floor(jd)