views:

377

answers:

3

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 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.

+2  A: 

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.

MrTelly
+4  A: 

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!

RM
Ross - thanks a lot for the great code sample. I need both a common definition for a geography, but also ability for users to customize.It's strange that there isn't a way to do it without storing year, neither in Java nor C#, but I guess it doesn't come up often enough to justify a special API.
Jean Barmash
NYWebGuy - N.P. Yeah it does seem like a fairly common problem, interested to know what you come up with in the end. Only thing I may add is that u could have 2 templates to cover n/s hemisphere and users customise form there?
RM
+2  A: 

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.

Leah
Thanks, @Leah. Looks like I have to do something similar to what you are suggesting, although I am working with Groovy / GORM, so I am trying to stay a bit higher than DB-level initially, and will work to ooptimize later .
Jean Barmash