Chronos is one of the many Smalltalk-related blogs syndicated on Planet Smalltalk
χρόνος

Discussion of the Essence# programming language, and related issues and technologies.

Blog Timezone: America/Los_Angeles [Winter: -0800 hhmm | Summer: -0700 hhmm] 
Your local time:  
Showing posts with label concept. Show all posts
Showing posts with label concept. Show all posts

2007-02-02

Why don’t calendars do Time Zones?

Jim Phelps asks (2006-01-18) "Why don’t calendars do Time Zones?"

He laments "My laptop and my palm both understand Time Zones. I understand Time Zones. Why don’t my calendar applications (Oracle’s Calendar and the Palm Calendar in the handheld) understand Time Zones?"

Jim has the following "simple" requests:


  1. When I create an appointment I should be able to mark the Time Zone for the appointment.

  2. I should also be able to make appointments that are Time Zone neutral - not tied to a Time Zone.

  3. My clients (Palm and Desktop and Web) should all understand Time Zones and should be able to shift the alarms to compensate for my changes in Time Zone.

Here's how Jim would use such functionality, were it only available:

  • If I get an invite to join a conference call at 11AM EST, I could just enter that time without adjusting for my local time zone. Same with UTC time.

  • If I changed time zones (say fly from Madison to San Francisco), my alarms would change to be appropriate. As it is now, if I fly to California, I need to keep track of the fact that the 10AM meeting is really a[t] 10AM Central time so I need to set an alarm 2 hours earlier. But I can’t do a global change because some things might be dinners in California so they need to stay on Pacific time.

Of course, Jim's not the only one whose Use Cases aren't well satisfied by most current applications and/or date time libraries. For example, a few years ago, some date/time Use Cases were posted on Zope.org, because Zope's date/time functionality was seen as unsatisfactory. And on BoingBoing, there was the rant "Extended iCal rant from a timezone warrior," whose basic complaint resembles Jim's. There was also recently a discussion on Bugzilla where some of the same issues and concerns were raised.

