I need to model some information about seasons, and need to track them based on start / end date in a year-agnostic way. I.e. I need to allow users to define summer as being between, say, May 15th and September 10th, and do that for all years.
I'll need to do a lot of checking of the type isThisDateInSeason). All the date manipulation functions (i.e. Date, Calendar) seem to only work with valid dates, i.e. including year information.
Are there any best practices around how to do this? I can think of a bunch of hacky ways of doing it (i.e. store month and day of the month or to store dates and just bring them to a baseline year so I can can compare), but it seems like there might be a better way.
I am writing this in Java or Groovy.
Would the Joda-Time library help here? I don't have experience with it, but it looks to have much more flexibility.
I found this question about how to identify a season from date, but that focuses on months, and I need more flexibility to include dates.
If each user owns their own data (i.e. they specify their seasons and then they enter their own information) then you could just store the data with the season as part of it, however I have a feeling the scenario you are after is for shared data across numerous users who define seasons differently.
You would have to be very careful to 'normalize' dates as the leap year may cause unexpected problems, i.e. trying to set Feb 29 on a non leap year will cause problems / exceptions.
I have put the below together, unforutnatly its c# but the concept will be the same. I havnt actually tested the code, but as a even as psudo code it may help.
public class SeasonChecker
{
public enum Season {Summer, Autumn, Winter, Spring};
private List<SeasonRange> _seasons = new List<SeasonRange>();
public void DefineSeason(Season season, DateTime starting, DateTime ending)
{
starting = starting.Date;
ending = ending.Date;
if(ending.Month < starting.Month)
{
// split into 2
DateTime tmp_ending = new DateTime(ending.Year, 12, 31);
DateTime tmp_starting = new DateTime(starting.Year, 1, 1);
SeasonRange r1 = new SeasonRange() { Season = season, Starting= tmp_starting, Ending = ending };
SeasonRange r2 = new SeasonRange() { Season = season, Starting= starting, Ending = tmp_ending };
this._seasons.Add(r1);
this._seasons.Add(r2);
}
else
{
SeasonRange r1 = new SeasonRange() { Season = season, Starting= starting, Ending = ending };
this._seasons.Add(r1);
}
}
public Season GetSeason(DateTime check)
{
foreach(SeasonRange range in _seasons)
{
if(range.InRange(check))
return range.Season;
}
throw new ArgumentOutOfRangeException("Does not fall into any season");
}
private class SeasonRange
{
public DateTime Starting;
public DateTime Ending;
public Season Season;
public bool InRange(DateTime test)
{
if(test.Month == Starting.Month)
{
if(test.Day >= Starting.Day)
{
return true;
}
}
else if(test.Month == Ending.Month)
{
if(test.Day <= Ending.Day)
{
return true;
}
}
else if(test.Month > Starting.Month && test.Month < Ending.Month)
{
return true;
}
return false;
}
}
}
Note the above code makes the assumption that the season will not start and end on the same month - a fairly safe one I think!
I think you'll have to roll your own DateRange class, although this is such a common problem you would hope there is already a clever util out there that does it. As a general point when dealing with seasons you also have to take account of geography, which will make life really hard. In Oz we're the reverse of the US, so Summer runs from November to February.
Where is the data? If it is in a database I would consider making a table for dates (like a time dimension in OLAP). You can then calculate a column for your seasons as they are for financial quarters in in some Time Dimension examples. (this is assuming that your season does not change but has fixed dates).
All the "if date in season" type checking will be pre-built pushing the costs of calculating the season into your setup time rather then your run time.
Edit: Just saw your comment on user configurable season dates. This would still work as you can do a join in a database including the time dimension which could be easier working with the set of data then in Java.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With