The Timing of Time

Alan Lovejoy squeak-dev.sourcery at forum-mail.net
Thu Apr 20 03:17:31 UTC 2006


Hi Jeff,

You're asking the right questions.

Jeff: "So how do these various packages handle indexing data by time?"

Well, I won't presume to speak for the other packages, but here's the
situation in Chronos:

Firstly, Chronos dates, times, durations and time periods are all immutable
objects, and fully support comparison operations--including answering a hash
code.  So they can be safely added to Sets, or used as keys in a Dictionary.
Date equality works correctly between different calendars, and Timepoint
equality works correctly between different time zones and/or different
calendars.

It's easy to convert a Chronos YearMonthDay or Timepoint into a count of
days, or to convert a count of days into a YearMonthDay or Timepoint:

	YearMonthDay daysSinceEpoch: YearMonthDay today daysSinceEpoch.

So the problem of indexing by a date resolves to the problem of indexing by
an integer. It's also possible to convert a date into a count of months or
years since the calendar epoch, and to convert a count of months or years
since a calendar epoch into a date.

Both YearMonthDay and Timepoint (which inherits from YearMonthDay) support
date and time arithmetic operations:

	#addingYears:
	#subtractingYears:
	#addingMonths:
	#subtractingMonths:
	#addingDays:
	#subtractingDays:
	#addingYears:months:days:
	#subtractingYears:months:days:
	#addingHours:
	#subtractingHours:
	#addingMinutes:
	#subtractingMinutes:
	#addingSeconds:
	#subtractingSeconds:
	#addingSeconds:nanonseconds:
	#subtractingSeconds:nanoseconds:

Or, durational objects can be used instead:

	Timepoint now + (ScientificDuration microseconds: 75)
	YearMonthDayToday - (CalendarDuration months: 3)
	Timepoint now + (CivilDuration years: 5 months: -3 days: 10 hours:
-1 minutes: 30 seconds: -11)

A method to compute the Timepoint N periods after a base timepoint might
look like this:

SomeClass>>at: index
	^baseTimepoint + (duration * index)

The "duration" instance variable might have the value "ScientificDuration
hours: 3.5", or it might have the value "CalendarDuration months: 3." The
"baseTimepoint" instance variable could hold either a Timepoint, a
YearMonthDay--or even a Timeperiod (interval of time.) For example,
"Timeperiod currentMonth + (CalendarDuration days: 5)" (if executed during
the month of April, 2006) evaluates to "2006-04-06/P1M" (the one-month
interval starting 6 April 2006.)

Does that answer your question about indexing?

Jeff: "What should happen if you start at January 28'th and advance by a
month and then go back by a month?
Jan28 --> Feb28 --> (Jan 28 or Jan 31?)   Can we agree on some
conventions? "

(YearMonthDay year: 2006 month: 1 day: 28) + 1 months =>  2006-02-28
(YearMonthDay year: 2006 month: 1 day: 31) + 1 months =>  2006-02-28
(YearMonthDay year: 2006 month: 1 day: 28) + 1 months - 1 months  =>
2006-01-28
(YearMonthDay year: 2006 month: 1 day: 31) + 1 months - 1 months  =>
2006-01-28

(Note: "1 months" works in VW and Dolphin, but not in Squeak, where
"(CalendarDuration months: 1)" must be used instead--or you could add a
#months and #years method to Integer yourself.)

Chronos implements date arithmetic to satisfy the typical business use case.
If you want "scientific" behavior, use a ScientificDuration.  If you want a
"month" to always be 30 days, use a "monthDuration" value defined as
"CalendarDuration days: 30."

But these questions just scratch the surface.  There's a lot more than can
and should be asked.

--Alan

-----Original Message-----
From: squeak-dev-bounces at lists.squeakfoundation.org
[mailto:squeak-dev-bounces at lists.squeakfoundation.org] On Behalf Of Jeffrey
J. Hallman
Sent: Wednesday, April 19, 2006 9:10 AM
To: squeak-dev at lists.squeakfoundation.org
Subject: Re: The Timing of Time

I'm enjoying the discussion about calendars and times, but I'd like to
direct some attention to the idea of a TimeIndex.  I came at this from
another direction, thinking of a TimeSeries as data indexed by time, which
leads to the idea of a TimeIndex.

In my current implementation, a TimeIndex has two instance variables: a freq
symbol (such as #weeklyMonday, #monthly, #hourly, etc.) and an integer
period, which represents the number of periods elapsed since the base period
for that freq. TimeIndex understands '+' and '-', so if 'z'
is the weeklyMonday time index for April 17 2006, then (z - 4) asYmd yields
20060320, and so on.

My TimeSeries class is a subclass of Matrix with an additional instance
variable called 'start' which is a TimeIndex, and has accessors like

TimeSeries>>atTimeIndex:
TimeSeries>>atTimeIndex:put:
TimeSeries>>atTimeIndex:column:
TimeSeries>>atTimeIndex:column:put:

I've found this scheme works pretty well, and will probably generalize it a
bit more by making TimeIndex an abstract class with subclasses for different
kinds of sequences.

I think a package that wants to handle date and time issues should have
something like a TimeIndex in it that allows sequences of various
frequencies to be defined.  At the same time, the implementation should be
simple enough to be easily understood, and indexing into a TimeSeries should
be very fast.  If indexing isn't fast, we'll end up with ugly code in every
TimeSeries method that that converts TimeIndex'es to row numbers and back.
Trust me, you really don't want that.

So how do these various packages handle indexing data by time?

A second issue is how to handle date arithmetic.  Some durations are based
on linear time (e.g., weeks, seconds, hours, etc.) and others are based on
the calendar (months, years, etc.).  The distinction between these two leads
to questions like: What should happen if you start at January 28'th and
advance by a month and then go back by a month?
Jan28 --> Feb28 --> (Jan 28 or Jan 31?)   Can we agree on some
conventions?

Jeff Hallman





More information about the Squeak-dev mailing list