Skip to content

Custom Calendars

Joël Deffner edited this page Sep 5, 2026 · 6 revisions

Custom Calendars

A total conversion does not use the game's years. AGoT counts from Aegon's Landing, LotR from the start of an Age, Hegemonia from the founding of a city. The engine does not care: it has one increasing year axis, and every script date stays on it. So your files say 3000.1.1 while the game shows 1000 BC, and you do the subtraction in your head every time you read a start date.

Declare the mapping once and the toolkit does the subtraction for you.

Declaring the calendar

The calendar belongs to the mod, so it lives in the mod: <mod>/.px-toolkit/calendar.json. Paradox: Declare Calendar (Display Calendar) writes it, seeded from px.calendar when you already have one and from a worked example otherwise, and opens it for editing.

{ "epoch": 4000, "after": "AD", "before": "BC" }

Commit it with the mod and everyone working on it gets the same dates. One calendar per mod, which is what a multi-mod window needs, and the language server reads the file itself, so a bare LSP client outside VS Code gets the dates with no settings plumbing (Outside VS Code). While you edit the file, a schema checks it.

epoch is the script year that displays as year 1 of the later era. With epoch: 4000, script year 4000 reads 1 AD, 3999 reads 1 BC and 3000 reads 1000 BC. There is no year zero.

Field Required What it does
epoch yes Script year shown as year 1 of the after era. A whole number, 1 or more
after yes Era label for script years at or past the epoch: AD, TA, AC
before no Era label for earlier years. Leave it out for a single-era calendar, and years before the epoch get no display form
months no The engine's twelve months under your own names, first month first, as twelve strings. Left out, you get January to December. Only the names are yours: the game's month lengths (31 28 31 30 31 30 31 31 30 31 30 31, no leap years) cannot be changed
{
  "epoch": 4000,
  "after": "TA",
  "months": [
    "Narvinye", "Nenime", "Sulime", "Viresse", "Lotesse", "Narie",
    "Cermie", "Urime", "Yavannie", "Narquelie", "Hisime", "Ringare"
  ]
}

A calendar that does not make sense is ignored rather than half-applied: a missing or non-integer epoch, an empty after, a before that reads the same as after, a duplicate month name, or a months list that is not exactly twelve names. Typed input picks the era by its label and the month by its name, so two labels that collide would resolve to the wrong one. An older file that wrote months as { "name", "days" } objects still reads; the day counts are ignored, since they never reached the game.

One epoch, two eras

A calendar has exactly one epoch, so it has at most two eras: before it and after it. A world with several ages, a First, Second and Third Age each counted from its own year 1, cannot be declared in full. Pick the age most of your dates fall in as after, and the age just before it as before:

{ "epoch": 4031, "after": "TA", "before": "SA" }

Script year 4031 reads 1 TA and 4030 reads 1 SA, but the Second Age counts backwards from its end rather than forwards from its start, and a First Age year reads as a Second Age year counted further back (script year 500 reads 3531 SA). Leave before out instead and every year under the epoch keeps its script form in the hints and the hover, and Insert Date does not accept it. A list of eras is a possible extension, not a planned one; ask on the Discord if your mod needs it.

The setting, and where it has to live

px.calendar is still there as the fallback for a mod with no calendar file, and it takes the same shape. The file wins wherever both exist.

The setting is window-scoped: VS Code reads it from the folder you actually open, or from your .code-workspace file, and nowhere else. That matters in the mod projects layout, where you often open the project folder and the mod sits one level down. A px.calendar in <project>/mod/.vscode/settings.json is read by nobody, and every date feature stays quietly off. The toolkit looks for that case once per workspace and offers Move Into Mod, which writes the declaration to <mod>/.px-toolkit/calendar.json where it counts, next to a button that opens the ignored settings file.

What you see in the editor

Inlay hints follow every date in a script file: 3000.1.1 gets 1000 BC after it, 3000.3.15 gets 15 March 1000 BC. The first of January reads as the bare year, which is how start dates read in game. Quoted strings are skipped, so a "1.12.3" version number stays a version number, and comment tails are left alone. Script files only: localization, .gui, descriptor.mod and _*.info files get no date hints.

Hover a date for the full card: the script form, the display form, and the rule in force, named after where it came from (.px-toolkit/calendar.json or the setting). Useful when you inherit a file and want to check which calendar the mod is on.

Paradox: Insert Date (Display Calendar) goes the other way. Type the date the way the mod shows it and the command inserts the script date the game logic needs:

You type Inserted
1000 BC 3000.1.1
1000 BC March 15 3000.3.15
1000 BC 3 15 3000.3.15
1 AD 4000.1.1
500 4499.1.1 (the era defaults to after)

The month takes a number or a name, and a unique prefix is enough (Mar, Narv). The input box shows the conversion as you type, so 1000 BC March 15 → inserts 3000.3.15 sits under the field before you press Enter, and a bad date says why (February has 28 days). The command replaces your selection, or inserts at every cursor in a multi-cursor edit.

The creators read it too. The Dynasty Tree shows every card's years and every date control in the mod's eras and month names, with a Calendar / Script toggle in its toolbar for the game's own year.month.day, and the Culture Creator checks a hybrid culture's creation date the same way (Content Creators).

Nothing rewrites your files. The hints and the hover are display only, and Insert Date writes only where you asked it to. No existing date in your mod is ever converted.

Making the game show the same calendar

The declaration above teaches the editor your calendar. Paradox: Generate Calendar Localization (Display Calendar) writes the other half, the loc that makes the game itself display it. Two files land in the mod, named for the language in px.locLanguage (english by default):

  • localization/<lang>/px_calendar_l_<lang>.yml holds the era math as two new keys, PX_CAL_YEAR and PX_CAL_ERA, built from the engine's own Select_int32 and Subtract_int32 datafunctions.
  • localization/replace/<lang>/px_calendar_dates_l_<lang>.yml overrides the engine's date-format keys (GAME_DATE_STRING, GAME_DATE_STRING_SHORT, GAME_DATE_STRING_LONG) with the vanilla formats, routing the raw $YEAR$ through those two keys. Declare custom months and the engine's CW_DATE_* month names are overridden here too, long and abbreviated form both.

These are the only calendar files the command touches, and the names are fixed. Change the calendar, run the command again, confirm the overwrite prompt, and both files regenerate in place.

Four things to know before you run it:

  • CK3 only for now. The date-format key names are verified against the game's own files and binary, and only CK3's are. On Victoria 3 and EU5 the command says so and writes nothing; the editor-side hints, hover and Insert Date work on all three games regardless.
  • The months are engine-fixed. Twelve, with the lengths above, and no loc key changes either. Declare twelve names and they replace the vanilla ones everywhere a date is written.
  • One language per run. Other languages your mod ships need the same two files in their own folders. Switch px.locLanguage and run again.

The generated files carry a header saying they came from your calendar declaration, because hand-editing them and then regenerating loses the edit.

See Configuration for px.calendar, the .px-toolkit/ folder and px.locLanguage next to every other setting.

Clone this wiki locally