Jump to content

Module:Japanese calendar

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 11:04, 22 August 2013 (add kanji year output function). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local eras = mw.loadData( 'Module:Japanese calendar/data' )

--------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------

local function yearToEraIndex( year )
    year = tonumber( year )
    if type( year ) ~= 'number' then return end
    for i, t in ipairs( eras ) do
        if year >= t.year then
            return i
        end
    end
end

local function textToEraIndex( s )
    for i, t in ipairs( eras ) do
        if s == t.article or s == t.kanji then
            return i
        end
    end
end

--------------------------------------------------------------------
-- Era class definition
--------------------------------------------------------------------

local era = {}
era.__index = era

function era:new( init )
    init = type( init ) == 'table' and init or {}
    local obj = {}
    
    -- Grab the data from the init table.
    obj.gregorianYear = tonumber( init.year )
    obj.article = type( init.article ) == 'string' and init.article or nil
    obj.kanji = type( init.kanji ) == 'string' and init.kanji or nil
    
    -- Calculate the era data from the input. Some input values may be overwritten if they are inconsistent with the data in the /data subpage.
    -- If the article name and the kanji differ, the article name takes priority.
    if obj.gregorianYear then
        -- We have the Gregorian year, so we can calculate all the data.
        -- Check if the article or the kanji have been specified, in case the era is different from the year era.
        local eraIndex
        if obj.article or obj.kanji then
            eraIndex = textToEraIndex( obj.article ) or textToEraIndex( obj.kanji )
        else
            eraIndex = yearToEraIndex( obj.gregorianYear )
        end
        
        if eraIndex then
            local eraData = eras[ eraIndex ]
            obj.startYear = eraData.year
            -- Allow matching years to different eras, but only for the first year of the next era. For example, Taisho 15 is also Showa 1, but there is no such thing as Taisho 16.
            local nextEraData = eras[ eraIndex - 1 ]
            local nextStartYear = nextEraData and nextEraData.year
            if 
                (
                    not nextStartYear
                    or ( nextStartYear and obj.gregorianYear <= nextStartYear )
                )
                and obj.gregorianYear >= obj.startYear -- Don't allow negative years.
                and math.floor( obj.gregorianYear ) ~= 687 -- 686 is a special case as the Shuchō era lasted for less than one year. Disallowing 687 is a quick fix for this.
                then
                obj.eraYear = obj.gregorianYear - obj.startYear + 1
                obj.eraYearKanji = tostring( obj.eraYear ) -- Although most of these will be numbers, one is a kanji character, so convert them all to strings.
                if obj.eraYearKanji == '1' then
                    obj.eraYearKanji = '元'
                end
            end
                
            obj.article = eraData.article
            obj.kanji = eraData.kanji
            obj.label = eraData.label
        end
    
    elseif obj.article or obj.kanji then
        -- No year was specified, so we will take what data we can from the article name or from the kanji.
        local eraIndex = textToEraIndex( obj.article ) or textToEraIndex( obj.kanji )
        if eraIndex then
            local eraData = eras[ eraIndex ]
            obj.startYear = eraData.year
            obj.kanji = eraData.kanji
            obj.label = eraData.label
        end
    end
    -- Create a link to the era article if possible.
    if obj.article then
        if obj.label then
            obj.link = mw.ustring.format( '[[%s|%s]]', obj.article, obj.label )
        else
            obj.link = mw.ustring.format( '[[%s]]', obj.article )
        end
    end
    
    return setmetatable( obj, {
        __index = self
    })
end

--------------------------------------------------------------------
-- Interface for old Japanese calendar templates
--------------------------------------------------------------------

local function getStartYear( obj )
    return obj.startYear
end

local function getEraYear( obj )
    return obj.eraYear
end

local function getEraYearKanji( obj )
    return obj.eraYearKanji
end

local function getArticle( obj )
    return obj.article
end

local function getLabel( obj )
    return obj.label
end

local function getLink( obj )
    return obj.link
end

local function getKanji( obj )
    return obj.kanji
end

local function getLabelAndEraYear( obj, kanji )
    local eraYear = kanji and obj.eraYearKanji or obj.eraYear
    if obj.label and eraYear then
        return mw.ustring.format( '%s %s', obj.label, tostring( eraYear ) )
    elseif obj.article and eraYear then
        return mw.ustring.format( '%s %s', obj.article, tostring( eraYear ) )
    end
end

local function getLinkAndEraYear( obj, kanji )
    local eraYear = kanji and obj.eraYearKanji or obj.eraYear
    if obj.link and eraYear then
        return mw.ustring.format( '%s %s', obj.link, tostring( eraYear ) )
    end
end

local function getLabelAndEraYearKanji( obj )
    return getLabelAndEraYear( obj, true )
end

local function getLinkAndEraYearKanji( obj )
    return getLinkAndEraYear( obj, true )
end

-- Process the arguments from #invoke.
local function makeWrapper( func )
    return function( frame )
        -- If called via #invoke, use the args passed into the invoking
        -- template, or the args passed to #invoke if any exist. Otherwise
        -- assume args are being passed directly in from the debug console
        -- or from another Lua module.
        local origArgs
        if frame == mw.getCurrentFrame() then
            origArgs = frame:getParent().args
            for k, v in pairs( frame.args ) do
                origArgs = frame.args
                break
            end
        else
            origArgs = frame
        end
        -- Trim whitespace and remove blank arguments.
        local args = {}
        for k, v in pairs( origArgs ) do
            if type( v ) == 'string' then
                v = mw.text.trim( v )
            end
            if v ~= '' then
                args[k] = v
            end
        end
        
        local argsEra = era:new( args )
        return func( argsEra ) or ''
    end
end

--------------------------------------------------------------------
-- Return the era class and the template interface
--------------------------------------------------------------------
 
return {
    era = era,
    baseyear = makeWrapper( getStartYear ),
    year = makeWrapper( getEraYear ),
    kanjiyear = makeWrapper( getEraYearKanji ),
    article = makeWrapper( getArticle ),
    label = makeWrapper( getLabel ),
    link = makeWrapper( getLink ),
    kanji = makeWrapper( getKanji ),
    label_year = makeWrapper( getLabelAndEraYear ),
    link_year = makeWrapper( getLinkAndEraYear ),
    label_kanjiyear = makeWrapper( getLabelAndEraYearKanji ),
    link_kanjiyear = makeWrapper( getLinkAndEraYearKanji )
}