From 49c2ee517d3276b4547bb25ceda3ebf5e9707887 Mon Sep 17 00:00:00 2001 From: Kevin Daughtridge Date: Mon, 13 Apr 2020 15:42:00 -0700 Subject: SDate: Add WorldDate conversions and features - SeasonIndex - FromWorldDate() - FromDaysSinceStart() - ToLocaleString() - ToWorldDate() --- src/SMAPI/Utilities/SDate.cs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'src') diff --git a/src/SMAPI/Utilities/SDate.cs b/src/SMAPI/Utilities/SDate.cs index 0ab37aa0..301ea9d2 100644 --- a/src/SMAPI/Utilities/SDate.cs +++ b/src/SMAPI/Utilities/SDate.cs @@ -29,6 +29,9 @@ namespace StardewModdingAPI.Utilities /// The season name. public string Season { get; } + /// The season index. + public int SeasonIndex { get; } + /// The year. public int Year { get; } @@ -63,6 +66,20 @@ namespace StardewModdingAPI.Utilities return new SDate(Game1.dayOfMonth, Game1.currentSeason, Game1.year, allowDayZero: true); } + /// Get the date equivalent to the given WorldDate. + /// A date returned from a core game property or method. + public static SDate FromWorldDate(WorldDate worldDate) + { + return new SDate(worldDate.DayOfMonth, worldDate.Season, worldDate.Year, allowDayZero: true); + } + + /// Get the date falling the given number of days after 0 spring Y1. + /// The number of days since 0 spring Y1. + public static SDate FromDaysSinceStart(int daysSinceStart) + { + return new SDate(0, "spring", 1, allowDayZero: true).AddDays(daysSinceStart); + } + /// Get a new date with the given number of days added. /// The number of days to add. /// Returns the resulting date. @@ -98,6 +115,18 @@ namespace StardewModdingAPI.Utilities return $"{this.Day:00} {this.Season} Y{this.Year}"; } + /// Get a string representation of the date in the current game locale. + public string ToLocaleString() + { + return this.ToWorldDate().Localize(); + } + + /// Get the date as an instance of the game's WorldDate class. This is intended for passing to core game methods. + public WorldDate ToWorldDate() + { + return new WorldDate(this.Year, this.Season, this.Day); + } + /**** ** IEquatable ****/ @@ -200,6 +229,7 @@ namespace StardewModdingAPI.Utilities // initialize this.Day = day; this.Season = season; + this.SeasonIndex = this.GetSeasonIndex(season); this.Year = year; this.DayOfWeek = this.GetDayOfWeek(day); this.DaysSinceStart = this.GetDaysSinceStart(day, season, year); -- cgit From 7a60dc4ee9d32831f55fe066c20729ca8e9dc8a1 Mon Sep 17 00:00:00 2001 From: Kevin Daughtridge Date: Mon, 13 Apr 2020 23:29:56 -0700 Subject: SDate: fixes to new methods - FromWorldDate: replace with explicit operator SDate - ToWorldDate: replace with explicit operator WorldDate - ToLocaleString: use Utility.getDateStringFor directly - FromDaysSinceStart: reinterpret exception to an appropriate one --- src/SMAPI/Utilities/SDate.cs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/SMAPI/Utilities/SDate.cs b/src/SMAPI/Utilities/SDate.cs index 301ea9d2..8cb55891 100644 --- a/src/SMAPI/Utilities/SDate.cs +++ b/src/SMAPI/Utilities/SDate.cs @@ -66,18 +66,18 @@ namespace StardewModdingAPI.Utilities return new SDate(Game1.dayOfMonth, Game1.currentSeason, Game1.year, allowDayZero: true); } - /// Get the date equivalent to the given WorldDate. - /// A date returned from a core game property or method. - public static SDate FromWorldDate(WorldDate worldDate) - { - return new SDate(worldDate.DayOfMonth, worldDate.Season, worldDate.Year, allowDayZero: true); - } - /// Get the date falling the given number of days after 0 spring Y1. /// The number of days since 0 spring Y1. public static SDate FromDaysSinceStart(int daysSinceStart) { - return new SDate(0, "spring", 1, allowDayZero: true).AddDays(daysSinceStart); + try + { + return new SDate(0, "spring", 1, allowDayZero: true).AddDays(daysSinceStart); + } + catch (ArithmeticException) + { + throw new ArgumentException($"Invalid daysSinceStart '{daysSinceStart}', must be at least 1."); + } } /// Get a new date with the given number of days added. @@ -118,13 +118,7 @@ namespace StardewModdingAPI.Utilities /// Get a string representation of the date in the current game locale. public string ToLocaleString() { - return this.ToWorldDate().Localize(); - } - - /// Get the date as an instance of the game's WorldDate class. This is intended for passing to core game methods. - public WorldDate ToWorldDate() - { - return new WorldDate(this.Year, this.Season, this.Day); + return Utility.getDateStringFor(this.Day, this.SeasonIndex, this.Year); } /**** @@ -153,6 +147,19 @@ namespace StardewModdingAPI.Utilities /**** ** Operators ****/ + /// Get the SDate equivalent to the given WorldDate. + /// A date returned from a core game property or method. + public static explicit operator SDate(WorldDate worldDate) + { + return new SDate(worldDate.DayOfMonth, worldDate.Season, worldDate.Year, allowDayZero: true); + } + + /// Get the SDate as an instance of the game's WorldDate class. This is intended for passing to core game methods. + public static explicit operator WorldDate(SDate date) + { + return new WorldDate(date.Year, date.Season, date.Day); + } + /// Get whether one date is equal to another. /// The base date to compare. /// The other date to compare. -- cgit From 421bcfcd3e1dcd79c4ab9f9c44c300126ce01fe9 Mon Sep 17 00:00:00 2001 From: Kevin Daughtridge Date: Tue, 14 Apr 2020 00:31:47 -0700 Subject: SDateTests: cover new field and methods --- src/SMAPI.Tests/Utilities/SDateTests.cs | 102 ++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'src') diff --git a/src/SMAPI.Tests/Utilities/SDateTests.cs b/src/SMAPI.Tests/Utilities/SDateTests.cs index d25a101a..3fca9b29 100644 --- a/src/SMAPI.Tests/Utilities/SDateTests.cs +++ b/src/SMAPI.Tests/Utilities/SDateTests.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Text.RegularExpressions; using NUnit.Framework; using StardewModdingAPI.Utilities; +using StardewValley; +using LC = StardewValley.LocalizedContentManager.LanguageCode; namespace SMAPI.Tests.Utilities { @@ -81,6 +83,21 @@ namespace SMAPI.Tests.Utilities Assert.Throws(() => _ = new SDate(day, season, year), "Constructing the invalid date didn't throw the expected exception."); } + /**** + ** SeasonIndex + ****/ + [Test(Description = "Assert the numeric index of the season.")] + [TestCase("01 spring Y1", ExpectedResult = 0)] + [TestCase("02 summer Y1", ExpectedResult = 1)] + [TestCase("28 fall Y1", ExpectedResult = 2)] + [TestCase("01 winter Y1", ExpectedResult = 3)] + [TestCase("01 winter Y2", ExpectedResult = 3)] + public int SeasonIndex(string dateStr) + { + // act + return this.GetDate(dateStr).SeasonIndex; + } + /**** ** DayOfWeek ****/ @@ -147,6 +164,58 @@ namespace SMAPI.Tests.Utilities return this.GetDate(dateStr).ToString(); } + /**** + ** ToLocaleString + ****/ + // TODO: Provide an appropriate XNA/MonoGame context to run this in, or else remove the test. + // [Test(Description = "Assert that ToLocaleString returns the expected string in various locales.")] + // [TestCase("14 spring Y1", LC.en, ExpectedResult = "Day 14 of Spring, Year 1")] + // [TestCase("01 summer Y16", LC.en, ExpectedResult = "Day 1 of Summer, Year 16")] + // [TestCase("28 fall Y10", LC.en, ExpectedResult = "Day 28 of Fall, Year 10")] + // [TestCase("01 winter Y1", LC.en, ExpectedResult = "Day 1 of Winter, Year 1")] + // [TestCase("14 spring Y1", LC.es, ExpectedResult = "Día 14 de primavera, año 1")] + // [TestCase("01 summer Y16", LC.es, ExpectedResult = "Día 1 de verano, año 16")] + // [TestCase("28 fall Y10", LC.es, ExpectedResult = "Día 28 de otoño, año 10")] + // [TestCase("01 winter Y1", LC.es, ExpectedResult = "Día 1 de invierno, año 1")] + // public string ToLocaleString(string dateStr, LC langCode) + // { + // LC oldCode = LocalizedContentManager.CurrentLanguageCode; + // try + // { + // LocalizedContentManager.CurrentLanguageCode = langCode; + // return this.GetDate(dateStr).ToLocaleString(); + // } + // finally + // { + // LocalizedContentManager.CurrentLanguageCode = oldCode; + // } + // } + + /**** + ** FromDaysSinceStart + ****/ + [Test(Description = "Assert that FromDaysSinceStart returns the expected date.")] + [TestCase(1, ExpectedResult = "01 spring Y1")] + [TestCase(2, ExpectedResult = "02 spring Y1")] + [TestCase(28, ExpectedResult = "28 spring Y1")] + [TestCase(29, ExpectedResult = "01 summer Y1")] + [TestCase(141, ExpectedResult = "01 summer Y2")] + public string FromDaysSinceStart(int daysSinceStart) + { + // act + return SDate.FromDaysSinceStart(daysSinceStart).ToString(); + } + + [Test(Description = "Assert that FromDaysSinceStart throws an exception if the number of days is invalid.")] + [TestCase(-1)] // day < 0 + [TestCase(0)] // day == 0 + [SuppressMessage("ReSharper", "AssignmentIsFullyDiscarded", Justification = "Deliberate for unit test.")] + public void FromDaysSinceStart_RejectsInvalidValues(int daysSinceStart) + { + // act & assert + Assert.Throws(() => _ = SDate.FromDaysSinceStart(daysSinceStart), "Passing the invalid number of days didn't throw the expected exception."); + } + /**** ** AddDays ****/ @@ -166,6 +235,17 @@ namespace SMAPI.Tests.Utilities return this.GetDate(dateStr).AddDays(addDays).ToString(); } + [Test(Description = "Assert that AddDays throws an exception if the number of days is invalid.")] + [TestCase("01 spring Y1", -1)] + [TestCase("01 summer Y1", -29)] + [TestCase("01 spring Y2", -113)] + [SuppressMessage("ReSharper", "AssignmentIsFullyDiscarded", Justification = "Deliberate for unit test.")] + public void AddDays_RejectsInvalidValues(string dateStr, int addDays) + { + // act & assert + Assert.Throws(() => _ = this.GetDate(dateStr).AddDays(addDays), "Passing the invalid number of days didn't throw the expected exception."); + } + /**** ** GetHashCode ****/ @@ -194,6 +274,28 @@ namespace SMAPI.Tests.Utilities } } + [Test(Description = "Assert that the SDate operator for WorldDates returns the corresponding SDate.")] + [TestCase(0, ExpectedResult = "01 spring Y1")] + [TestCase(1, ExpectedResult = "02 spring Y1")] + [TestCase(27, ExpectedResult = "28 spring Y1")] + [TestCase(28, ExpectedResult = "01 summer Y1")] + [TestCase(140, ExpectedResult = "01 summer Y2")] + public string Operators_SDate_WorldDate(int totalDays) + { + return ((SDate)new WorldDate { TotalDays = totalDays }).ToString(); + } + + [Test(Description = "Assert that the WorldDate operator returns the corresponding WorldDate.")] + [TestCase("01 spring Y1", ExpectedResult = 0)] + [TestCase("02 spring Y1", ExpectedResult = 1)] + [TestCase("28 spring Y1", ExpectedResult = 27)] + [TestCase("01 summer Y1", ExpectedResult = 28)] + [TestCase("01 summer Y2", ExpectedResult = 140)] + public int Operators_WorldDate(string dateStr) + { + return ((WorldDate)this.GetDate(dateStr)).TotalDays; + } + [Test(Description = "Assert that the == operator returns the expected values. We only need a few test cases, since it's based on GetHashCode which is tested more thoroughly.")] [TestCase(Dates.Now, null, ExpectedResult = false)] [TestCase(Dates.Now, Dates.PrevDay, ExpectedResult = false)] -- cgit