Simple Timekeeping
A simple and unobtrusive way to keep track of time. Simple Timekeeping docks on top of the screen to always have a reminder of essential date, time and weather information you can change with a click. Syncs with the core Foundry calendar API.
If you are looking for premade calendars, click Here
Introduction
Simple Timekeeping is a lightweight module designed to help Game Masters manage time, weather, and events in their campaigns. It offers an intuitive interface with minimal setup while providing a rich set of features to enhance immersion and storytelling. The module is designed with a philosophy of "simple yet powerful" - offering complex functionality without a steep learning curve. It works with any game system and can be used alongside other Foundry modules.
Interface Overview
The Simple Timekeeping interface consists of several components:
Top Bar Controls
- Time Display: Shows the current day and time
- Navigation Controls: Buttons to move backward/forward in time
- Preset Time Buttons: Quick access to sunrise, sunset, midday, midnight
- Weather Display: Shows current weather condition
- Settings Button: Access to module configuration
Event Panel
- Event List: Displays upcoming and past events
- Search Bar: Filter events by name
- Create Event Button: Add new events to the timeline
Configuration Panel
- Tabs: Navigate between different configuration sections
- Settings: Customize module behavior
- Save Button: Apply configuration changes
Configuration
Time Management
Manual Time Control
The interface provides several ways to adjust the game time:
- Hour Back/Forward: Move time backward/forward by one hour
- Day Back/Forward: Move time backward/forward by one day
- Preset Times: Jump to specific times of day:
- Sunrise: Beginning of daylight
- Midday: Peak daylight
- Sunset: Beginning of darkness
- Midnight: Peak darkness
Combat Integration
The module automatically tracks time during combat:
- Each combat round advances time by the configured "Seconds per Round" value
Real-time Progression
You can enable automatic time progression:
- Game time advances based on "Seconds per Real Second" setting
- For example, setting this to 60 would mean 1 minute of game time passes every real second
Weather Badge
Simple Timekeeping includes some weather presets for you that also set the matching (or closest) weather effect on the current scene. Click on the weather badge to chose from presets or set something custom in the configuration menu.
Event Management
Events in Simple Timekeeping allow you to track important campaign moments and schedule future occurrences.
Creating Events
- Click the "Create Event" button
- Fill in the event details:
- Event Name: Brief descriptive title
- Event Start: Date and time when event begins
- Event End: (Optional) Date and time when event ends
- Repeat: Frequency of recurrence (None, Daily, Weekly, Monthly, Yearly)
Managing Events
- Search: Filter events by name using the search bar
- Edit: Right Click on an event to access it's context menu
Event Display
Events are displayed in the event list, ordered by date. Current and upcoming events are highlighted for easy reference.
Badge System
Badges provide visual indicators for tracking various campaign elements.
Custom Badge Configuration
You can configure up to four custom badges:
- Navigate to module settings
- Under "Custom Badge 1-4":
- Set the label text
- Choose a color (hex code)
Badge Usage Examples
- Moon Phases: Track lunar cycles
- Seasons: Indicate current season
- Campaign Day: Track days since campaign start
- Faction Status: Monitor allegiances
- Holiday: Mark special dates
Scene Integration
Darkness Synchronization
The module can automatically adjust scene darkness based on time of day.
Troubleshooting
Module Not Visible
- The module is not visible during combat. Make sure you don't have an ongoing combat.
API
All methods can be accessed from ui.simpleTimekeeping
Badge Management APIs
These functions provide programmatic control over the visual badge elements in the SimpleTimekeeping interface, allowing developers to dynamically update weather and moon phase displays.
setWeatherBadge(label, color, weatherEffect)
Updates the weather badge display and optionally applies weather effects to the current scene.
Parameters:
label
(string) - The text to display on the weather badge. Will be localized using the module's localization systemcolor
(string) - CSS color value for the badge background (hex, rgb, named colors, etc.)weatherEffect
(string, optional) - Weather effect to apply to the scene. Pass"none"
to clear existing effects
Behavior:
- Saves the weather label and color to persistent module settings
- If
weatherEffect
is provided, updates the current scene's weather property - Passing
"none"
as weatherEffect will clear any existing weather effects by setting an empty string - The badge visibility is controlled by whether a label is set - empty labels hide the badge
Usage Examples:
// Set a storm weather badge with scene effects
await ui.simpleTimekeeping.setWeatherBadge("Heavy Rain", "#4a90e2", "rain");
// Update just the visual badge without scene effects
await ui.simpleTimekeeping.setWeatherBadge("Partly Cloudy", "#87ceeb");
// Clear weather effects but keep the badge
await ui.simpleTimekeeping.setWeatherBadge("Clear Skies", "#87ceeb", "none");
setMoonBadge(label, color)
Updates the moon phase badge display with custom text and styling.
Parameters:
label
(string) - The text to display on the moon badge. Will be localized using the module's localization systemcolor
(string, optional) - CSS color value for the badge background. If not provided, retains the current color setting
Behavior:
- Saves the moon label and optionally the color to persistent module settings
- If color is omitted or null, the existing color configuration is preserved
- The badge visibility is controlled by whether a label is set - empty labels hide the badge
- Will not work if the automatic moon phase calculation system is enabled
Usage Examples:
// Set a custom moon phase with new color
await ui.simpleTimekeeping.setMoonBadge("Waning Crescent", "#c0c0c0");
// Update only the text, keeping existing color
await ui.simpleTimekeeping.setMoonBadge("Full Moon");
// Clear the moon badge
await ui.simpleTimekeeping.setMoonBadge("");
Custom Climate Data
If you wish you could customize the weather generation starting from this sample template. No support is provided for custom climate data. default
is used whenever a matching season or climate is not found.
Valid weather options are clear
partlyCloudy
cloudy
overcast
rain
thunderstorm
drizzle
snow
blizzard
hail
fog
mist
windy
tornado
hurricane
ashfall
sandstorm
luminousSky
bloodRain
manaStorm
arcaneFog
voidstorm
celestialEclipse
meteorShower
frozenHell
sunshower
spectralStorm
etherealDrizzle
wildMagicWinds
Note that seasons are determined independently from how they are setup in the calendar and are based of percentages of days of the year following standard northern emisphere seasons.
Copy paste the following and change the values as your prefer.
{
"default": {
"temperature": {
"spring": {
"min": 10,
"max": 18
},
"summer": {
"min": 18,
"max": 30
},
"autumn": {
"min": 8,
"max": 16
},
"winter": {
"min": -5,
"max": 5
},
"default": {
"min": 8,
"max": 20
}
},
"weather": {
"spring": {
"clear": 5,
"rain": 4,
"cloudy": 3,
"mist": 2
},
"summer": {
"clear": 6,
"partlyCloudy": 4,
"thunderstorm": 2
},
"autumn": {
"rain": 4,
"cloudy": 4,
"windy": 2
},
"winter": {
"snow": 5,
"blizzard": 3,
"fog": 2
},
"default": {
"rain": 3,
"cloudy": 3,
"mist": 2,
"drizzle": 2
}
}
}
}
AI Calendar Generator
If you wish, you can use this AI Foundry calendar generator created by @Phenomen
here (opens in a new tab).
Custom Calendar
Simple Timekeeping utilizes the core Foundry API for the calendar, if you want to have specifics beyond the examples below, visit the FVTT API Reference (opens in a new tab)
Custom Simple Timekeeping values
Simple Timekeeping will read some additional values from the configuration. You can check the Harptos configuration for the most complete example.
Dusk\Dawn
Months support dusk and dawn properties, ranging from 0 to 1, where in a 24hrs day 0.25 is 06:00, 0.5 is 12:00 and 1 is 24:00. Scale it accordingly for non-24hrs calendars.
{ "name": "Hammer", "abbreviation": "Ham", "ordinal": 1, "days": 31, "dawn": 0.33, "dusk": 0.69 }
Moons
The moons key can hold data regarding moons (or planets) and will be used to dynamically update the moon badge. Phase names must be in order starting from new moon. The moon cycle is fixed and starts at time 0, if you need to offset it, use the offset property. Both cycle and offset are in days.
{
"moons": {
"values": [
{
"name": "Selûne",
"cycleLength": 30,
"phaseNames": [
"New Moon",
"Waxing Crescent",
"First Quarter",
"Waxing Gibbous",
"Full Moon",
"Waning Gibbous",
"Last Quarter",
"Waning Crescent"
],
"offset": 0
}
]
}
}
Custom moons setting
The custom moons setting allows to define a JSON that overrides the moons defined in the calendar, it just needs the values of the moons and not the full moon object
[
{
"name": "Selûne",
"cycleLength": 30,
"offset": 0
}
]
In this example, the phase names are also omitted, if you omit the phase names, the default ones for the earth moon will be used.
Sample Calendars
These examples are already included in the module but here they are provided in JSON format for easy reference or customization. You can also view the currently active calendar JSON while in-game by using the console command CONFIG.time.worldCalendarConfig
.
Harptos Calendar (Forgotten Realms)
{
"name": "Harptos (Forgotten Realms)",
"id": "harptos",
"system": "dnd5e",
"description": "The standard calendar of the Forgotten Realms, consisting of twelve 30-day months punctuated by five annual festival days.",
"years": {
"yearZero": 0,
"firstWeekday": 0,
"leapYear": {
"leapStart": 4,
"leapInterval": 4
}
},
"months": {
"values": [
{
"name": "Hammer",
"abbreviation": "Ham",
"ordinal": 1,
"days": 30,
"startingWeekday": 0
},
{
"name": "Midwinter",
"abbreviation": "Mid",
"ordinal": 1,
"days": 1,
"startingWeekday": 0,
"intercalary": true
},
{
"name": "Alturiak",
"abbreviation": "Alt",
"ordinal": 2,
"days": 30,
"startingWeekday": 0
},
{
"name": "Ches",
"abbreviation": "Che",
"ordinal": 3,
"days": 30,
"startingWeekday": 0
},
{
"name": "Tarsakh",
"abbreviation": "Tar",
"ordinal": 4,
"days": 30,
"startingWeekday": 0
},
{
"name": "Greengrass",
"abbreviation": "Gre",
"ordinal": 4,
"days": 1,
"intercalary": true,
"startingWeekday": 0
},
{
"name": "Mirtul",
"abbreviation": "Mir",
"ordinal": 5,
"days": 30,
"startingWeekday": 0
},
{
"name": "Kythorn",
"abbreviation": "Kyt",
"ordinal": 6,
"days": 30,
"startingWeekday": 0
},
{
"name": "Flamerule",
"abbreviation": "Fla",
"ordinal": 7,
"days": 30,
"startingWeekday": 0
},
{
"name": "Midsummer",
"abbreviation": "Mid",
"ordinal": 7,
"days": 1,
"intercalary": true,
"startingWeekday": 0
},
{
"name": "Shieldmeet",
"abbreviation": "Shi",
"ordinal": 7,
"days": 0,
"leapDays": 1,
"intercalary": true,
"startingWeekday": 0
},
{
"name": "Eleasis",
"abbreviation": "Ele",
"ordinal": 8,
"days": 30,
"startingWeekday": 0
},
{
"name": "Eleint",
"abbreviation": "El",
"ordinal": 9,
"days": 30,
"startingWeekday": 0
},
{
"name": "Highharvestide",
"abbreviation": "Hig",
"ordinal": 9,
"days": 1,
"intercalary": true,
"startingWeekday": 0
},
{
"name": "Marpenoth",
"abbreviation": "Mar",
"ordinal": 10,
"days": 30,
"startingWeekday": 0
},
{
"name": "Uktar",
"abbreviation": "Ukt",
"ordinal": 11,
"days": 30,
"startingWeekday": 0
},
{
"name": "Feast of the Moon",
"abbreviation": "Fes",
"ordinal": 11,
"days": 1,
"intercalary": true,
"startingWeekday": 0
},
{
"name": "Nightal",
"abbreviation": "Nig",
"ordinal": 12,
"days": 30,
"startingWeekday": 0
}
]
},
"days": {
"values": [
{
"name": "First-day",
"abbreviation": "1D",
"ordinal": 1
},
{
"name": "Second-day",
"abbreviation": "2D",
"ordinal": 2
},
{
"name": "Third-day",
"abbreviation": "3D",
"ordinal": 3
},
{
"name": "Fourth-day",
"abbreviation": "4D",
"ordinal": 4
},
{
"name": "Fifth-day",
"abbreviation": "5D",
"ordinal": 5
},
{
"name": "Sixth-day",
"abbreviation": "6D",
"ordinal": 6
},
{
"name": "Seventh-day",
"abbreviation": "7D",
"ordinal": 7,
"isRestDay": true
}
],
"daysPerYear": 365,
"hoursPerDay": 24,
"minutesPerHour": 60,
"secondsPerMinute": 60
},
"seasons": {
"values": [
{
"name": "Spring",
"monthStart": 3,
"monthEnd": 6,
"dayStart": 78,
"dayEnd": 171
},
{
"name": "Summer",
"monthStart": 6,
"monthEnd": 9,
"dayStart": 171,
"dayEnd": 264
},
{
"name": "Autumn",
"monthStart": 9,
"monthEnd": 12,
"dayStart": 264,
"dayEnd": 354
},
{
"name": "Winter",
"monthStart": 12,
"monthEnd": 3,
"dayStart": 354,
"dayEnd": 78
}
]
},
"moons": {
"values": [
{
"name": "Selûne",
"cycleLength": 30,
"offset": 0
}
]
}
}
Gregorian Simplified
This calendar uses the built in localization keys for the gregorian calendar instead of using the straight up localized names.
{
"name": "Simplified Gregorian",
"id": "gregorian",
"description": "The Gregorian calendar with some simplifications regarding leap years or seasonal timing.",
"years": {
"yearZero": 0,
"firstWeekday": 0,
"leapYear": {
"leapStart": 8,
"leapInterval": 4
}
},
"months": {
"values": [
{
"name": "CALENDAR.GREGORIAN.January",
"abbreviation": "CALENDAR.GREGORIAN.JanuaryAbbr",
"ordinal": 1,
"days": 31
},
{
"name": "CALENDAR.GREGORIAN.February",
"abbreviation": "CALENDAR.GREGORIAN.FebruaryAbbr",
"ordinal": 2,
"days": 28,
"leapDays": 29
},
{
"name": "CALENDAR.GREGORIAN.March",
"abbreviation": "CALENDAR.GREGORIAN.MarchAbbr",
"ordinal": 3,
"days": 31
},
{
"name": "CALENDAR.GREGORIAN.April",
"abbreviation": "CALENDAR.GREGORIAN.AprilAbbr",
"ordinal": 4,
"days": 30
},
{
"name": "CALENDAR.GREGORIAN.May",
"abbreviation": "CALENDAR.GREGORIAN.MayAbbr",
"ordinal": 5,
"days": 31
},
{
"name": "CALENDAR.GREGORIAN.June",
"abbreviation": "CALENDAR.GREGORIAN.JuneAbbr",
"ordinal": 6,
"days": 30
},
{
"name": "CALENDAR.GREGORIAN.July",
"abbreviation": "CALENDAR.GREGORIAN.JulyAbbr",
"ordinal": 7,
"days": 31
},
{
"name": "CALENDAR.GREGORIAN.August",
"abbreviation": "CALENDAR.GREGORIAN.AugustAbbr",
"ordinal": 8,
"days": 31
},
{
"name": "CALENDAR.GREGORIAN.September",
"abbreviation": "CALENDAR.GREGORIAN.SeptemberAbbr",
"ordinal": 9,
"days": 30
},
{
"name": "CALENDAR.GREGORIAN.October",
"abbreviation": "CALENDAR.GREGORIAN.OctoberAbbr",
"ordinal": 10,
"days": 31
},
{
"name": "CALENDAR.GREGORIAN.November",
"abbreviation": "CALENDAR.GREGORIAN.NovemberAbbr",
"ordinal": 11,
"days": 30
},
{
"name": "CALENDAR.GREGORIAN.December",
"abbreviation": "CALENDAR.GREGORIAN.DecemberAbbr",
"ordinal": 12,
"days": 31
}
]
},
"days": {
"values": [
{
"name": "CALENDAR.GREGORIAN.Monday",
"abbreviation": "CALENDAR.GREGORIAN.MondayAbbr",
"ordinal": 1
},
{
"name": "CALENDAR.GREGORIAN.Tuesday",
"abbreviation": "CALENDAR.GREGORIAN.TuesdayAbbr",
"ordinal": 2
},
{
"name": "CALENDAR.GREGORIAN.Wednesday",
"abbreviation": "CALENDAR.GREGORIAN.WednesdayAbbr",
"ordinal": 3
},
{
"name": "CALENDAR.GREGORIAN.Thursday",
"abbreviation": "CALENDAR.GREGORIAN.ThursdayAbbr",
"ordinal": 4
},
{
"name": "CALENDAR.GREGORIAN.Friday",
"abbreviation": "CALENDAR.GREGORIAN.FridayAbbr",
"ordinal": 5
},
{
"name": "CALENDAR.GREGORIAN.Saturday",
"abbreviation": "CALENDAR.GREGORIAN.SaturdayAbbr",
"ordinal": 6,
"isRestDay": true
},
{
"name": "CALENDAR.GREGORIAN.Sunday",
"abbreviation": "CALENDAR.GREGORIAN.SundayAbbr",
"ordinal": 7,
"isRestDay": true
}
],
"daysPerYear": 365,
"hoursPerDay": 24,
"minutesPerHour": 60,
"secondsPerMinute": 60
},
"seasons": {
"values": [
{
"name": "CALENDAR.GREGORIAN.Spring",
"monthStart": 3,
"monthEnd": 5
},
{
"name": "CALENDAR.GREGORIAN.Summer",
"monthStart": 6,
"monthEnd": 8
},
{
"name": "CALENDAR.GREGORIAN.Fall",
"monthStart": 9,
"monthEnd": 11
},
{
"name": "CALENDAR.GREGORIAN.Winter",
"monthStart": 12,
"monthEnd": 2
}
]
},
"moons": {
"values": [
{
"name": "Moon",
"cycleLength": 30,
"offset": 0
}
]
}
}