How to model a time range generically that will allow a range for any period of time, or a more specific period of time

StackOverflow https://stackoverflow.com/questions/12878088

Question

Imagine if you wanted to model a non-fraction time range, that could be any of the following:

"1 hour" (all/any 1 hour period)
"1 hour, starting 1pm")  (all/any 1 hour periods that start at 1pm)
"1 hour, starting 1pm, on Wednesdays" (all/any 1 hour periods that start at 1pm on wednesdays)
"1 hour, starting 1pm, on 3rd Wednesday in November"
"1 week, starting the first week in November"

You get the idea. An additional goal is to easily and efficiently compute overlap and subsets of these ranges. e.g. "1 hour, starting 1pm, on Wednesdays" overlaps with "1 hour, starting 1pm"

Additional info: This is for time periods in a baselining system. I want to have multiple granularities of time periods for baseline segments. Like the baseline for any 1 hour period at 1pm or the baseline for the 1 hour period starting at 1pm on the 3rd Wednesday of November.

An additional consideration is these baseline periods will be stored in a no-sql store, and it would be nice to efficiently fine periods in the smallest granularity that exist in the store. (does specific day-week-hour period exist? no, how about week-hour?, no? how about just a day-hour period) - if that makes sense. Maybe a tree-like hierarchy of some sort.

edit: the storing and querying part is probably the most important requirement. There will be billions of time periods stored, and they need to be looked up as fast as possible, (finding the finest granularity that exists). I will gladly sacrifice completeness for speed of looking up.

edit: thinking about it more, and how it will have to be stored in the datastore, a tree-like structure might be good for efficient look-ups. I can just walk down the tree to get the finest granularity that exists.

          1hr
          /
       1hr@1pm
       /
     1hr@1pm@wednesday
     /
   1hr@1pm@wednesday@November

This is what I've come up with, but I feel it is pretty weak. I'm going to keep fiddling with it and updating here, but I'm curious to see if someone has a smarter way to model this.

public class DateRange {

    Integer fTimeSpan;
    TimeUnit fTimeUnit;
    Integer fStartHour;
    Integer fStartDay;
    Integer fStartWeek;
    Integer fStartMonth;

    boolean intersects(DateRange other) { ... }

}
enum TimeUnit {
    HOURS,
    DAYS,
    WEEKS,
    MONTHS;

}

edit: A tree-based structure (like in my edit above) would be a lot simpler. No unused fields for large-grained spans. The granularity would be in the tree, rather than in the datastructure...

public class RangeTreeNode {

    TimeUnit fTimeUnit;
    int fStartTime;
    int fSpanTime;
    List<RangeTreeNode> fChildren;
}
Was it helpful?

Solution

Abstractions

I think what you are describing can be modelled with Joda Time's Interval class. It supports the notions of Instants, Periods, and Durations:

An interval represents an interval of time from one millisecond instant to another instant. Both instants are fully specified instants in the datetime continuum, complete with time zone.

An instant represents an exact point on the time-line, but limited to the precision of milliseconds.

A period represents a period of time defined in terms of fields, for example, 3 years 5 months 2 days and 7 hours. This differs from a duration in that it is inexact in terms of milliseconds. A period can only be resolved to an exact number of milliseconds by specifying the instant (including chronology and time zone) it is relative to.

A duration represents a duration of time measured in milliseconds. The duration is often obtained from an interval.

Furthermore, it's interface supports overlap, abuts, gap and other Interval relationship methods defined in AbstractInterval.

You may also want to consider Partial for your approach, which is explained in general terms here. This will help you since:

A partial does not fully specify a single point in the datetime continuum, but instead may match multiple points (partial + missing fields + time zone = instant)

Examples

Some examples that relate back to your original question:

import static org.joda.time.DateTimeConstants.NOVEMBER;
import static org.joda.time.DateTimeConstants.WEDNESDAY;
import static org.joda.time.DateTimeFieldType.dayOfMonth;
import static org.joda.time.DateTimeFieldType.dayOfWeek;
import static org.joda.time.DateTimeFieldType.hourOfDay;
import static org.joda.time.DateTimeFieldType.monthOfYear;
import static org.joda.time.Duration.standardDays;
import static org.joda.time.Duration.standardHours;

import org.joda.time.Duration;
import org.joda.time.Partial;

public class Periods {

    public static void main(String[] args) {

        // "1 hour" (all/any 1 hour period)
        Duration d1 = standardHours(1);
        Partial p1 = new Partial();

        // "1 hour, starting 1pm" (all/any 1 hour periods that start at 1pm)
        Duration d2 = standardHours(1);
        Partial p2 = new Partial().withField(hourOfDay(), 13);

        // "1 hour, starting 1pm, on Wednesdays" (all/any 1 hour periods that start at 1pm on Eednesdays)
        Duration d3 = standardHours(1);
        Partial p4 = new Partial().withField(hourOfDay(), 13).withField(hourOfDay(), 1).withField(dayOfWeek(), WEDNESDAY);

        // "1 hour, starting 1pm, on Wednesday in November"
        Duration d4 = standardHours(1);
        Partial p3 = new Partial().withField(hourOfDay(), 13).withField(hourOfDay(), 1).withField(dayOfWeek(), WEDNESDAY).withField(monthOfYear(), NOVEMBER);

        // "1 week, starting the first week in November"
        Duration d5 = standardDays(7);
        Partial p5 = new Partial().withField(dayOfMonth(), 1).withField(monthOfYear(), NOVEMBER);
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top