Module:Date: Difference between revisions

    (major refactor, mostly working now; add/subtract with a date; remove template handling which will be in Module:Age)
    (clean up and some aliases)
    Line 1: Line 1:
    -- Date functions for implementing templates and for use by other modules.
    -- Date functions for use by other modules.
    -- I18N and time zones are not supported.
    -- I18N and time zones are not supported.


    local MINUS = '−'  -- Unicode U+2212 MINUS SIGN
    local MINUS = '−'  -- Unicode U+2212 MINUS SIGN
    local Date, DateDiff, datemt  -- forward declarations
    local function is_date(t)
    return type(t) == 'table' and getmetatable(t) == datemt
    end


    local function collection()
    local function collection()
    Line 46: Line 52:
    local function days_in_month(year, month, calname)
    local function days_in_month(year, month, calname)
    -- Return number of days (1..31) in given month (1..12).
    -- Return number of days (1..31) in given month (1..12).
    local month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
    if month == 2 and is_leap_year(year, calname) then
    if month == 2 and is_leap_year(year, calname) then
    return 29
    return 29
    end
    end
    return month_days[month]
    return ({ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 })[month]
    end
    end


    Line 341: Line 346:
    local function _date_text(date, fmt, options)
    local function _date_text(date, fmt, options)
    -- Return formatted string from given date.
    -- Return formatted string from given date.
    if not is_date(date) then
    return 'Need a date (use "date:text()" with a colon).'
    end
    if type(fmt) ~= 'string' then
    if type(fmt) ~= 'string' then
    fmt = '%-d %B %-Y %{era}'
    fmt = '%-d %B %-Y %{era}'
    Line 423: Line 431:
    end
    end


    -- A table to get the current year/month/day (UTC), but only if needed.
    -- A table to get the current date/time (UTC), but only if needed.
    local current = setmetatable({}, {
    local current = setmetatable({}, {
    __index = function (self, key)
    __index = function (self, key)
    Line 574: Line 582:
    end
    end
    return date, options
    return date, options
    end
    local Date, DateDiff, datemt  -- forward declarations
    local function is_date(t)
    return type(t) == 'table' and getmetatable(t) == datemt
    end
    end


    Line 688: Line 690:
    elseif key == 'dow' then
    elseif key == 'dow' then
    value = (self.jd + 1) % 7  -- day-of-week 0=Sun to 6=Sat
    value = (self.jd + 1) % 7  -- day-of-week 0=Sun to 6=Sat
    elseif key == 'dayofweek' then
    value = self.dow
    elseif key == 'dowiso' then
    elseif key == 'dowiso' then
    value = (self.jd % 7) + 1  -- ISO day-of-week 1=Mon to 7=Sun
    value = (self.jd % 7) + 1  -- ISO day-of-week 1=Mon to 7=Sun
    elseif key == 'dayofweekiso' then
    value = self.dowiso
    elseif key == 'doy' then
    elseif key == 'doy' then
    local first = Date(self.year, 1, 1, self.calname).jd
    local first = Date(self.year, 1, 1, self.calname).jd
    value = self.jd - first + 1  -- day-of-year 1 to 366
    value = self.jd - first + 1  -- day-of-year 1 to 366
    elseif key == 'dayofyear' then
    value = self.doy
    elseif key == 'era' then
    elseif key == 'era' then
    -- Era text (not a negative sign) from year and options.
    -- Era text (never a negative sign) from year and options.
    value = get_era_for_year(self.options.era, self.year)
    value = get_era_for_year(self.options.era, self.year)
    elseif key == 'gsd' then
    elseif key == 'gsd' then
    Line 700: Line 708:
    -- which is JDN = 1721426, and is from jd 1721425.5 to 1721426.49999.
    -- which is JDN = 1721426, and is from jd 1721425.5 to 1721426.49999.
    value = math.floor(self.jd - 1721424.5)
    value = math.floor(self.jd - 1721424.5)
    elseif key == 'jd' or key == 'jdz' then
    elseif key == 'juliandate' or key == 'jd' or key == 'jdz' then
    local jd, jdz = julian_date(self)
    local jd, jdz = julian_date(self)
    rawset(self, 'juliandate', jd)
    rawset(self, 'jd', jd)
    rawset(self, 'jd', jd)
    rawset(self, 'jdz', jdz)
    rawset(self, 'jdz', jdz)
    return key == 'jd' and jd or jdz
    return key == 'jdz' and jdz or jd
    elseif key == 'is_leap_year' then
    elseif key == 'isleapyear' then
    value = is_leap_year(self.year, self.calname)
    value = is_leap_year(self.year, self.calname)
    elseif key == 'monthabbr' then
    elseif key == 'monthabbr' then
    value = month_info[self.month][1]
    value = month_info[self.month][1]
    elseif key == 'monthdays' then
    value = days_in_month(self.year, self.month, self.calname)
    elseif key == 'monthname' then
    elseif key == 'monthname' then
    value = month_info[self.month][2]
    value = month_info[self.month][2]
    Line 718: Line 729:
    end,
    end,
    }
    }
    local function _month_days(date, month)
    return days_in_month(date.year, month, date.calname)
    end


    --[[ Examples of syntax to construct a date:
    --[[ Examples of syntax to construct a date:
    Line 748: Line 755:
    minute = 0,
    minute = 0,
    second = 0,
    second = 0,
    month_days = _month_days,
    options = make_option_table(),
    options = make_option_table(),
    text = _date_text,
    text = _date_text,
    Line 847: Line 853:
    -- The difference is negative if date2 is more recent than date1.
    -- The difference is negative if date2 is more recent than date1.
    -- Return nothing if invalid.
    -- Return nothing if invalid.
    if not (date1 and date2 and date1.calname == date2.calname) then
    if not (is_date(date1) and is_date(date2) and date1.calname == date2.calname) then
    return
    return
    end
    end
    Line 898: Line 904:
    _current = current,
    _current = current,
    _Date = Date,
    _Date = Date,
    _DateDiff = DateDiff,
    _days_in_month = days_in_month,
    _days_in_month = days_in_month,
    }
    }