views:

72

answers:

4

I'm having a hard time conceiving an object model that can store time periods like the following:

"Any Wednesday from 2:00PM to 3:00PM"
"April 1, 2010"
"All April for any year"
"Any day in April from 2:00PM to 3:00PM for any year"
"Any day 1:30PM to 2:00PM"
"Any April 1 to Any April 3"

Sometimes the period will be for a specific date, other times it will be for a day of the week plus a time span. Maybe a whole month.

Testing a given DateTime against the model is a seperate problem I will have to solve later. Is there a way to model this idea in POCOs? I'm drawing a blank.

I'm not looking for a set of DateTimes representing two distinct points in time. I'm looking for a structure that will store the same sorts of time periods you'd use to create a cell phone calling plan or a shift rotation.

+1  A: 

What's wrong with storing two DateTimes?

The representation of that can change to any form you want when you need to present/do something else with it, but the start and end points are a must in any operations you do with them.

So when you need a string representation you will just need some custom logic to decide if the DateTimes are in the same day (then output the day only once), if the day is in the current week (only output the day), etc. etc.

Of course you can also hold some measure of granularity or get that from code as well - if the variables don't have time information then it's clear the output should only be "April 1 to April 3"; if the start and end points fall exactly on a month, then only output the month.

I think it's easier than it sounds, because you only need to handle several cases for each component of the date. It will also make comparing and matching intervals later on easier. Of course the actual code can be a lot more nicely structured than what I described - no way I'm suggesting several big nested ifs!

Alex Paven
Surely this assumes that the start and end point is always associated with a specific single point in time. The question does not imply that. The way I read the question, I get the impression that he wants to represent ideas such as “Monday 2 p.m. in any week” or “April 16 in any year”.
Timwi
@Timwi that's correct! It didn't seem like such an indistinct question in my head...
Chris McCall
+1  A: 

Personally, I would probably use a bunch of nullables, for example:

public class /* or struct? */ ArbitraryPointInTime
{
    public int? Year { get; private set; }
    public int? Month { get; private set; }
    public int? Day { get; private set; }
    public int? Hour { get; private set; }
    public int? Minute { get; private set; }
    public int? Second { get; private set; }
    public TimeZone TimeZone { get; private set; }

    // Bunch of constructors that construct a valid structure
    // Prevent nonsensical combinations, e.g. Second specified but Minute null
}

public class /* or struct? */ ArbitraryTimePeriod
{
    public ArbitraryPointInTime? Start { get; private set; }
    public ArbitraryPointInTime? End { get; private set; }

    // Bunch of constructors as above
}

Then I would think about which methods you need in order to manipulate this data. Personally I would keep the structure immutable and have every method return a new instance (unless of course it returns a bool or something). Write a specification for each of those methods, and think hard about all the corner-cases. Eric Lippert has given a few example corner-cases in the comments to the question. Write down what each method should do in each of those cases, even if the answer is “throw an exception”.

Then implement the specification.

Then write a bunch of unit tests. :)

Timwi
A: 

You could write a class that stores a start time (DateTime) and a TimeSpan representing the period beginning at the start time. As Eric mentioned, you'll have to take into account timezones when setting the start date.

rmx
This answer requires the creation of seperate time periods for each distinct span. I want something representing a set of rules to apply to a range of dates.
Chris McCall
+1  A: 

I've been involved a system that presented a similar problem. Our resolution was a Schedule which defined an overarching start & end time, each of which had a collection of (up to seven) ScheduleDay which represent the days of the week.

Schedule
StartDateTime
EndDateTime

ScheduleDay
DayOfWeek (enum for Mon, Tues etc)
StartTime (time of day, expressed as minutes)
EndTime (time of day, expressed as minutes)

This allows time periods such as '1pm - 3pm every Tuesday between 1 March 2010 and 10 September 2010'; e.g. reasonably complex but still pattern-forming time periods.

This is still a bit clunky, though. What you really want is a data representation of a gant chart; a collection of 'on' and 'off' periods that form time intervals.

If I were designing this system I'd consider storing a unary function that takes a DateTime and returns a bool which describes whether the given time exists in the time period. Your business object needs a delegate; it might just be if (date.Year == 2010) return true else return false, or it might be extremely complex depending on the time period you are representing, but it allows to completely extensible time periods (not restrictions on what could be represented).

Unfortunately I don't know how this could be queried inside the database, and it obviously isn't a POCO.

Kirk Broadhurst