# Jovian Calendar A configurable in-game calendar and world clock system for Unity. Supports custom day lengths, hours, minutes, months with variable lengths, named months, week tracking, and a serializable date-time struct. No external dependencies. ## Requirements - Unity 2022.3 or later - No additional package dependencies ## Quick Start ### 1. Create a CalendarSettings asset In the Unity Editor, go to **Assets > Create > Jovian > Calendar > Calendar Settings**. Configure: - **Seconds Per Full Day**: How many real-time seconds equal one full in-game day (the 0-1 cycle) - **Hours Per Day**: In-world hours per day (e.g. 24) - **Minutes Per Hour**: In-world minutes per hour (e.g. 60) - **Days Per Month**: Array defining the length of each month. Array length = months per year - **Month Names**: Display names for each month (must match Days Per Month length) - **Days Per Week**: Optional week length (0 to disable) - **Start Date**: Starting year, month, day, hour, minute for the clock ### 2. Create and tick a WorldClock ```csharp using Jovian.Calendar; // Create the clock from settings var clock = new WorldClock(calendarSettings); // Each frame, feed it a 0-1 normalized day progress clock.Tick(normalizedDayTime); // Query the current date and time WorldDateTime now = clock.Now; Debug.Log($"Year {now.year}, {clock.GetMonthName()} {now.DisplayDay}, {now.TimeString}"); ``` ### 3. Display the date ```csharp // Individual fields int year = clock.Now.year; int month = clock.Now.DisplayMonth; // 1-indexed int day = clock.Now.DisplayDay; // 1-indexed string time = clock.Now.TimeString; // "HH:MM" // Full display string string full = clock.FullStringNamed(); // e.g. "14:30, 5th day of Frostmoon, year 3" ``` ## CalendarSettings ScriptableObject defining calendar rules. Create via **Assets > Create > Jovian > Calendar > Calendar Settings**. | Field | Type | Description | |---|---|---| | `secondsPerFullDay` | float | Real-time seconds for one full 0-1 day cycle | | `hoursPerDay` | int | In-world hours per day | | `minutesPerHour` | int | In-world minutes per hour | | `daysPerMonth` | int[] | Days in each month. Array length = months per year | | `monthNames` | string[] | Display name per month. Must match daysPerMonth length | | `daysPerWeek` | int | Days per week (0 to disable week tracking) | | `startYear` | int | Starting year | | `startMonth` | int | Starting month (0-indexed) | | `startDay` | int | Starting day (0-indexed) | | `startHour` | int | Starting hour | | `startMinute` | int | Starting minute | **Computed properties:** - `MonthsPerYear` -- derived from daysPerMonth array length - `TotalDaysInYear` -- sum of all daysPerMonth entries - `MinutesPerDay` -- hoursPerDay * minutesPerHour ## WorldClock The core clock class. Feed it a normalized 0-1 day progress each tick. It accumulates world-minutes internally, handling day wrap-around and fractional minute accumulation across frames. ```csharp var clock = new WorldClock(settings); // Tick with normalized time (0 = midnight, 0.5 = noon, 1 = next midnight) clock.Tick(normalizedTime); // Manual time skip (sleep, rest, cutscene) clock.AdvanceMinutes(480); // skip 8 hours // Queries WorldDateTime now = clock.Now; long elapsed = clock.TotalElapsedMinutes; float normalized = clock.NormalizedTimeOfDay; // 0-1 for lighting/skybox int weekday = clock.DayOfWeek; // 0-indexed, -1 if disabled string monthName = clock.GetMonthName(); string display = clock.FullStringNamed(); ``` ## WorldDateTime A serializable struct representing a point in calendar time. Implements `IEquatable` and `IComparable` for collections and sorting. ```csharp WorldDateTime dt = clock.Now; // 0-indexed fields dt.year; dt.month; dt.day; dt.hour; dt.minute; // 1-indexed display properties dt.DisplayDay; // day + 1 dt.DisplayMonth; // month + 1 // Formatting dt.TimeString; // "14:30" dt.DateString; // "5/3/1" dt.FullString; // "5/3/1 14:30" // Comparison if(dateA < dateB) { ... } if(dateA == dateB) { ... } var sorted = dates.OrderBy(d => d); ``` ## Integration Example ```csharp // In your game state initialization: var calendarSettings = Addressables.LoadAssetAsync("CalendarSettings").WaitForCompletion(); var worldClock = new WorldClock(calendarSettings); // In your time handler's Tick(): float normalizedTime = localTime / dayLength; worldClock.Tick(normalizedTime); // Update UI: dayText.text = $"Day {worldClock.Now.DisplayDay}, {worldClock.GetMonthName()}"; // Use NormalizedTimeOfDay for visual effects: float t = worldClock.NormalizedTimeOfDay; skyboxMaterial.SetFloat("_Blend", t); directionalLight.intensity = sunCurve.Evaluate(t); ``` ## API Reference | Type | Description | |---|---| | `CalendarSettings` | ScriptableObject defining calendar rules (day length, months, weeks, start date) | | `WorldClock` | Core clock class. Tick with normalized time, query WorldDateTime | | `WorldDateTime` | Serializable struct for date-time values. IEquatable, IComparable, operator overloads |