Time Point

Represents a point in time to some granularity

07 March 2004

This is part of the Further Enterprise Application Architecture development writing that I was doing in the mid 2000’s. Sadly too many other things have claimed my attention since, so I haven’t had time to work on them further, nor do I see much time in the foreseeable future. As such this material is very much in draft form and I won’t be doing any corrections or updates until I’m able to find time to work on it again.

A time point is, rather obviously, a point in time. I'm writing this on August 28 2000. That's a time point. Which raises the point, so why write about time points as a pattern? After all they are now part of many languages and most class libraries.

The problem lies in that there are a few subtleties to time points that aren't obvious, even to folks who build libraries.

How it Works

The most common issue with time points is that they come at various levels of precision. When I say I'm writing this on August 28 2000, and I say I'm writing this at August 28 2000 2:33:34 pm, I'm saying two different things. One statement is to day precision, and other to second precision. Notice that the second precision is more precise than the day precision, but in this case happens to be less accurate. Any time point needs to know its precision so that you can answer questions such as did this event occur at the same time as another event.

The key point is that you can't rely on only on precision for most domains. Much of business is done at day precision. It doesn't matter when my phone request for transfer of funds occurs during the day, it's just processed according to the day I do it. Otherwise life could get very annoying. If I want to make a transfer the same day a bill is presented for payment, should I worry about exactly what time the bill is presented. Common business practice says no, if it was on the same day, I don't run the risk of going overdrawn or refusing the payment.

Beware of using time points that are too precise for your needs. Many platforms only provide a time point with second or greater precision. So how do I represent any time on August 28 2000? Often you use a convention such as at exactly midnight "00:00:00". That may work in some circumstances but problems leak in. It's often surprisingly easy to leak a few milliseconds into the time point, at which point you get problems because August 28 2000 isn't equal to August 28 2000.

Another tricky area with time points is how to handle time zones. In a similar way with precision, there is no right answer for all applications. Sometimes you'll want a time point with a time zone, sometimes not. A timepoint without a time zone is perfectly sensible, it means within the local time of it's context. You find these where further time zone information is either not useful, or can be obtained from it's context. Be wary of using time zones when you don't need it.

An example of this problem which was a big irritation to me was in Microsoft's Outlook. Any appointment times you put in (at least with Outlook 98) were time zone specific. So if I moved time zones and changed the time on my laptop, the time of the meeting would change (unless I'd thought to take that into account when putting the time into outlook in the first place - not a thing most people do.) This was compounded by the fact that if I picked an all day meeting, it didn't hold the day as a timepoint of day precision, it picked a Range of timepoints. So an all day meeting I entered in Boston became one from 11pm to 11pm when I flew to Chicago. What's worse my WinCE machine only displayed all day meetings to day precision, using the start of the range, thus moving my all day appointments to the previous day.

In my discussion of Time Point an important feature of time points is that they are anchored, that is a time zone refers to a particular point on the timeline. A time object whose value is 2:30pm is not anchored because it could mean 2.30pm on any day. If the there is a day precision time point to give context for the 2.30pm, then the 2.30pm, with the day precision time, represents a (properly anchored) time point.

An obvious, and common, service for a timepoint class is to get the current timepoint. Usually this is done by interrogating the system clock using the operating system. However it's a good idea to add indirection here. Testing often requires stable times, and often even operations may require to run the system on Monday as of last Friday. So as well as accessing the system date it's also worth accessing the processing date, which may or may not be the same. Furthermore the processing date should be settable during operation. When you then use logging, you may need to use both the processing and system dates in your logs.

When to Use It

Timepoints, of some form or another, are used pretty much all the time. The key question in using them is to decide about the precision and time zone issues talked about above. Only use a day precision time point if your domain requires it, don't try to fudge it with conventions or a Range, even if it comes to writing your own wrapper class. Similarly don't use time zones unless you really need to. Think about how the people using the system see the world, many people don't think about time zones unless they need to.

Example: A simple date precision wrapper (Java)

Here's part of a simple date precision wrapper that I'm using for examples here. The basic structure wraps a java Gregorian Calendar instance

class MfDate...

  private GregorianCalendar _base;
public MfDate() {
  this(new GregorianCalendar());
}
public MfDate(int year, int month, int day) {
  initialize (new GregorianCalendar(year, month - 1, day));
}
  private void initialize (GregorianCalendar arg) {
      _base = trimToDays(arg);
  }
  private GregorianCalendar trimToDays(GregorianCalendar arg) {
      GregorianCalendar result = arg;
      result.set(Calendar.HOUR_OF_DAY,0);
      result.set(Calendar.MINUTE, 0);
      result.set(Calendar.SECOND, 0);
      result.set(Calendar.MILLISECOND, 0);
      return result;
  }

Notice that I force a trim by setting the appropriate values in the gregorian calendar to zero. I also provide a constructor that works with US style number arguments. Since I use these a lot in book code, I might as well make it easy....

Here's the comparison operations, an example of behavior that I just delegate to the underlying objects.

class MfDate...

  public boolean after (MfDate arg) {
    return getTime().after(arg.getTime());
  }
  public boolean before (MfDate arg) {
    return getTime().before(arg.getTime());
  }
  public int compareTo(Object arg) {
    MfDate other = (MfDate) arg;
    return getTime().compareTo(other.getTime());
  }
  public boolean equals(Object arg) {
    if (! (arg instanceof MfDate)) return false;
    MfDate other = (MfDate) arg;
    return (_base.equals(other._base));
  }
    public Date getTime() {
        return _base.getTime();
    }

A wrapper also allows me to add behavior that I find handy, but isn't there on the base class.

class MfDate...

  public MfDate addDays(int arg) {
    return new MfDate(new GregorianCalendar(getYear(), getMonth(), getDayOfMonth() + arg));
  }
    public MfDate minusDays(int arg) {
        return addDays(-arg);
    }