Z28135

{
    "Z1K1": "Z2",
    "Z2K1": {
        "Z1K1": "Z6",
        "Z6K1": "Z28135"
    },
    "Z2K2": {
        "Z1K1": "Z14",
        "Z14K1": "Z23984",
        "Z14K3": {
            "Z1K1": "Z16",
            "Z16K1": "Z600",
            "Z16K2": "function Z23984( Z23984K1 ) {\n\t/** \n\t * This implementation is based on Z25739\n\t */\n\n\t// Declare shared state\n\tlet input = Z23984K1;\n\tlet elements=[\"\"];\n\tlet iAtt = 0;\n\tlet _day = null;\n\tlet _month = null;\n\tlet _year = null;\n\tlet _era = null;\n\tlet _forced = null;\n\tlet _nSteps = 1;\n\tlet _isToBeIgnored = false;\n\tlet _constraintsList = [];\n\tlet possibilities = [];\n\n\t// Separators used to split input into tokens\n\tconst separators = [\" \", \"\\t\", \"/\", \"\\\\\", \"'\", '\"', \"|\"];\n\n\t// Preferred parsing orders\n\tconst prefOptions = [\"dmye\", \"dmey\", \"mdye\", \"mdey\", \"yemd\", \"eymd\", \"yedm\", \"eydm\"];\n\n\t// Semantic categories\n\tconst DAY = 0;\n\tconst MONTH = 1;\n\tconst YEAR = 2;\n\tconst ERA = 3;\n\n\t// Constraint positioning\n\tconst BEFORE = 10;\n\tconst AFTER = 20;\n\tconst BEFORE_OR_AFTER = 30;\n\n\t// Comparison modes\n\tconst LENIENT = true;\n\tconst NORMAL = false;\n\tconst LETTER_BY_LETTER = Symbol();\n\n\t/**\n\t * Populates language-specific date parsing rules.\n\t * Defines literals for eras (BC/AD), months (short and long),\n\t * ordinal suffix constraints (st, nd, rd, th), and Roman numerals.\n\t */\n\tfunction languageSpecificOptions(){\n\t\tdefineLiterals({\n\t\t\tad:1n,\n\t\t\tbc:-1n,\n\t\t\tbce:-1n,\n\t\t\tce:1n,\n\t\t},ERA,LETTER_BY_LETTER);\n\n\t\tdefineLiterals({\n\t\t\tJA:0n,\n\t\t\tFE:1n,\n\t\t\tMR:2n,\n\t\t\tAP:3n,\n\t\t\tMY:4n,\n\t\t\tJN:5n,\n\t\t\tJL:6n,\n\t\t\tAU:7n,\n\t\t\tSE:8n,\n\t\t\tOC:9n,\n\t\t\tNV:10n,\n\t\t\tDE:11n,\n\t\t\tjanuary:0n,\n\t\t\tfebruary:1n,\n\t\t\tmarch:2n,\n\t\t\tapril:3n,\n\t\t\tmay:4n,\n\t\t\tjune:5n,\n\t\t\tjuly:6n,\n\t\t\taugust:7n,\n\t\t\tseptember:8n,\n\t\t\toctober:9n,\n\t\t\tnovember:10n,\n\t\t\tdecember:11n\n\t\t},MONTH);\n\n\t\tdefineConstraint([\"st\",\"nd\",\"rd\",\"th\"],DAY,AFTER);\n\n\t\tdefineLiterals({\n\t\t\ti:0n,\n\t\t\tii:1n,\n\t\t\tiii:2n,\n\t\t\tiv:3n,\n\t\t\tiiii:3n,\n\t\t\tv:4n,\n\t\t\tvi:5n,\n\t\t\tvii:6n,\n\t\t\tviii:7n,\n\t\t\tviiii:8n,\n\t\t\tix:8n,\n\t\t\tx:9n,\n\t\t\txi:10n,\n\t\t\txii:11n\n\t\t},MONTH,NORMAL);\n\t}\n\n\t/**\n\t * Define textual literals that map to a date component.\n\t * E.g., \"January\" -\u003E month 0, \"AD\" -\u003E era +1.\n\t *\n\t * @param {Object\u003Cstring, bigint\u003E} literals - Map of string to numeric value.\n\t * @param {number|number[]} meaning - Which component(s) these literals represent.\n\t * @param {boolean|symbol} [compMode=LENIENT] - Comparison strictness.\n\t * @param {boolean} [forced=false] - If true, force assignment to this component.\n\t * @returns {boolean} True if match found and applied.\n\t */\n\tfunction defineLiterals(literals, meaning, compMode=LENIENT, forced=false){\n\t\tlet bestGuess=null;\n\t\tconst compFunc = compMode == LENIENT ? lenientComp : lessLenientComp;\n\t\tfor (let m in literals){\n\t\t\tlet target=m.split(\" \");\n\t\t\tlet ok=true;\n\t\t\tfor (let t=0; t\u003Ctarget.length; ++t){\n\t\t\t\tif (elements[iAtt+t]==undefined || !compFunc(elements[iAtt+t],target[t])){\n\t\t\t\t\tok=false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (ok){\n\t\t\t\tif (bestGuess == null || bestGuess[1]\u003Ctarget.length){\n\t\t\t\t\tbestGuess = [literals[m],target.length];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\telse if (compMode==LETTER_BY_LETTER ){\n\t\t\t\tlet target=m.split(\"\");\n\t\t\t\ttarget.filter((l)=\u003El!=\" \");\n\t\t\t\tlet ok=true;\n\t\t\t\tfor (let t=0; t\u003Ctarget.length; ++t){\n\t\t\t\t\tif (elements[iAtt+t]==undefined || !compFunc(elements[iAtt+t],target[t])){\n\t\t\t\t\t\tok=false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (ok){\n\t\t\t\t\tif (bestGuess == null || bestGuess[1]\u003Ctarget.length){\n\t\t\t\t\t\tbestGuess = [literals[m],target.length];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tif (bestGuess==null){\n\t\t\treturn false;\n\t\t}\n\n\t\tif (typeof(meaning)==\"number\"){\n\t\t\tmeaning=[meaning];\n\t\t}\n\t\tfor (let m of meaning){\n\t\t\tswitch(m){\n\t\t\t\tcase 0: _day=bestGuess[0]; break;\n\t\t\t\tcase 1: _month=bestGuess[0]; break;\n\t\t\t\tcase 2: _year=bestGuess[0]; break;\n\t\t\t\tcase 3: _era=bestGuess[0]; break;\n\t\t\t}\n\t\t}\n\t\t_nSteps=bestGuess[1];\n\t\tif (forced){\n\t\t\t_forced=meaning;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Constraint definition using a function instead of static strings.\n\t *\n\t * @param {Function} f - Function(elements, iAtt) returning match length.\n\t * @param {number|number[]} meaning - Component(s) affected.\n\t * @param {number} position - BEFORE, AFTER, etc.\n\t * @returns {boolean} True if constraint was applied.\n\t */\n\tfunction defineConstraint(literals, meaning, position, compMode=NORMAL){\n\t\tlet bestGuess=null;\n\t\tconst compFunc= compMode==LENIENT ? lenientComp : lessLenientComp;\n\t\tfor (let m of literals){\n\t\t\tlet target=m.split(\" \");\n\t\t\tlet ok=true;\n\t\t\tfor (let t=0; t\u003Ctarget.length; ++t){\n\t\t\t\tif (elements[iAtt+t]==undefined || !compFunc(elements[iAtt+t],target[t])){\n\t\t\t\t\tok=false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (ok){\n\t\t\t\tif (bestGuess == null || bestGuess\u003Ctarget.length){\n\t\t\t\t\tbestGuess = target.length;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tif (bestGuess==null){\n\t\t\treturn false;\n\t\t}\n\n\t\tif (typeof(meaning)==\"number\"){\n\t\t\tmeaning=[meaning];\n\t\t}\n\n\t\t_constraintsList.push({pos:possibilities.length, meaning:meaning, type: position});\n\n\t\t_nSteps=bestGuess;\n\t\t_isToBeIgnored=true;\n\t\treturn true;\n\t}\n\n\t/**\n\t * Compares two strings in a lenient way:\n\t * ignores case, strips punctuation, allows prefix matches.\n\t */\n\tfunction lenientComp(a,b){\n\t\ta=a.toLowerCase().replaceAll(/,|\\.|;|:/g,\"\");\n\t\tb=b.toLowerCase();\n\t\tif (a==\"\") return false;\n\t\treturn a==b.substring(0,a.length);\n\t}\n\n\t/**\n\t * Stricter comparison: requires full match after lowercasing and stripping.\n\t */\n\tfunction lessLenientComp(a,b){\n\t\ta=a.toLowerCase().replaceAll(/,|\\.|;|:/g,\"\");\n\t\tb=b.toLowerCase();\n\t\tif (a==\"\") return false;\n\t\treturn a==b;\n\t}\n\n\t/**\n\t * Extracts the first integer-like substring from s and returns as BigInt.\n\t * @param {string} s\n\t * @returns {bigint|null}\n\t */\n\tfunction extractNumber(s){\n\t\tlet res=/-?[0-9]+/.exec(s);\n\t\tif (res!=null){\n\t\t\treturn BigInt(res);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Determines whether a string should be interpreted as a year.\n\t * Checks for +/- signs or more than 2 digits.\n\t * @param {string} s\n\t * @returns {boolean}\n\t */\n\tfunction forceToYear(s){\n\t\tlet res=(/-?\\+?[0-9]+/.exec(s))?.[0];\n\t\tif (res==null){\n\t\t\treturn false;\n\t\t}\n\t\tif (res.length\u003E2) return true;\n\t\tif (res[0]==\"-\" || res[0]==\"+\") return true;\n\t\treturn false;\n\t}\n\n\t/**\n\t * Main parsing routine.\n\t * Attempts to safely parse a wide variety of date string formats into a structured object.\n\t * Handles ISO 8601, numeric-only formats, mixed text + numbers, and accounts for era markers.\n\t *\n\t * @returns {{K1: bigint|number, K2: number, K3: number}|undefined} - Structured date:\n\t *\t - K1: year (BigInt, possibly adjusted for era)\n\t *\t - K2: month (0-based integer)\n\t *\t - K3: day (integer)\n\t */\n\tfunction safeReadDate() {\n\t\t// Normalize input: replace various dash-like Unicode characters with a standard hyphen\n\t\tinput = input.replaceAll(/-|—|‒|–|—|⸺|⸺/g,\"-\");\n\n\t\t// Step 1: Handle ISO 8601-like datetime formats (e.g., 1985-05-31T12:34:56)\n\t\tconst isoRegex = /(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+)|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d)|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d)/;\n\t\tif (isoRegex.exec(input)){\n\t\t\t// Strip time portion (everything after 'T')\n\t\t\tinput = input.split(\"T\")[0];\n\t\t\t// Split YYYY-MM-DD by hyphen\n\t\t\tconst list = input.split(/(?\u003C=.)-(?=.)/);\n\t\t\t// Return normalized object (month is zero-based)\n\t\t\treturn {\n\t\t\t\tK1: BigInt(list[0]),\n\t\t\t\tK2: list[1]-1,\n\t\t\t\tK3: list[2]\n\t\t\t};\n\t\t}\n\n\t\t// Step 2: Tokenize input into chunks separated by custom separators (space, tab, slash, etc.)\n\t\tfor (let i=0; i\u003Cinput.length;){\n\t\t\tlet s=separators.find((v)=\u003E input.substring(i,i+v.length)==v);\n\t\t\tif (s!==undefined){\n\t\t\t\t// Start a new token only if previous isn’t empty\n\t\t\t\tif(elements[elements.length-1]!=\"\")\n\t\t\t\t\telements.push(\"\");\n\t\t\t\ti+=s.length;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Append character to current token\n\t\t\telements[elements.length-1]+=input[i++];\n\t\t}\n\t\t// Remove trailing empty element\n\t\tif(elements[elements.length-1]==\"\") elements.pop();\n\n\t\t// Step 3: Further split each element into numeric and non-numeric parts\n\t\tlet elSeparato=[];\n\t\tfor (let i=0; i\u003Celements.length; ++i){\n\t\t\t// Match leading optional number and optional trailing text\n\t\t\tfor (let e of elements[i].matchAll(/((?:(?\u003C=^|\\s)[\\-\\+])?[0-9]+)?(?:\\-?([^0-9\\-]+))?/g)){\n\t\t\t\telSeparato.push(e.splice(1))\n\t\t\t}\n\t\t\t// elSeparato.push(/-?\\+?[0-9]+/.exec(elements[i]));\n\t\t\t// elSeparato.push(elements[i].split(/-?\\+?[0-9]+/));\n\t\t}\n\t\t// Flatten array of matches\n\t\telSeparato = elSeparato.flat();\n\t\t// Filter out null/empty values\n\t\telements = elSeparato.filter((v)=\u003E(v!=null \u0026\u0026 v!=\"\"));\n\n\t\t// Step 5: Iterate through tokenized elements\n\t\tfor(iAtt; iAtt\u003Celements.length; iAtt+=_nSteps){\n\t\t\t// Set state for each iteration\n\t\t\t_nSteps = 1;\n\t\t\t_day = null;\n\t\t\t_month = null;\n\t\t\t_year = null;\n\t\t\t_era = null;\n\t\t\t_forced = null;\n\t\t\t_isToBeIgnored = false;\n\n\t\t\t// Attempt to extract a number from current token\n\t\t\tconst n=extractNumber(elements[iAtt]);\n\n\t\t\t// Local parsing variables (reset for each token)\n\t\t\t// Load language-specific literals and constraints (month names, BC/AD, etc.)\n\t\t\tlanguageSpecificOptions();\n\n\t\t\t// Build a candidate interpretation [day, month, year, era]\n\t\t\tconst p=[];\n\t\t\t// Day: prefer literal, else numeric in [1,31]\n\t\t\tp.push(_day ?? ((n!=null \u0026\u0026 n\u003E0n \u0026\u0026 n\u003C=31n) ? n : null));\n\t\t\t// Month: prefer literal, else numeric in [1,12] → convert to zero-based\n\t\t\tp.push(_month ?? ((n!=null \u0026\u0026 n\u003E0n \u0026\u0026 n\u003C=12n) ? n-1n :null));\n\t\t\t// Year: literal or numeric\n\t\t\tp.push(_year ?? n);\n\t\t\t// Era: AD/BC markers if any\n\t\t\tp.push(_era);\n\n\t\t\t// If some fields are forced, wipe others\n\t\t\tif (_forced!=null){\n\t\t\t\tfor (let j=0; j\u003C=3; ++j){\n\t\t\t\t\tif (_forced.find((v)=\u003E(v==j)) == undefined){\n\t\t\t\t\t\tp[j]=null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (forceToYear(elements[iAtt])){\n\t\t\t\t// Heuristic: large numbers or signed numbers → year only\n\t\t\t\tp[0]=null;\n\t\t\t\tp[1]=null;\n\t\t\t\tp[3]=null;\n\t\t\t}\n\n\t\t\t// Only add if not marked ignorable\n\t\t\tif (!_isToBeIgnored)\n\t\t\t\tpossibilities.push(p);\n\t\t}\n\n\t\t// Step 6: Apply constraints collected earlier (e.g., suffixes must follow numbers)\n\t\tif (_constraintsList.length\u003E0){\n\t\t\tfor (let pAtt=0; pAtt\u003C possibilities.length; ++pAtt){\n\t\t\t\tfor (let c of _constraintsList){\n\t\t\t\t\tif ((c.type==AFTER \u0026\u0026 pAtt!=c.pos-1) ||\n\t\t\t\t\t\t(c.type==BEFORE \u0026\u0026 pAtt!=c.pos) ||\n\t\t\t\t\t\t(c.type==BEFORE_OR_AFTER \u0026\u0026 pAtt!=c.pos-1 \u0026\u0026 pAtt!=c.pos)){\n\t\t\t\t\t\t// Invalidate fields that violate constraint\n\t\t\t\t\t\tfor(let m of c.meaning){\n\t\t\t\t\t\t\tpossibilities[pAtt][m]=null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Remove candidates where all fields are null\n\t\tpossibilities=possibilities.filter((v)=\u003E(v[0]!=null || v[1]!=null || v[2]!=null || v[3]!=null));\n\n\t\t// Step 7: Try different interpretation orders defined in prefOptions\n\t\tfor (let i=0; i\u003CprefOptions.length; ++i){\n\t\t\tlet actPref=prefOptions[i];\n\t\t\tlet d,m,y,e;\n\t\t\t// If fewer than 4 tokens, ignore era in preference string\n\t\t\tif (possibilities.length\u003C4) actPref=actPref.replace(\"e\",\"\");\n\t\t\t// Map possibilities to (d,m,y,e) depending on preference string\n\t\t\tfor (let j=0; j\u003CactPref.length; ++j){\n\t\t\t\tswitch(actPref[j]){\n\t\t\t\t\tcase \"d\":{\n\t\t\t\t\t\td=possibilities[j]?.[0]; break;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"m\":{\n\t\t\t\t\t\tm=possibilities[j]?.[1]; break;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"y\":{\n\t\t\t\t\t\ty=possibilities[j]?.[2]; break;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"e\":{\n\t\t\t\t\t\te=possibilities[j]?.[3];break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we found a complete candidate\n\t\t\tif (d!=null \u0026\u0026 m!=null \u0026\u0026 y!=null){\n\t\t\t\t// Adjust year if BC era (BC year counting: 1 BC = year 0, 2 BC = -1, etc.)\n\t\t\t\tlet year = y;\n\t\t\t\tif (e!=null){\n\t\t\t\t\tyear = e==-1n ? -(y\u003C0n ? -y : y)+1n : (y\u003C0n ? -y : y);\n\t\t\t\t}\n\n\t\t\t\t// Return normalized date object\n\t\t\t\treturn {\n\t\t\t\t\tK3: Number(d),\n\t\t\t\t\tK2: Number(m),\n\t\t\t\t\tK1: (year)\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\tconst finalDate = safeReadDate();\n\t\tif ( finalDate ) {\n\t\t\treturn finalDate;\n\t\t}\n\t\t// If no result, throw Z28131/Invalid date format error\n\t\tWikifunctions.Error( 'Z28131', [ input , \"en\"] );\n\t} catch( e ) {\n\t\t// If something fails, throw Z28131/Invalid date format error\n\t\tWikifunctions.Error( 'Z28131', [ input , \"en\"] );\n\t}\n}"
        }
    },
    "Z2K3": {
        "Z1K1": "Z12",
        "Z12K1": [
            "Z11",
            {
                "Z1K1": "Z11",
                "Z11K1": "Z1002",
                "Z11K2": "read Gregorian Date, en (dmy), with custom errors"
            }
        ]
    },
    "Z2K4": {
        "Z1K1": "Z32",
        "Z32K1": [
            "Z31"
        ]
    },
    "Z2K5": {
        "Z1K1": "Z12",
        "Z12K1": [
            "Z11"
        ]
    }
}