Here are the key requirements that can be abstracted from the aforementioned discussions/rants:
  • It must be possible to associate each point-in-time value with a time zone that may or may not be the same as the time zone associated with other point-in-time values.
  • It must be possible to specify a point-in-time without reference or association to any particular time zone. (Examples: The point-in-time that specifies the first moment of 2008 (for any and all time zones,) the annual date on which a person's birthday is celebrated, the list of non-business days of the US Federal government, the date on which a person must have been born in order to legally purchase alcohol today.)
  • Point-in-time values must either know, or be able to derive, both their local time and their Universal Time.
  • It must be possible to convert point-in-time values from one time zone to another.
  • The same point-in-time value must be able to act as though it is associated with ("local to") a different time zone in different contexts (e.g., the possibly-different local time zone of different users at the same time, and also the local time zone of the same user at different times, even when the user's local time zone changes as he travels from one location to another.)
  • Point-in-time values should provide both sufficient sub-second resolution and sufficient range (from earliest to latest representable value.)
  • Point-in-time values should provide appropriate granularities--timestamps should span a reasonably small sub-second duration, but dates should span a calendar day (and not be sub-second-resolution timestamps that happen to specify the first moment of the day.)
  • Point-in-time values must be immutable so that changes to their value don't invalidate the logic that uses them (e.g., they must be useable as dictionary keys.)
  • Adding any combination of some number of days, months and years to a point-in-time value should result in a new point-in-time value that number of days/months/years distant--but having the same civil time of day, regardless of any DST transitions that may have occurred.
  • It must be possible specify partial/recurring dates and times (e.g., "every/any day at 5pm," "the last Sunday of October every/any year.")


I was especially interested in what one of the Zope developers had to say in response to the Zope Use Cases posted by the general public (spelling and punctuation as in the original):

"I find all of that very allarming.

Do people want the datetime object solve all this use cases out of the box? I hope that they are only samples of applications and that people agree that they will have to do a little work by theimself.

It is already difficult to find a straightforward interface for basic operations, if we wait to have an universal datetime type, we will never have it before Python 48.12b1 release.

So please, go for a minimal API, and let people play with the type and discover the better way to do elaborated stuff.

By the way from the tz database README file at: http://www.twinsun.com/tz/tz-link.htm you can't guess a time zone from a time-zone offset and a daylight saving flag.

So I think that a full and cross-platform timezone support is just a wonderful dream. But if you can prove me that I'm false, with an actual implementation ..."


I'm quite sure that many programmers would have a similar reaction: They'd be highly alarmed. Well, tough. People don't care how you solve their problems, they just want them solved. A solution that doesn't work right can be worse (or no better than) not having any solution at all.

We've had "a minimal API" for date/time computations available in just about every programming language and operating system for decades now--but the requests, complaints and rants shown above are from recent times, not from decades past. It's almost universally true that programming languages, operating systems and applications either don't handle dates/times at all, or don't do so correctly and consistently, and don't satisfy most of the requirements discussed above.

"Do the simplest thing that could possibly work" is not a license to provide incomplete and/or incorrect functionality. Real use cases that are commonly encountered are not being satisfied. The first step to a cure is to admit there's a problem.

Dates and times are not simple. Not at all. They're a relatively hard problem. The truth is, dates and times are too hard for most programmers to handle correctly on their own--which is precisely why they don't do it, and instead attempt to justify their failure by misapplying design principles. And that's why the "provide the simple stuff, and let the application coders solve the hard problems" approach won't work. It doesn't work in other domains, either--not in the past, not now, and probably not ever.

The "let them do it themselves" approach is about as wise as letting each application programmer code his own number classes, his own collection classes, his own filesystem, his own garbage collection engine, his own virtual memory subsystem, his own graphics library and GUI framework, his own web/application server--and in his spare time, develop his own programming language and compiler. If you really feel that a date/time library that actually gets the job done would be just too complex, then to be consistent, you would have to raise the same objection to all the other tools that programmers don't do themselves anymore (because the problems are hard, and most programmers will take unwise shortcuts, or make serious errors, if they have to do it themselves.)

As time has gone by, we've gone from machine language, to assembly language, to procedural languages, to functional languages, to object-oriented languages. We've gone from no operating system, to reusable, statically-linked code libraries, to dynamically-linked code libraries, to run time systems, to basic operating systems, to operating systems with virtual memory, to operating systems with GUIs. We've gone from punched cards to Microsoft Word, and from the abacus to Microsoft Excel.

Does anyone see a pattern here? Yes, you have to walk before you can run. But we've been crawling around trying to avoid actually dealing with dates and times in all their glory for far too long now. It's time to advance to the next level. So, to the programmers of the world, I say: Admit it. Admit that you're not satisfying the real date/time use cases.

As for an actual implementation of a date/time library that satisfies the requirements we've been discussing, it happens that there is at least one: The Chronos Date/Time Library. Of course, it's a date/time library, not a calendaring/scheduling application, so it would be more accurate to say it makes it much easier to write applications with just about all of the requested features. Yes, applications do need to take some responsibility for their own particular use cases.

What are the features of Chronos that satisfy (or greatly help to satisfy) the requirements discussed above? They are as follows:

  1. Chronos uses the Olson Time Zone Database, so it knows about all the world's time zones--including historical information. For example, Chronos knows, and is able to correctly handle, all the following facts:
    • Most (but not all!) locations in North America have been switching from standard time to daylight saving time on the first Sunday of April each year, from 1987 through 2006--but starting in 2007, most North American locations will switch from standard time to daylight saving time on the second Sunday of March.
    • All the countries in the European Union modernly transition to and from daylight saving time at the same moment in Universal Time--unlike the case in North America, where all zones with the same offset transition to/from daylight saving time at the same local time, but at a different moment in Universal Time.
    • All of India uses the same time zone offset, does not observe daylight saving time, and uses an offset of 5.5 hours east of Universal Time.
    • In Australia, South America, New Zealand and Africa, daylight saving time, if it is observed at all, is observed during the Southern Hemisphere's summer (for example, from September to April.)
    • During World War II, several countries were on "daylight saving time" all year round for several years in a row (in the US, this was called "War Time," and was abbreviated as "EWT," "CWT," "MWT" and "PWT.")
    • For the years 1921 and 1922 only, Moscow started the year with an offset of 3 hours, transitioned to an offset of 4 hours on 1920-02-24T23:00 and 1921-02-24T23:00, transitioned to an offset of 5 hours on the 79th day of the year (different dates and day-of-the-week each year,) transitioned back to an offset of 4 hours on the 244th day of the year, and then transitioned back to an offset of 3 hours on the 274th day of the year (so there were 4 offset transitions and 3 different offsets each year.)

  2. In addition to the predefined time zones that come from the information provided by the Olson Time Zone Database, Chronos permits you to define your own time zones--which can be as simple as "-5 hours asTimezone" or as complex as any of the cases described in the preceding bullet item. Or you can easily construct a Chronos time zone from either a POSIX time zone rule literal or from the information in the Windows registry--and it will behave identically to its UNIX or Windows counterpart.

  3. The Chronos Time Zone Repository (which is generated from the Olson Time Zone Database) is deployed separately and independently from the Chronos codebase. Either can be changed without any need to redeploy the other--and even already-running applications can see and use a newly-deployed version of the time zone repository. And both Chronos and the Chronos Time Zone Repository are available for Windows, Mac, Unix and Linux--so your applications will not only run unchanged in all those environments, but will exhibit identical date/time behavior.

  4. Chronos point-in-time values can be associated with ("bound to" in Chronos terminology) a particular time zone. Or, they can be bound to a proxy time zone that dynamically refers to a different time zone at different times or in different contexts (so that the associated point-in-time's local time changes dynamically whenever the proxy time zone is changed to have a different time zone as its referent.) Or, Chronos point-in-time values can be bound to a special time zone that Chronos refers to as "nominal time." Nominal time represents the concept of "any time zone," or "local time in some unspecified context, or in any and all contexts simultaneously."

  5. Chronos point-in-time values that are bound to a specific time zone or to a proxy time zone (and so aren't bound to nominal time) are said to be invariant to Universal Time. Chronos point-in-time values that are bound to nominal time are said to be invariant to nominal time. Point-in-time values that are invariant to Universal Time keep the moment in time that they represent in Unversal Time as their semantic invariant. Point-in-time values that are invariant to nominal time keep the moment in time that they represent in nominal time (their "local time") as their semantic invariant. In other words, two points-in-time that are invariant to Universal Time are equal when they represent the same moment in Universal Time, regardless of the moment they nominally specify in their local time zone (the one to which they are bound.) But a point-in-time that is invariant to nominal time is equal to any other point-in-time that specifies the same nominal (local) time. So the following expressions both evaluate to true:
    • (Timepoint 
      year: 2007 month: 2 day: 3
      hour: 10 minute: 0 second: 0
      timeZone: -8 hours)
      = (Timepoint
      year: 2007 month: 2 day: 3
      hour: 13 minute: 0 second: 0
      timeZone: -5 hours)
      "10 am in California is the same moment
      in Universal Time as is 1pm in New York"
    • (Timepoint 
      year: 2007 month: 2 day: 3
      hour: 13 minute: 0 second: 0
      timeZone: Timezone nominal)
      = (Timepoint
      year: 2007 month: 2 day: 3
      hour: 13 minute: 0 second: 0
      timeZone: -5 hours)
      "1 pm nominal time is 1pm local time,
      regardless of time zone"

  6. Chronos point-in-time values that are invariant to Universal-Time internally store their value in Universal Time, but report their year, month, day, hour, minute and second according to the equivalent local time in the time zone with which they are associated. Also, the local time values for their year, month and day-of-month are cached (lazily computed when needed--it's an expensive computation.)

  7. Chronos point-in-time values can easily (and efficiently) be converted from any time zone to any other:
    (Timepoint 
    year: 2007 month: 2 day: 3
    hour: 13 minute: 0 second: 0
    timeZone: 'Pacific/Honolulu') >> #'Asia/Katmandu'
    results in 2007-02-04T04:45:00+05:45 (and yes, Asia/Katmandu actually does have an offset of 5 hours 45 minutes.)

  8. Chronos point-in-time values have no limits (other than available memory on your computer) with respect to the range of representable dates--a feature that derives mostly from the fact that Chronos is implemened in Smalltalk--although, even if the implementation language had limited the design to using nothing but 32-bit signed integers, the way Chronos internally represents point-in-time values would still have supported a range of +/- 6 million years for timestamps (and +/- 2 billion years for dates.) Chronos also provides a special value that represents the infinite past, and another that represents the infinite future.

  9. Chronos point-in-time values are actually time intervals, as explained in the Chronos blog entry Chronos 101: "Points" in Time. Chronos points-in-time that are meant to represent dates have a temporal extent (and hence resolution) of one calendar day. Chronos points-in-time that are meant to be used as timestamps have a temporal extent (and hence resolution) of one nanosecond. In addition to these two common cases (one optimized for dates, the other for timestamps,) Chronos provides general Timeperiod values, which can start at any point in time, and can have any temporal extent/duration.

  10. Chronos point-in-time, durational and time interval values are all immutable.

  11. Chronos handles date/time arithmetic correctly, satisfying both civil and scientific/technical use cases. Adding a day results in the same civil time of day on the next calendar day.

  12. Chronos provides a variety of classes for representing partial/recurring dates--including the time-of-day without any associated date, and the month/day without any specified year. You can even construct a value that specifies "the first Tuesday of November, every fourth year modulo 4."

And there's yet more. If you're interested, you can peruse the Chronos web site, browse the Chronos blog, or read the "Chronos 101" tutorials, of which the following have been published so far:

So, to answer Jim's original question, calendar applications don't understand time zones because most people, including software developers, programming language library creators and programming language designers don't understand time zones. Or in some cases, they understand them well enough, but decide (for whatever reason) that full and correct time zone behavior is more work than they want to take on. Fortunately, that unwillingness to tackle the date/time domain comprehensively is not universal.


2007-01-15

Chronos 101: Intervals of Time

This is the third article in a series of articles that conceptually discuss the Chronos model of time. The first one, Chronos 101: "Points" in Time, explains the way Chronos models "points" in time. The second one, Chronos 101: Durational Values, explains the way Chronos models durations of time.

Time Intervals in Chronos



An interval of time has a beginning, and end, and a duration. Unlike a duration, a time interval has a specific location on the timeline: It starts at a specific moment, and it ends at a specific moment.

The duration of a time interval is the amount of elapsed time from the beginning of the interval up to its end. For example, the first quarter of 2007 is a 3-month time interval starting at the first moment of January 1, 2007 and ending as of the last moment of March 31, 2007. The temporal extent (size, length, duration) of the interval is 3 months. The interval begins as of the first moment of January 1, 2007, and ends as of the last moment of March 31, 2007.

Mathematical Analogy



As used in mathematics, an interval is an ordered set of values on a number line, all of which are greater than one value but less than another. Ignoring advanced mathematics and/or degenerate cases, there are four types of interval:
  • Closed: A closed interval contains both its endpoints, one of which is the starting point and the other the ending point. Example: All the numbers between 3 and 4, including both 3 and 4. More formally: [3, 4] is the set of all x such that 3 <= x <= 4.

  • Open: An open interval contains neither its leftmost (starting) point nor its rightmost (ending) point. Example: All the numbers between 3 and 4, including neither 3 nor 4. More formally: (3, 4) is the set of all x such that 3 < x < 4.

  • Left-closed, right-open: A left-closed, right-open interval contains its leftmost (starting) point, but does not contain its rightmost (ending) point. Example: All the numbers between 3 and 4, including 3 but not including 4. More formally: [3, 4) is the set of all x such that 3 <= x < 4.

  • Left-open, right-closed: A left-open, right-closed interval does not contain its leftmost (starting) point, but does contain its rightmost (ending) point. Example: All the numbers between 3 and 4, excluding 3 but including 4. More formally: (3, 4] is the set of all x such that 3 < x <= 4.


Time intervals in Chronos are always left-closed, right-open intervals. A Chronos interval contains its starting/leftmost point in time, but does not contain its ending/rightmost point in time. In Chronos, a time interval of three months duration that starts as of the first moment of January contains every moment of January 1, and contains every moment of March 31, but does not contain any moments of April 1--not even the very first moment.

Time Intervals with Negative Durations



However, a Chronos time interval may have a negative duration. Example: The interval starting on the initial moment of 2007 and extending backwards in time one calendar year (more formally, the set of moments in time such that, for all x that are members of the set, 2006-01-01T00:00:00 < x <= 2007-01-01T00:00:00.) In the case of Chronos time intervals whose duration is negative, the starting moment occurs temporally later than the ending moment, because a negative duration involves traveling backwards in time. In other words, Chronos time intervals have temporal directionality (forwards or backwards.) Why? Because an algorithm might traverse the timeline either forwards or backwards, even though physical objects can only traverse the timeline in the forward direction. This behavior for Chronos time intervals was chosen so that the semantics of a Chronos time interval depends only on the interval itself, with the effect that algorithms that use a Chronos interval can be independent of whether the interval's directionality is forwards or backwards.

So Chronos intervals with positive duration are "earliest-endpoint-closed, latest-endpoint-open" intervals, but Chronos intervals with negative duration are "earliest-endpoint-open, latest-endpoint-closed" intervals. Consequently, Chronos time intervals preserve the invariant that the starting endpoint (from the perspective of the direction in which the timeline is being traversed) are always included in the interval, but the last endpoint (from the perspective of the direction in which the timeline is being traversed) are never included in the interval. Also, from the perspective of real-world objects that traverse the timeline going forward, this means that earliest-open, latest-closed intervals can be represented by simply making the duration negative (and specifying the temporally-latest point-in-time, instead of the temporally-first point-in-time, as the interval's starting/left-most endpoint.)

It should also be noted that ISO 86O1 does not allow time intervals with negative durations (so "2007-01-01T00:00:00/P-1Y" is not legal ISO 86O1--although Chronos supports it anyway.) But ISO 86O1 does allow the first element of a time interval to be a duration. For example, P1Y/2007-01-01T00:00:00 is a legal IS0 86O1 designation of a time interval, where the starting/rightmost moment is computed by subtracting the duration from the rightmost/ending moment. So P1Y/2007-01-01T00:00:00 designates the time interval containing all moments of the year 2006, but not containing any moments of any other year (more formally, P1Y/2007-01-01T00:00:00 specifies the set of moments in time such that, for all x that are members of the set, 2006-01-01T00:00:00 <= x < 2007-01-01T00:00:00.)

Points-in-Time as Special-Case Time Intervals



Even the Chronos "point-in-time" values (instances of YearMonthDay, Timepoint or InfiniteTimepoint) are implemented as time intervals in Chronos. However, the duration of YearMonthDay, Timepoint and InfiniteTimepoint instances is static, is the same for all instances, and no instance variable is used to specify the duration of a YearMonthDay, Timepoint or InfiniteTimepoint (as discussed in the first essay of this series, Chronos 101: "Points" in Time.)

The duration of a YearMonthDay is one calendar day. The duration of a Timepoint is one nanosecond. The duration of an InfiniteTimepoint is "InfiniteDuration positive."

Time Intervals: The General Case



The Chronos class Timeperiod implements time intervals that can have any duration representable using any of the Chronos durational classes (ScientificDuration, CalendarDuration, CivilDuration, InfiniteDuration--as discussed in the essay Chronos 101: Durational Values.)

Timeperiod is not an abstract class, and has no subclasses. By unifying "points-in-time" with time intervals, by delegating as much responsibility as possible to the Chronos durational values, and by leveraging Smalltalk's dynamic typing, a Timeperiod instance can be composed of any combination of one Chronos "point-in-time" value and one Chronos durational value. So there is no need for time-interval classes such as Year, Month or Week, as can be found in Squeak's Chronology date/time library.


2007-01-01

Chronos 101: Durational Values

This is the second article in a series of articles that conceptually discuss the Chronos model of time. The first one, Chronos 101: "Points" in Time, addresses the Chronos concept of "points" in time.

Durational Values in Chronos


What is a durational value? It's simply a value that specifies a duration of time, which differs from a "date" or "point-in-time" value in the same way that a "distance" value differs from a "position" value. A "position" value specifies an absolute location by specifying the distance between the location and some absolute reference point (.e.g, "93 million miles from the Sun.") Similarly, a "point-in-time" value specifies an absolute point in time by specifying the duration since (or until) a particular point-in-time and some absolute reference time (e.g., "5765 years since the creation of the world.") A durational value specifies/designates a temporal extent ("how long,") without specifying when the temporal extent begins or ends.

Civil Time versus Scientific Time


Chronos implemements three different types of durational ("temporal extent") values: "scientific time " durations, "civil time" durations and infinite durations.

The fundamental unit of scientific time is the second--all other durational units of scientific time are defined based on the second. The fundamental unit of civil time is the calendar day--with the possible exception of the second, all other durational units of civil time are defined based on the calendar day.

Scientific time defines the length of a second in terms of physical constants, whereas civil time may or may not do likewise--depending on whether the law in a particular legal jurisdiction specifies that the timescale used to define civil time-of-day is Universal Coordinated Time (UTC,) UT1 (modernly synonymous with Greenwich Mean Time,) or some other timescale.

A timescale defines the size of durations of time (temporal extents.) A calendrical system is a specialized timescale that defines the size of years and months in terms of a count of calendar days--or, to put it differently, a calendrical system defines a bidirectional mapping between a count of days and a year-month-day designation.

UTC is a timescale that defines the size of a civil day in terms of scientific seconds. UT1 (GMT) is a timescale that defines the size of a mean solar second as 1/86,400th of a mean solar day. Both define the time-of-day (and hence, the size of a day,) either directly or indirectly, based on Earth's rotation relative to the position of the Sun in the sky.

A scientific second (used by UTC) has an invariant size (as measured by atomic clocks.) A mean solar second (as defined by UT1) is 1/86,400th of a mean solar day. A mean solar second not only hasn't been the same size as a scientific second as it is currently defined since the early 1800s, but its size varies over time (as measured by atomic clocks.)

If civil time is based on the UTC timescale, then the size of a civil day may differ from most other civil days by one second--plus or minus one leap second. Whether or not there are leap seconds, the length of one civil day may differ from another due to either a regularly-scheduled or one-time-only time-of-day change, which might happen when a) civil time makes an annually-recurring and/or regularly-scheduled transition to or from daylight-saving (summer) time, or b) when the standard-time offset from Universal Time for a locale is "permanently" changed--as happened recently in Indiana, and in 1946 in Hawaii (when Hawaii's standard-time offset from Universal Time was changed from -10:30 hours to -10 hours.)

A civil day during which there is a transition from standard time to daylight-saving (summer) time will have 23 hours, not 24 (assuming there is exactly a one-hour difference between standard time and daylight-saving time, which is not universally true.) A civil day during which there is a transition from daylight-saving (summer) time to standard time will (typically, based on the same assumption as above) have 25 hours, not 24 (in which case, some of the hour:minute:second labels on the local time-of-day clock will occur twice in the same day.)

For timescales other than those based (either directly or indirectly) on the mean solar day, there may be yet other complications and discontinuites. For example, if civil time is based on apparent solar time instead of mean solar time, then the size of hours, minutes and seconds will differ continuously during each day (as measured by a typical clock that isn't a sundial, or doesn't emulate a sundial).

Chronos uses instances of the class ScientificDuration to represent durations of scientific time. Chronos uses instances of either CalendarDuration or CivilDuration to represent durations of civil time. Infinite durations of time are represented by instances of InfiniteDuration, of which there are only two: "InfiniteDuration positive" and "InfiniteDuration negative."

Civil Time: CalendarDuration, CivilDuration


A CalendarDuration or CivilDuration represents an extent (duration) of civil time. Since civil time works in such a way that the number of months in a year may vary (which happens in the Hebrew, Chinese, and Hindu lunisolar calendars, for example,) and the number of a days in a month vary, and the number of days in a year vary, and the number of hours in a day vary (as discussed above,) and the number of minutes in an hour and/or day may vary (as implied by the discussion above,) and the number of seconds in a minute, hour or day may vary (e.g., due to leap seconds,) Chronos represents durations of civil time as a vector of elements, where each vector element represents one of the count of years, months, days, hours, minutes or seconds.

A CalendarDuration only specifies a number of years, months and days. A CivilDuration also specifies a number of hours, minutes, seconds and nanoseconds. The number of years, months, days, hours, minutes, seconds and nanoseconds may each be specified to have a different sign (positive/negative,) and any or all of them may be specified to include a fractional value (e.g., "1.5 years -2 months 13.333 days 20 hours -30 minutes 17.44518 seconds.")

Adding a CalendarDuration or CivilDuration that represents one day of civil time to a Chronos point-in-time value results in a point-in-time that is one civil day later, at the same civil time of day, regardless of the number of hours, minutes and seconds actually between the two points in time.

Scientific Time: ScientificDuration


A ScientificDuration represents an extent (duration) of scientific time, where the size of seconds, minutes, hours and days are absolute invariants.

ScientificDurations do not specify any number of months or years, although they may be converted to and from a count of months and years using both a particular timescale and base time. There is no well-defined answer to the question "How many seconds are there in 30 years?" But there is a well-defined answer to the question "How many seconds are there, according to the UTC timescale, in the thirty-year period starting at 1972-01-01T00:00:00Z?"

Adding a ScientificDuration that represents 24 hours of scientific time to a Chronos point-in-time value results in a point-in-time value that is 86,400 scientific (metric) seconds later--which may or may not have the same civil time-of-day as the original point-in-time value.

Part 3: Chronos 101: Intervals of Time


2006-12-31

Chronos 101: "Points" in Time`

It is common to imagine that a point in time is a precise instant in time, based on the idea that a number line is the optimal mathematical model of time, and based on the idea that an instant is a dimensionless point whose location on the number line can be specified as a real number. In practice, however, the "real number line" model of time does not pass muster.

One problem is that the physics of time does not correspond to the real-number line model, because time is quantized: There is a minimum possible measurable length of time, known as the Planck Time, and also a minimum possible measurable amount of distance and of mass, known respectively as the Planck Length and the Planck Mass.

Another problem is that, in practice, we can't measure durations of time to anywhere near a Plank-time level of precision. Few are those with access to a clock that can measure durations of time with even nanosecond precision--and even that's a billion times less precise than the highest precision time duration measurement we've been able to achieve up to now (2006,) and 10^37 times less precise than a Planck Time.

Generally, physical measurements are not infinitely precise. There are usually "error bars" that prevent the "data points" collected by measurement devices from truly being dimensionless "points." This certainly applies to the measurement of time durations. There is little to be gained from pretending otherwise.

And then there's the problem of clock synchronization. Even without considering the fact that General Relativity prevents the concept of simultaneity among a set of events from having any absolute reality or well-defined physical meaning, clocks simply cannot be perfectly synchronized. Even if clocks existed that could timestamp events with infinite precision, no two of those clocks would ever agree as to the precise timestamp of any one event (to infinite precision.) If each clock that timestamps an event (with infinite precision) provides a slightly different time value, which one is correct?

Also, even if durations of time could physically (and not just conceptually) have infinitesimal extent, it would nevertheless not be possible to specify most "points" in time with infinite precision (the exception being those whose location on the timeline happens to be expressible as a rational number.) You can't physically write out a number with an infinite number of digits, nor store such a number in computer memory. The number must be rounded or truncated.

Considering all the points above, it should therefore be clear that any specification or measurement of a "point" in time (as a coordinate on a number line, expressed as a real number) is necessarily an approximation, even without considering the issues of quantization, quantum uncertainty and General Relativity.

In spite of the foregoing problems and issues, we humans insist on measuring time using discrete, countable durations of time. We like to pretend that each event can be timestamped at a precise instant, and so unambiguously placed in a particular second, minute, hour, day, month and year. There's nothing wrong with that, provided we understand that physical law limits us to approximations valid only to some finite limit of precision.

Consequently, Chronos represents "points" in time to some specified resolution, because physical law prevents "time points" from actually being dimensionless points on a timeline. Chronos does not recognize nor attempt to support what most would conceive of as "instants" in time. Rather, a Chronos point-in-time value specifies the initial instant of some interval of time whose duration is no smaller than the resolution of the internal numeric representation. So Chronos points-in-time are not dimensionless points, but rather intervals of time.

Note that the model of time used by Chronos means that there are no gaps in the Chronos timeline. Chronos thus avoids the problem that occurs in the model of time typically adopted by other date/time libraries, which can only represent some of the points on their limeline, and leave most of the points on the line unspecifiable (so that, in effect, they have to translate most timepoints up or down to the nearest point-in-time representable by the internal implementation of their date/time values.) Of course, in practice the result is the same. But the "dimensionless point" model of points-in-time often leads to strange architectural and design decisions that are not well-motivated, and can even cause suboptimal code.

Currently, Chronos provides three different classes that implement the concept of a "point in time." One is the class YearMonthDay, which has a resolution of one calendar day--which means both that its temporal extent is one day, and also that the minimal (non-zero) difference between any two instances of YearMonthDay is one day. The second one is the class Timepoint, which has a resolution of one nanosecond--which means both that its temporal extent is one nanosecond, and also that the minimal (non-zero) difference between any two instances of Timepoint is one nanosecond. The last one is the class InfiniteTimepoint, whose two instances represent either the infinite past ("InfiniteTimepoint past") or the infinite future ("InfiniteTimepoint future.") InfiniteTimepoints have infinite duration.

So a Chronos "date" (instance of YearMonthDay) is simply a point in time whose resolution (and temporal extent) is one calendar day. And since the only difference between a YearMonthDay and a Timepoint is the representational resolution, there is no reason that they both cannot be mostly type compatible (which, in fact, they are.)

There are also two other fundamental types of time values supported by Chronos: Durations of time (temporal extents) and periods of time (temporal intervals.) A time duration has a length of time, but has no location on the timeline (e.g., "one day," "fifty years," "3 minutes 20.3989 seconds.") A time interval has both a starting point-in-time (a location on the time lime, to some limit of resolution) and a duration (whose extent can be any durational value, not just "day" and "nanosecond"; for example "the time interval starting at 2006-12-22T03:13:06.325914 and lasting for 15 days, 12 hours, 19 minutes and 23.43557 seconds.")

Chronos provides several classes that implement durational values, and the class Timeperiod that implements time intervals.

Part 2: Chronos 101: Durational Values


2006-04-22

Time Semantics: Getting it wrong is easy

Outlook, Appointments and Time Zones:

"To many people, Outlook really seems really bad at handling time zone changes, and that's being kind. This is in part because people don't understand that Outlook uses UTC time for appointments and adjusts the time using the time zone offset configured on the computer."

Translation: You're stupid. Learn to think like your computer!

"Note that this is not "an Outlook problem", as this is how computers, email clients and mail servers handle time zones."

Translation: Other programs work just like Outlook, and the programmers can't all be wrong. [Or can they?]

"Outlook does not support an absolute time option for the calendar, which would permit you to enter 2 PM and the appointment would always stay on 2 PM, no matter how many times you changed the time zone. It also doesn't have an option for ‘in what time zone?’ so that you could make an appointment for 2 PM and select Pacific time zone and it would show up as 5 PM in your calendar when the computer is using the Eastern time zone."

Translation: Ok, we fess up: The problem really does lie in the fact that Outlook uses the wrong time semantics.

The Moral of the Story: Even Billion-Dollar Corporations can get time semantics wrong.

Sometimes, you want timestamps that are invariant to nominal time, so that 3pm = 3pm, with the time not bound to any time zone at all. Other times, you want timestamps that are invariant to Universal Time--so that 2pm in San Francicso will be equal to 5pm in New York.

Sometimes, you want timestamps statically bound to a specified time zone, so that 2pm in San Franciso always is displayed as 2pm (even though the computer treats it as equal to 5pm in New York.) Other times, you want timestamps whose "local time" changes dynamically to match whatever time zone the user specifies as the "local time" of the computer--so that the time displays as 2pm when you've set the computer's time zone to San Francisco time, but displays as 5pm when you've set the computer's time zone to New York time.

Unfortunately, not only do most applications get this wrong, most date/time libraries don't support both nominal-time invariance and Unviersal Time invariance, nor do they support both timestamps statically-bound to a specified time zone and also timestamps bound dynamically to the current system time zone.

Chronos does it all.


2006-02-02

Discovering the Local Time Zone--Why It's a Hard Problem

I've written a rather long essay that explores all the issues involved in having VisualWorks (and Squeak) synch up with the host operating system's time zone offset rules. It's too long for the blog, so here's the first 3 paragraphs, followed by a link to the full essay:

Smalltalk-80 was released to the world in 1983. I no longer remember exactly when, but well before 1990, ParcPlace systems (the company formed by Xerox to commercialize Smalltalk) introduced a Time Zone class (which had not been in the version given to Tektronix, HP, TI and Apple in 1980--which is why Squeak Smalltalk, which descends from the version Xerox gave Apple, didn't have a Time Zone class until just recently.) It's been roughly 20 years since Smalltalk first had a Time Zone class--well before any other language that I know of had one. And yet both VisualWorks and Squeak, the two modern descendants of Smalltalk-80, still can't configure their Time Zone objects so that the offset transition rules match those of the host operating system (although Squeak dodges the issue by having the VM report clock ticks in local time, an approach that gives the strong but deceptive illusion of "working" as long as one only ever deals with one time zone, and doesn't care about correct time computations into the past or the future.)

Why does this situation persist? Why doesn't Cincom (current ownwers of VisualWorks, which descends from the original ST-80 marketed by ParcPlace) just write a few lines of code so that when VisualWorks first starts up, it finds out what time zone it's in from the host operating environment? Why don't the Squeak folks do likewise? Is this a hard problem or something?

Well, yes. It is. Here's the story, in all its sordid details: Discovering the Local Time Zone--Why It's a Hard Problem.


2006-02-01

Martin Fowler talks about TimePoints

Martin Fowler:

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.

Right on! However, the correct technical term when speaking of time is resolution, not precision. Resolution is a matter of the capabilities of the syntax/notation used to represent a temporal value; precision is a matter of data quality. A clock that provides one-microsecond ticks has a resolution of one microsecond--but its precision may not be that high. [Which reminds me: I need to ask Doug Surber what he meant by "DateAndTime clockPrecision" when he put that method in the ANSI Smalltalk Standard's <DateAndTime factory> protocol. I've assumed he actually meant precision, and not resolution.]

Chronos provides YearMonthDay, whose resolution is one civil day, and Timepoint, whose resolution is one nanosecond.

YearMonthDay instances aren't bound to time zones--they have nominal time invariance. Timepoint instances can either be bound to a time zone and have Universal Time invariance, or they can have nominal time invariance and not be bound to any time zone.

As Martin points out, choosing the time-invariance semantics that is right for each situation is of no small importance:
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.


Also be wary of not using time zones when you do need them.

Martin also advises against using temporal intervals as TimePoints. He's absolutely correct. Squeak's Chronology library makes this mistake.

Many other date/time libraries use a model of time that assumes that TimePoints have infinite resolution--which is a different aspect of the same conceptual error (e.g., Joda-Time's concept of the "Instant".) But there is no such thing as infinite resolution--not even in Physics. Such mistakes lead to ugly code, awkward APIs, poor architecture--and perhaps even wrong results.



2006-01-30

DateAndTime Construction--And Nominal Time Invariance

At the cost of adding a few new (small) methods (in the latest development version of Chronos, B1.20) I've made it much easier to compose a DateAndTime by combining a TimeOfDay with a Date (or with an AnnualDate, or with a DateAndTime.) The new instance methods (of TemporalCoordinate and CalendricalCoordinate, respectively) are #on: and #atTimeOfDay: The new class methods (of Timepoint) are #todayAt: and #todayAt:in:.

Here are some example usages:


DateAndTime todayAt: TimeOfDay now
DateAndTime todayAt: TimeOfDay now in: (Duration hours: 5.5)
TimeOfDay now on: YearMonthDay today
TimeOfDay now on: Timepoint today
(TimeOfDay hour: 17 minute: 30 second: 0) on: (YearMonthDay year: 2006 month: June day: 14)
(TimeOfDay hour: 17 minute: 30 second: 0) on: (DateAndTime year: 2006 month: June day: 14)
(TimeOfDay hour: 9 minute: 20 second: 0) on: GregorianEaster canonical
YearMonthDay today atTimeOfDay: TimeOfDay now
DateAndTime today atTimeOfDay: TimeOfDay now
GregorianEaster canonical atTimeOfDay: (TimeOfDay hour: 12 minute: 0 second: 0)


Which produce the following results when evaluated:

2006-01-30T22:15:33.465363-08:00
2006-01-30T22:15:44.920133+05:30
2006-01-30T22:15:53.108186
2006-01-30T22:16:03.176193-08:00
2006-06-14T17:30:00
2006-06-14T17:30:00-07:00
2006-04-16T09:20:00
2006-01-30T22:16:58.624536
2006-01-30T22:17:12.008788-08:00
2006-04-16T12:00:00


You might notice that some of the Timepoints that result from the examples above show a time zone offset, and some don't. Those that show a time zone offset are invariant to Universal Time. Those that don't are invariant to nominal time. A VisualWorks Timestamp has nominal time invariance. A DateAndTime instance that conforms to the ANSI-Smalltalk Standard has Universal Time invariance. Instances of either java.util.Calendar or of java.util.Date also have Universal Time invariance.

If "(DateAndTime year: 2006 month: 1 day: 1 offset: 0 hours) = (DateAndTime year: 2006 month: 1 day: 1 hour: 8 minute: 0 second: 0 offset: 8 hours)" evaluates to true, then the two DateAndTime instances have Universal Time invariance. For Universal-Time invariant time values, the point in Universal Time they designate is their invariant for the purpose of defining their meaning (semantics)--such as whether or not they are equal to some other point-in-time value.

A time value that is invariant to Universal Time will still designate the same point in Universal Time when it is translated or converted to a different time zone.

A time value that is invariant to nominal time always designates the same nominal time regardless of time zone (in other words, it acts just like a VisualWorks Timestamp.) It uses the nominal time it designates as its semantic invariant. It compares as equal to any time value that designates the same nominal time. When bound to a time zone (converted into a point-in-time that is invariant to Universal Time) its local time in the time to which it has become bound is the same as its former nominal time (because its nominal time is its semantic invariant.)

If you create a Core.Timestamp (or a nominal-time invariant Chronos.Timepoint) in an image running in San Francisco, save the image, take it with you on a plane flight to Bangalore, then restart the image, that Core.Timestamp instance would still designate the same nominal time--because there is no "binding" between a nominal-time-invariant time value and any particular time zone.

I know that most of you have been trying to use Core.Timestamps as though they had Universal Time invariance--because that's usually what you need. But Core.Timestamps do not have Universal Time invariance--which is one of the reasons they sometimes don't work for you the way you'd like.

If you need to deal with time values from multiple time zones, you need point-in-time objects with Universal Time invariance. The ANSI-Smalltalk Standard requires them. Chronos provides them. The VisualWorks Core library does not.

Which is not to say that nominal time invariance doesn't have its uses. It certainly does. The canonical example is dates that are literal values in business rules (such as the rules that define which days are holidays.) And trying to use Universal Time invariant time values in situations where they are not appropriate can lead to even worse problems than using nominal time invariant time values when Universal Time invariance is that's needed. Just ask those who've had to use java.util.Calendar for certain use cases.

Note: The latest "relatively stable" development version of Chronos is always available from the Cincom Public StORE Repository.


2005-12-27

Why Chronos?

I thought it would be appropriate to start my Chronos blog by explaining my motivations for creating and publishing the Chronos Date/Time Library.

It all started back in 1984, when my friend Kenton Hoover encouraged me to purchase a book named Smalltalk: The Language And Its Implementation (affectionately now known as the "Blue Book.") When I first read it, I did not have access to an actual Smalltalk implementation, and so failed to get the complete experience and complete understanding. Nevertheless, the book had a profound impact on my computer programming and computer science world view.

About a year later, a technical support person wheeled a Tektronix 4404 Smalltalk workstation into my cubicle at Bank America (in an office building on Spear Street in San Francisco.) Over the next few months, I came to fully understand the terms 'class,' 'object,' 'instance,' 'message,' 'method,' 'metaclass' and 'block,' in a way that transcended the level of understanding I had achieved from just reading the Blue Book. The mental transformation involved is not unlike the one that must happen in order for someone with an Aristotelian weltanschauung to grok Newton. [Most programmers have yet to benefit from an equivalent enlightenment, including many who think they already have. But more on that at some other time.]

Smalltalk was so far ahead of anything else available at the time, that fully experiencing and grokking it during the mid 1980's can best be compared to taking a ride in a time machine to a world decades in the future. But instead of enumerating all the advantages of Smalltalk compared to its mid-1980's competition, let me focus on just one small facet of the overall picture: Dates and Times (which are, after all, the subject of both this post and this blog.)

For most programming languages in 1985, the handling of dates and times was left to application coders to deal with as best they were able (ahem.) Which is not to say that there weren't reusable date/time libraries available for some of the major languages. For example, the <time.h> module of what is now the ANSI Standard C library came from code donated to 4.3BSD Unix by Arthur David Olson in 1986. The <time.h> module was incorporated into ANSI C in 1989.

By the standards of the day, <time.h> was in many ways rather superior to anything else that was commonly available. For one thing, it actually dealt with time zones in a reasonable way. For another, its internal representation of time was a count of seconds since an epoch, specified in Universal Time (which is often incorrectly referred to as either GMT or UTC—but more about that at some other time.) In this, C's <time.h> module was superior to Smalltalk's Date and Time classes. But in 1985 and early 1986, I didn't know any of that. In fact, I didn't even learn C until 1988, and didn't pay much attention to <time.h> until much later.

Unlike any other language with which I was familiar at the time, Smalltalk actually had dates and times as coherent, first-class values. You could assign them to variables, pass them as arguments, and return them as the result of a message send. You didn't need to know or care anything about the internal representation of a Date or Time object. Because of the way Smalltalk works, and because the Date and Time classes were included as part of the "standard class library," it was the equivalent of having Date and Time as "built in" data types in Pascal, Modula-2 or Ada.

The fact that "types" (classes) implemented by the programmer can have the same "look and feel" as "built-in" types is of course one of the major benefits of Smalltalk compared to most of its competition. Dates and Times are one of the more compelling concrete, non-theoretical examples of why that matters. In 1985, the idea of having a Date type and a Time type was a revolutionary advance.

From my 1985 perspective, Smalltalk started out in a situation where its support of Dates and Times was second to none. In 2004, when I started work on what is now the Chronos Date/Time Library, that was no longer the case. Chronos is my attempt to restore Smalltalk to the same position with respect to date/time handling that it enjoyed back in 1985 (as I saw it then.) I want the programmers of today to experience the same feeling of delight about Smalltalk's date/time capabibilities that I felt back in 1985. And I also hope to inspire others to make similar contributions to Smalltalk (and/or its deserving successors) in other domains.

Additionally, I want to encourage the various Smalltalk implementations to grow together, and not apart. Smalltalk needs a much wider and deeper "standard library" if it is going to survive. I hope that Chronos will be a significant contribution towards that goal.