using AcDream.Core.World; using Xunit; namespace AcDream.Core.Tests.World; public sealed class DerethDateTimeTests { // ACE calendar anchor: tick 0 = Morningthaw 1, 10 P.Y. at Morntide-and-Half // (slot 7 on the 0-indexed 16-slot scale). The DayFraction function // applies +7/16 offset so "what time is it in the day" reads correctly. [Fact] public void DayFraction_AtTick0_IsMorntideAndHalf() { // Tick 0 = Morntide-and-Half = slot 7 = 7/16 of the day. Assert.Equal(7.0 / 16.0, DerethDateTime.DayFraction(0), 4); } [Fact] public void DayFraction_AtHalfDayFromTick0_IsHalf() { // From tick 0 (slot 7) + half a day = slot 7 + 8 = slot 15 (Gloaming-and-Half). // Verify the formula advances by 0.5 correctly: (0 + DayTicks/2) → 7/16 + 1/2 = 15/16. Assert.Equal(15.0 / 16.0, DerethDateTime.DayFraction(DerethDateTime.DayTicks / 2), 4); } [Fact] public void DayFraction_WrapsAfterOneDay() { // Full day from tick 0 returns to the same slot (Morntide-and-Half = 7/16). Assert.Equal(7.0 / 16.0, DerethDateTime.DayFraction(DerethDateTime.DayTicks), 4); } [Fact] public void CurrentHour_AtTick0_IsMorntideAndHalf() { // ACE ref: DerethDateTime.cs:145 — `hour = (int)Hours.Morntide_and_Half;` Assert.Equal(DerethDateTime.HourName.MorntideAndHalf, DerethDateTime.CurrentHour(0)); } [Fact] public void CurrentHour_AtMidnight_IsDarktide() { // ACE ref: DerethDateTime.cs:25 — `dayOneTicks = 0 + hourOneTicks + (hourTicks * 8) // // Morningthaw 2, 10 P.Y. - Darktide`. From tick 0 to Darktide next day = 4020. // Using our slot-based formula: from slot 7 -> slot 0 (wrap) = 9 slots = 9 * 476.25 // = 4286.25 ticks (slight difference because ACE snaps to quarter hours; our // continuous formula is smoother). We verify slot 0 (Darktide) at 9/16 of a day // past tick 0. double ticks = DerethDateTime.DayTicks * (9.0 / 16.0); Assert.Equal(DerethDateTime.HourName.Darktide, DerethDateTime.CurrentHour(ticks)); } [Fact] public void CurrentHour_AtNoon_IsMidsong() { // Midsong is slot 8 on the 16-slot scale. From tick 0 (slot 7) advance by 1 slot. double ticks = DerethDateTime.DayTicks * (1.0 / 16.0); Assert.Equal(DerethDateTime.HourName.Midsong, DerethDateTime.CurrentHour(ticks)); } [Fact] public void IsDaytime_Tick0_True() { // Morntide-and-Half (slot 7) falls in the daytime band (slots 4..11). Assert.True(DerethDateTime.IsDaytime(0)); } [Fact] public void IsDaytime_Darktide_False() { // Darktide = slot 0. Need tick offset of 9/16 from tick 0 to reach it. double ticks = DerethDateTime.DayTicks * (9.0 / 16.0); Assert.False(DerethDateTime.IsDaytime(ticks)); } [Fact] public void ToCalendar_PY0Day1_Snowreap() { var cal = DerethDateTime.ToCalendar(0); Assert.Equal(0, cal.Year); Assert.Equal(DerethDateTime.MonthName.Snowreap, cal.Month); Assert.Equal(1, cal.Day); } [Fact] public void ToCalendar_AdvancesCorrectly() { // One year from start → PY 1, Snowreap 1. var cal = DerethDateTime.ToCalendar(DerethDateTime.YearTicks); Assert.Equal(1, cal.Year); Assert.Equal(DerethDateTime.MonthName.Snowreap, cal.Month); Assert.Equal(1, cal.Day); // One month into year 1. var cal2 = DerethDateTime.ToCalendar(DerethDateTime.YearTicks + DerethDateTime.MonthTicks); Assert.Equal(1, cal2.Year); Assert.Equal(DerethDateTime.MonthName.ColdMeet, cal2.Month); } }