using System;
using System.Linq;
using StardewValley;
namespace StardewModdingAPI.Utilities
{
/// Represents a Stardew Valley date.
public class SDate
{
/*********
** Properties
*********/
/// The internal season names in order.
private readonly string[] Seasons = { "spring", "summer", "fall", "winter" };
/// The number of days in a season.
private readonly int DaysInSeason = 28;
/*********
** Accessors
*********/
/// The day of month.
public int Day { get; }
/// The season name.
public string Season { get; }
/// The year.
public int Year { get; }
/*********
** Public methods
*********/
/// Construct an instance.
/// The day of month.
/// The season name.
/// One of the arguments has an invalid value (like day 35).
public SDate(int day, string season)
: this(day, season, Game1.year) { }
/// Construct an instance.
/// The day of month.
/// The season name.
/// The year.
/// One of the arguments has an invalid value (like day 35).
public SDate(int day, string season, int year)
{
// validate
if (season == null)
throw new ArgumentNullException(nameof(season));
if (!this.Seasons.Contains(season))
throw new ArgumentException($"Unknown season '{season}', must be one of [{string.Join(", ", this.Seasons)}].");
if (day < 1 || day > this.DaysInSeason)
throw new ArgumentException($"Invalid day '{day}', must be a value from 1 to {this.DaysInSeason}.");
if (year < 1)
throw new ArgumentException($"Invalid year '{year}', must be at least 1.");
// initialise
this.Day = day;
this.Season = season;
this.Year = year;
}
/// Get a new date with the given number of days added.
/// The number of days to add.
/// Returns the resulting date.
/// The offset would result in an invalid date (like year 0).
public SDate AddDays(int offset)
{
// simple case
int day = this.Day + offset;
string season = this.Season;
int year = this.Year;
// handle season transition
if (day > this.DaysInSeason || day < 1)
{
// get current season index
int curSeasonIndex = Array.IndexOf(this.Seasons, this.Season);
if (curSeasonIndex == -1)
throw new InvalidOperationException($"The current season '{this.Season}' wasn't recognised.");
// get season offset
int seasonOffset = day / this.DaysInSeason;
if (day < 1)
seasonOffset -= 1;
// get new date
day = this.GetWrappedIndex(day, this.DaysInSeason);
season = this.Seasons[this.GetWrappedIndex(curSeasonIndex + seasonOffset, this.Seasons.Length)];
year += seasonOffset / this.Seasons.Length;
}
// validate
if(year < 1)
throw new ArithmeticException($"Adding {offset} days to {this} would result in invalid date {day:00} {season} {year}.");
// return new date
return new SDate(day, season, year);
}
/// Get a string representation of the date. This is mainly intended for debugging or console messages.
public override string ToString()
{
return $"{this.Day:00} {this.Season} Y{this.Year}";
}
/// Get the current in-game date.
public static SDate Now()
{
return new SDate(Game1.dayOfMonth, Game1.currentSeason, Game1.year);
}
/*********
** Private methods
*********/
/// Get the real index in an array which should be treated as a two-way loop.
/// The index in the looped array.
/// The number of elements in the array.
private int GetWrappedIndex(int index, int length)
{
int wrapped = index % length;
if (wrapped < 0)
wrapped += length;
return wrapped;
}
}
}