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 educational. Show all posts
Showing posts with label educational. Show all posts

2007-03-20

Which country has the most time zones?

I recently noticed quite a few hits on my website from people who wanted to know which country has the most time zones. I assume this was a school assignment of some sort.

The answer depends on the definition of "time zone." If a "time zone" is defined as simply an offset from Universal Time, then the answer is Russia (which has 11 different time zone offsets.) If a "time zone" is defined as "a location where the rules that determine the offset from moment to moment have been the same since at least 1970 or earlier" (which is the definition used by the Olson Time Zone Database, and is the one usually meant,) then the answer is the United States (which has 25 such time zones.)

Most countries only have one time zone, using either definition. Using the Olson definition, the only countries with more than one time zone are the United States, Russia, Canada, Australia, Greenland, Argentina, Brazil, Chile, Ecuador, Paraguay, Spain, Congo (Democratic Republic,) Mexico, Ukraine, Kazakhstan, Uzbekistan, Mongolia, China, Malaysia, Indonesia, New Zealand, French Polynesia, Micronesia and Kiribati.


2007-02-07

When's the next (or previous) DST offset transition?

How can you tell programmatically when the next (or previous) DST (daylight saving time) offset transition will occur (has occurred)--other than by using a brute-force approach where one checks the offset of successive points in time, each one second later than the previous one? Few date/time libraries provide any support for such functionality (and many don't even provide support for real-world time zones where offsets change during the year according to a set of rules.)

Although it's possible to use Chronos to discover all the moments when a time zone's offset, name or abbreviation changes (and not just by the brute-force method mentioned above,) the bad news is that for a developer who casually uses Chronos to figure out how to code that functionality would probably require not only a bit of research, but also the writing of some non-trivial amount of new code.

So, the next release of Chronos (which is not yet available) will provide a simple, easy-to-use API for enumerating through all the moments when a time zone's offset changes, so that developers who use Chronos won't have to do as much research, and won't have to write anywhere near as much new new code, in order to find the next (or previous) offset transition for a time zone. Using this new API, the following list of every offset transition in the America/Los_Angeles time zone, from the very first one in 1883 until the arbitrarily chosen end-year of 2050, can easily be generated:

1883-11-18T12:00:00-08:00  [1883-11-18T20:00:00Z] -07:52:58 -> -08:00  {-2717640000}
1918-03-31T03:00:00-07:00 [1918-03-31T10:00:00Z] -08:00 -> -07:00 {-1633269600}
1918-10-27T01:00:00-08:00 [1918-10-27T09:00:00Z] -07:00 -> -08:00 {-1615129200}
1919-03-30T03:00:00-07:00 [1919-03-30T10:00:00Z] -08:00 -> -07:00 {-1601820000}
1919-10-26T01:00:00-08:00 [1919-10-26T09:00:00Z] -07:00 -> -08:00 {-1583679600}
1942-02-09T03:00:00-07:00 [1942-02-09T10:00:00Z] -08:00 -> -07:00 {-880207200}
1945-08-14T16:00:00-07:00 [1945-08-14T23:00:00Z] -07:00 -> -07:00 {-769395600}
1945-09-30T01:00:00-08:00 [1945-09-30T09:00:00Z] -07:00 -> -08:00 {-765385200}
1948-03-14T03:00:00-07:00 [1948-03-14T10:00:00Z] -08:00 -> -07:00 {-687967200}
1949-01-01T01:00:00-08:00 [1949-01-01T09:00:00Z] -07:00 -> -08:00 {-662655600}
1950-04-30T03:00:00-07:00 [1950-04-30T10:00:00Z] -08:00 -> -07:00 {-620834400}
1950-09-24T01:00:00-08:00 [1950-09-24T09:00:00Z] -07:00 -> -08:00 {-608137200}
1951-04-29T03:00:00-07:00 [1951-04-29T10:00:00Z] -08:00 -> -07:00 {-589384800}
1951-09-30T01:00:00-08:00 [1951-09-30T09:00:00Z] -07:00 -> -08:00 {-576082800}
1952-04-27T03:00:00-07:00 [1952-04-27T10:00:00Z] -08:00 -> -07:00 {-557935200}
1952-09-28T01:00:00-08:00 [1952-09-28T09:00:00Z] -07:00 -> -08:00 {-544633200}
1953-04-26T03:00:00-07:00 [1953-04-26T10:00:00Z] -08:00 -> -07:00 {-526485600}
1953-09-27T01:00:00-08:00 [1953-09-27T09:00:00Z] -07:00 -> -08:00 {-513183600}
1954-04-25T03:00:00-07:00 [1954-04-25T10:00:00Z] -08:00 -> -07:00 {-495036000}
1954-09-26T01:00:00-08:00 [1954-09-26T09:00:00Z] -07:00 -> -08:00 {-481734000}
1955-04-24T03:00:00-07:00 [1955-04-24T10:00:00Z] -08:00 -> -07:00 {-463586400}
1955-09-25T01:00:00-08:00 [1955-09-25T09:00:00Z] -07:00 -> -08:00 {-450284400}
1956-04-29T03:00:00-07:00 [1956-04-29T10:00:00Z] -08:00 -> -07:00 {-431532000}
1956-09-30T01:00:00-08:00 [1956-09-30T09:00:00Z] -07:00 -> -08:00 {-418230000}
1957-04-28T03:00:00-07:00 [1957-04-28T10:00:00Z] -08:00 -> -07:00 {-400082400}
1957-09-29T01:00:00-08:00 [1957-09-29T09:00:00Z] -07:00 -> -08:00 {-386780400}
1958-04-27T03:00:00-07:00 [1958-04-27T10:00:00Z] -08:00 -> -07:00 {-368632800}
1958-09-28T01:00:00-08:00 [1958-09-28T09:00:00Z] -07:00 -> -08:00 {-355330800}
1959-04-26T03:00:00-07:00 [1959-04-26T10:00:00Z] -08:00 -> -07:00 {-337183200}
1959-09-27T01:00:00-08:00 [1959-09-27T09:00:00Z] -07:00 -> -08:00 {-323881200}
1960-04-24T03:00:00-07:00 [1960-04-24T10:00:00Z] -08:00 -> -07:00 {-305733600}
1960-09-25T01:00:00-08:00 [1960-09-25T09:00:00Z] -07:00 -> -08:00 {-292431600}
1961-04-30T03:00:00-07:00 [1961-04-30T10:00:00Z] -08:00 -> -07:00 {-273679200}
1961-09-24T01:00:00-08:00 [1961-09-24T09:00:00Z] -07:00 -> -08:00 {-260982000}
1962-04-29T03:00:00-07:00 [1962-04-29T10:00:00Z] -08:00 -> -07:00 {-242229600}
1962-10-28T01:00:00-08:00 [1962-10-28T09:00:00Z] -07:00 -> -08:00 {-226508400}
1963-04-28T03:00:00-07:00 [1963-04-28T10:00:00Z] -08:00 -> -07:00 {-210780000}
1963-10-27T01:00:00-08:00 [1963-10-27T09:00:00Z] -07:00 -> -08:00 {-195058800}
1964-04-26T03:00:00-07:00 [1964-04-26T10:00:00Z] -08:00 -> -07:00 {-179330400}
1964-10-25T01:00:00-08:00 [1964-10-25T09:00:00Z] -07:00 -> -08:00 {-163609200}
1965-04-25T03:00:00-07:00 [1965-04-25T10:00:00Z] -08:00 -> -07:00 {-147880800}
1965-10-31T01:00:00-08:00 [1965-10-31T09:00:00Z] -07:00 -> -08:00 {-131554800}
1966-04-24T03:00:00-07:00 [1966-04-24T10:00:00Z] -08:00 -> -07:00 {-116431200}
1966-10-30T01:00:00-08:00 [1966-10-30T09:00:00Z] -07:00 -> -08:00 {-100105200}
1967-04-30T03:00:00-07:00 [1967-04-30T10:00:00Z] -08:00 -> -07:00 {-84376800}
1967-10-29T01:00:00-08:00 [1967-10-29T09:00:00Z] -07:00 -> -08:00 {-68655600}
1968-04-28T03:00:00-07:00 [1968-04-28T10:00:00Z] -08:00 -> -07:00 {-52927200}
1968-10-27T01:00:00-08:00 [1968-10-27T09:00:00Z] -07:00 -> -08:00 {-37206000}
1969-04-27T03:00:00-07:00 [1969-04-27T10:00:00Z] -08:00 -> -07:00 {-21477600}
1969-10-26T01:00:00-08:00 [1969-10-26T09:00:00Z] -07:00 -> -08:00 {-5756400}
1970-04-26T03:00:00-07:00 [1970-04-26T10:00:00Z] -08:00 -> -07:00 {9972000}
1970-10-25T01:00:00-08:00 [1970-10-25T09:00:00Z] -07:00 -> -08:00 {25693200}
1971-04-25T03:00:00-07:00 [1971-04-25T10:00:00Z] -08:00 -> -07:00 {41421600}
1971-10-31T01:00:00-08:00 [1971-10-31T09:00:00Z] -07:00 -> -08:00 {57747600}
1972-04-30T03:00:00-07:00 [1972-04-30T10:00:00Z] -08:00 -> -07:00 {73476000}
1972-10-29T01:00:00-08:00 [1972-10-29T09:00:00Z] -07:00 -> -08:00 {89197200}
1973-04-29T03:00:00-07:00 [1973-04-29T10:00:00Z] -08:00 -> -07:00 {104925600}
1973-10-28T01:00:00-08:00 [1973-10-28T09:00:00Z] -07:00 -> -08:00 {120646800}
1974-01-06T03:00:00-07:00 [1974-01-06T10:00:00Z] -08:00 -> -07:00 {126698400}
1974-10-27T01:00:00-08:00 [1974-10-27T09:00:00Z] -07:00 -> -08:00 {152096400}
1975-02-23T03:00:00-07:00 [1975-02-23T10:00:00Z] -08:00 -> -07:00 {162381600}
1975-10-26T01:00:00-08:00 [1975-10-26T09:00:00Z] -07:00 -> -08:00 {183546000}
1976-04-25T03:00:00-07:00 [1976-04-25T10:00:00Z] -08:00 -> -07:00 {199274400}
1976-10-31T01:00:00-08:00 [1976-10-31T09:00:00Z] -07:00 -> -08:00 {215600400}
1977-04-24T03:00:00-07:00 [1977-04-24T10:00:00Z] -08:00 -> -07:00 {230724000}
1977-10-30T01:00:00-08:00 [1977-10-30T09:00:00Z] -07:00 -> -08:00 {247050000}
1978-04-30T03:00:00-07:00 [1978-04-30T10:00:00Z] -08:00 -> -07:00 {262778400}
1978-10-29T01:00:00-08:00 [1978-10-29T09:00:00Z] -07:00 -> -08:00 {278499600}
1979-04-29T03:00:00-07:00 [1979-04-29T10:00:00Z] -08:00 -> -07:00 {294228000}
1979-10-28T01:00:00-08:00 [1979-10-28T09:00:00Z] -07:00 -> -08:00 {309949200}
1980-04-27T03:00:00-07:00 [1980-04-27T10:00:00Z] -08:00 -> -07:00 {325677600}
1980-10-26T01:00:00-08:00 [1980-10-26T09:00:00Z] -07:00 -> -08:00 {341398800}
1981-04-26T03:00:00-07:00 [1981-04-26T10:00:00Z] -08:00 -> -07:00 {357127200}
1981-10-25T01:00:00-08:00 [1981-10-25T09:00:00Z] -07:00 -> -08:00 {372848400}
1982-04-25T03:00:00-07:00 [1982-04-25T10:00:00Z] -08:00 -> -07:00 {388576800}
1982-10-31T01:00:00-08:00 [1982-10-31T09:00:00Z] -07:00 -> -08:00 {404902800}
1983-04-24T03:00:00-07:00 [1983-04-24T10:00:00Z] -08:00 -> -07:00 {420026400}
1983-10-30T01:00:00-08:00 [1983-10-30T09:00:00Z] -07:00 -> -08:00 {436352400}
1984-04-29T03:00:00-07:00 [1984-04-29T10:00:00Z] -08:00 -> -07:00 {452080800}
1984-10-28T01:00:00-08:00 [1984-10-28T09:00:00Z] -07:00 -> -08:00 {467802000}
1985-04-28T03:00:00-07:00 [1985-04-28T10:00:00Z] -08:00 -> -07:00 {483530400}
1985-10-27T01:00:00-08:00 [1985-10-27T09:00:00Z] -07:00 -> -08:00 {499251600}
1986-04-27T03:00:00-07:00 [1986-04-27T10:00:00Z] -08:00 -> -07:00 {514980000}
1986-10-26T01:00:00-08:00 [1986-10-26T09:00:00Z] -07:00 -> -08:00 {530701200}
1987-04-05T03:00:00-07:00 [1987-04-05T10:00:00Z] -08:00 -> -07:00 {544615200}
1987-10-25T01:00:00-08:00 [1987-10-25T09:00:00Z] -07:00 -> -08:00 {562150800}
1988-04-03T03:00:00-07:00 [1988-04-03T10:00:00Z] -08:00 -> -07:00 {576064800}
1988-10-30T01:00:00-08:00 [1988-10-30T09:00:00Z] -07:00 -> -08:00 {594205200}
1989-04-02T03:00:00-07:00 [1989-04-02T10:00:00Z] -08:00 -> -07:00 {607514400}
1989-10-29T01:00:00-08:00 [1989-10-29T09:00:00Z] -07:00 -> -08:00 {625654800}
1990-04-01T03:00:00-07:00 [1990-04-01T10:00:00Z] -08:00 -> -07:00 {638964000}
1990-10-28T01:00:00-08:00 [1990-10-28T09:00:00Z] -07:00 -> -08:00 {657104400}
1991-04-07T03:00:00-07:00 [1991-04-07T10:00:00Z] -08:00 -> -07:00 {671018400}
1991-10-27T01:00:00-08:00 [1991-10-27T09:00:00Z] -07:00 -> -08:00 {688554000}
1992-04-05T03:00:00-07:00 [1992-04-05T10:00:00Z] -08:00 -> -07:00 {702468000}
1992-10-25T01:00:00-08:00 [1992-10-25T09:00:00Z] -07:00 -> -08:00 {720003600}
1993-04-04T03:00:00-07:00 [1993-04-04T10:00:00Z] -08:00 -> -07:00 {733917600}
1993-10-31T01:00:00-08:00 [1993-10-31T09:00:00Z] -07:00 -> -08:00 {752058000}
1994-04-03T03:00:00-07:00 [1994-04-03T10:00:00Z] -08:00 -> -07:00 {765367200}
1994-10-30T01:00:00-08:00 [1994-10-30T09:00:00Z] -07:00 -> -08:00 {783507600}
1995-04-02T03:00:00-07:00 [1995-04-02T10:00:00Z] -08:00 -> -07:00 {796816800}
1995-10-29T01:00:00-08:00 [1995-10-29T09:00:00Z] -07:00 -> -08:00 {814957200}
1996-04-07T03:00:00-07:00 [1996-04-07T10:00:00Z] -08:00 -> -07:00 {828871200}
1996-10-27T01:00:00-08:00 [1996-10-27T09:00:00Z] -07:00 -> -08:00 {846406800}
1997-04-06T03:00:00-07:00 [1997-04-06T10:00:00Z] -08:00 -> -07:00 {860320800}
1997-10-26T01:00:00-08:00 [1997-10-26T09:00:00Z] -07:00 -> -08:00 {877856400}
1998-04-05T03:00:00-07:00 [1998-04-05T10:00:00Z] -08:00 -> -07:00 {891770400}
1998-10-25T01:00:00-08:00 [1998-10-25T09:00:00Z] -07:00 -> -08:00 {909306000}
1999-04-04T03:00:00-07:00 [1999-04-04T10:00:00Z] -08:00 -> -07:00 {923220000}
1999-10-31T01:00:00-08:00 [1999-10-31T09:00:00Z] -07:00 -> -08:00 {941360400}
2000-04-02T03:00:00-07:00 [2000-04-02T10:00:00Z] -08:00 -> -07:00 {954669600}
2000-10-29T01:00:00-08:00 [2000-10-29T09:00:00Z] -07:00 -> -08:00 {972810000}
2001-04-01T03:00:00-07:00 [2001-04-01T10:00:00Z] -08:00 -> -07:00 {986119200}
2001-10-28T01:00:00-08:00 [2001-10-28T09:00:00Z] -07:00 -> -08:00 {1004259600}
2002-04-07T03:00:00-07:00 [2002-04-07T10:00:00Z] -08:00 -> -07:00 {1018173600}
2002-10-27T01:00:00-08:00 [2002-10-27T09:00:00Z] -07:00 -> -08:00 {1035709200}
2003-04-06T03:00:00-07:00 [2003-04-06T10:00:00Z] -08:00 -> -07:00 {1049623200}
2003-10-26T01:00:00-08:00 [2003-10-26T09:00:00Z] -07:00 -> -08:00 {1067158800}
2004-04-04T03:00:00-07:00 [2004-04-04T10:00:00Z] -08:00 -> -07:00 {1081072800}
2004-10-31T01:00:00-08:00 [2004-10-31T09:00:00Z] -07:00 -> -08:00 {1099213200}
2005-04-03T03:00:00-07:00 [2005-04-03T10:00:00Z] -08:00 -> -07:00 {1112522400}
2005-10-30T01:00:00-08:00 [2005-10-30T09:00:00Z] -07:00 -> -08:00 {1130662800}
2006-04-02T03:00:00-07:00 [2006-04-02T10:00:00Z] -08:00 -> -07:00 {1143972000}
2006-10-29T01:00:00-08:00 [2006-10-29T09:00:00Z] -07:00 -> -08:00 {1162112400}
2007-03-11T03:00:00-07:00 [2007-03-11T10:00:00Z] -08:00 -> -07:00 {1173607200}
2007-11-04T01:00:00-08:00 [2007-11-04T09:00:00Z] -07:00 -> -08:00 {1194166800}
2008-03-09T03:00:00-07:00 [2008-03-09T10:00:00Z] -08:00 -> -07:00 {1205056800}
2008-11-02T01:00:00-08:00 [2008-11-02T09:00:00Z] -07:00 -> -08:00 {1225616400}
2009-03-08T03:00:00-07:00 [2009-03-08T10:00:00Z] -08:00 -> -07:00 {1236506400}
2009-11-01T01:00:00-08:00 [2009-11-01T09:00:00Z] -07:00 -> -08:00 {1257066000}
2010-03-14T03:00:00-07:00 [2010-03-14T10:00:00Z] -08:00 -> -07:00 {1268560800}
2010-11-07T01:00:00-08:00 [2010-11-07T09:00:00Z] -07:00 -> -08:00 {1289120400}
2011-03-13T03:00:00-07:00 [2011-03-13T10:00:00Z] -08:00 -> -07:00 {1300010400}
2011-11-06T01:00:00-08:00 [2011-11-06T09:00:00Z] -07:00 -> -08:00 {1320570000}
2012-03-11T03:00:00-07:00 [2012-03-11T10:00:00Z] -08:00 -> -07:00 {1331460000}
2012-11-04T01:00:00-08:00 [2012-11-04T09:00:00Z] -07:00 -> -08:00 {1352019600}
2013-03-10T03:00:00-07:00 [2013-03-10T10:00:00Z] -08:00 -> -07:00 {1362909600}
2013-11-03T01:00:00-08:00 [2013-11-03T09:00:00Z] -07:00 -> -08:00 {1383469200}
2014-03-09T03:00:00-07:00 [2014-03-09T10:00:00Z] -08:00 -> -07:00 {1394359200}
2014-11-02T01:00:00-08:00 [2014-11-02T09:00:00Z] -07:00 -> -08:00 {1414918800}
2015-03-08T03:00:00-07:00 [2015-03-08T10:00:00Z] -08:00 -> -07:00 {1425808800}
2015-11-01T01:00:00-08:00 [2015-11-01T09:00:00Z] -07:00 -> -08:00 {1446368400}
2016-03-13T03:00:00-07:00 [2016-03-13T10:00:00Z] -08:00 -> -07:00 {1457863200}
2016-11-06T01:00:00-08:00 [2016-11-06T09:00:00Z] -07:00 -> -08:00 {1478422800}
2017-03-12T03:00:00-07:00 [2017-03-12T10:00:00Z] -08:00 -> -07:00 {1489312800}
2017-11-05T01:00:00-08:00 [2017-11-05T09:00:00Z] -07:00 -> -08:00 {1509872400}
2018-03-11T03:00:00-07:00 [2018-03-11T10:00:00Z] -08:00 -> -07:00 {1520762400}
2018-11-04T01:00:00-08:00 [2018-11-04T09:00:00Z] -07:00 -> -08:00 {1541322000}
2019-03-10T03:00:00-07:00 [2019-03-10T10:00:00Z] -08:00 -> -07:00 {1552212000}
2019-11-03T01:00:00-08:00 [2019-11-03T09:00:00Z] -07:00 -> -08:00 {1572771600}
2020-03-08T03:00:00-07:00 [2020-03-08T10:00:00Z] -08:00 -> -07:00 {1583661600}
2020-11-01T01:00:00-08:00 [2020-11-01T09:00:00Z] -07:00 -> -08:00 {1604221200}
2021-03-14T03:00:00-07:00 [2021-03-14T10:00:00Z] -08:00 -> -07:00 {1615716000}
2021-11-07T01:00:00-08:00 [2021-11-07T09:00:00Z] -07:00 -> -08:00 {1636275600}
2022-03-13T03:00:00-07:00 [2022-03-13T10:00:00Z] -08:00 -> -07:00 {1647165600}
2022-11-06T01:00:00-08:00 [2022-11-06T09:00:00Z] -07:00 -> -08:00 {1667725200}
2023-03-12T03:00:00-07:00 [2023-03-12T10:00:00Z] -08:00 -> -07:00 {1678615200}
2023-11-05T01:00:00-08:00 [2023-11-05T09:00:00Z] -07:00 -> -08:00 {1699174800}
2024-03-10T03:00:00-07:00 [2024-03-10T10:00:00Z] -08:00 -> -07:00 {1710064800}
2024-11-03T01:00:00-08:00 [2024-11-03T09:00:00Z] -07:00 -> -08:00 {1730624400}
2025-03-09T03:00:00-07:00 [2025-03-09T10:00:00Z] -08:00 -> -07:00 {1741514400}
2025-11-02T01:00:00-08:00 [2025-11-02T09:00:00Z] -07:00 -> -08:00 {1762074000}
2026-03-08T03:00:00-07:00 [2026-03-08T10:00:00Z] -08:00 -> -07:00 {1772964000}
2026-11-01T01:00:00-08:00 [2026-11-01T09:00:00Z] -07:00 -> -08:00 {1793523600}
2027-03-14T03:00:00-07:00 [2027-03-14T10:00:00Z] -08:00 -> -07:00 {1805018400}
2027-11-07T01:00:00-08:00 [2027-11-07T09:00:00Z] -07:00 -> -08:00 {1825578000}
2028-03-12T03:00:00-07:00 [2028-03-12T10:00:00Z] -08:00 -> -07:00 {1836468000}
2028-11-05T01:00:00-08:00 [2028-11-05T09:00:00Z] -07:00 -> -08:00 {1857027600}
2029-03-11T03:00:00-07:00 [2029-03-11T10:00:00Z] -08:00 -> -07:00 {1867917600}
2029-11-04T01:00:00-08:00 [2029-11-04T09:00:00Z] -07:00 -> -08:00 {1888477200}
2030-03-10T03:00:00-07:00 [2030-03-10T10:00:00Z] -08:00 -> -07:00 {1899367200}
2030-11-03T01:00:00-08:00 [2030-11-03T09:00:00Z] -07:00 -> -08:00 {1919926800}
2031-03-09T03:00:00-07:00 [2031-03-09T10:00:00Z] -08:00 -> -07:00 {1930816800}
2031-11-02T01:00:00-08:00 [2031-11-02T09:00:00Z] -07:00 -> -08:00 {1951376400}
2032-03-14T03:00:00-07:00 [2032-03-14T10:00:00Z] -08:00 -> -07:00 {1962871200}
2032-11-07T01:00:00-08:00 [2032-11-07T09:00:00Z] -07:00 -> -08:00 {1983430800}
2033-03-13T03:00:00-07:00 [2033-03-13T10:00:00Z] -08:00 -> -07:00 {1994320800}
2033-11-06T01:00:00-08:00 [2033-11-06T09:00:00Z] -07:00 -> -08:00 {2014880400}
2034-03-12T03:00:00-07:00 [2034-03-12T10:00:00Z] -08:00 -> -07:00 {2025770400}
2034-11-05T01:00:00-08:00 [2034-11-05T09:00:00Z] -07:00 -> -08:00 {2046330000}
2035-03-11T03:00:00-07:00 [2035-03-11T10:00:00Z] -08:00 -> -07:00 {2057220000}
2035-11-04T01:00:00-08:00 [2035-11-04T09:00:00Z] -07:00 -> -08:00 {2077779600}
2036-03-09T03:00:00-07:00 [2036-03-09T10:00:00Z] -08:00 -> -07:00 {2088669600}
2036-11-02T01:00:00-08:00 [2036-11-02T09:00:00Z] -07:00 -> -08:00 {2109229200}
2037-03-08T03:00:00-07:00 [2037-03-08T10:00:00Z] -08:00 -> -07:00 {2120119200}
2037-11-01T01:00:00-08:00 [2037-11-01T09:00:00Z] -07:00 -> -08:00 {2140678800}
2038-03-14T03:00:00-07:00 [2038-03-14T10:00:00Z] -08:00 -> -07:00 {2152173600}
2038-11-07T01:00:00-08:00 [2038-11-07T09:00:00Z] -07:00 -> -08:00 {2172733200}
2039-03-13T03:00:00-07:00 [2039-03-13T10:00:00Z] -08:00 -> -07:00 {2183623200}
2039-11-06T01:00:00-08:00 [2039-11-06T09:00:00Z] -07:00 -> -08:00 {2204182800}
2040-03-11T03:00:00-07:00 [2040-03-11T10:00:00Z] -08:00 -> -07:00 {2215072800}
2040-11-04T01:00:00-08:00 [2040-11-04T09:00:00Z] -07:00 -> -08:00 {2235632400}
2041-03-10T03:00:00-07:00 [2041-03-10T10:00:00Z] -08:00 -> -07:00 {2246522400}
2041-11-03T01:00:00-08:00 [2041-11-03T09:00:00Z] -07:00 -> -08:00 {2267082000}
2042-03-09T03:00:00-07:00 [2042-03-09T10:00:00Z] -08:00 -> -07:00 {2277972000}
2042-11-02T01:00:00-08:00 [2042-11-02T09:00:00Z] -07:00 -> -08:00 {2298531600}
2043-03-08T03:00:00-07:00 [2043-03-08T10:00:00Z] -08:00 -> -07:00 {2309421600}
2043-11-01T01:00:00-08:00 [2043-11-01T09:00:00Z] -07:00 -> -08:00 {2329981200}
2044-03-13T03:00:00-07:00 [2044-03-13T10:00:00Z] -08:00 -> -07:00 {2341476000}
2044-11-06T01:00:00-08:00 [2044-11-06T09:00:00Z] -07:00 -> -08:00 {2362035600}
2045-03-12T03:00:00-07:00 [2045-03-12T10:00:00Z] -08:00 -> -07:00 {2372925600}
2045-11-05T01:00:00-08:00 [2045-11-05T09:00:00Z] -07:00 -> -08:00 {2393485200}
2046-03-11T03:00:00-07:00 [2046-03-11T10:00:00Z] -08:00 -> -07:00 {2404375200}
2046-11-04T01:00:00-08:00 [2046-11-04T09:00:00Z] -07:00 -> -08:00 {2424934800}
2047-03-10T03:00:00-07:00 [2047-03-10T10:00:00Z] -08:00 -> -07:00 {2435824800}
2047-11-03T01:00:00-08:00 [2047-11-03T09:00:00Z] -07:00 -> -08:00 {2456384400}
2048-03-08T03:00:00-07:00 [2048-03-08T10:00:00Z] -08:00 -> -07:00 {2467274400}
2048-11-01T01:00:00-08:00 [2048-11-01T09:00:00Z] -07:00 -> -08:00 {2487834000}
2049-03-14T03:00:00-07:00 [2049-03-14T10:00:00Z] -08:00 -> -07:00 {2499328800}
2049-11-07T01:00:00-08:00 [2049-11-07T09:00:00Z] -07:00 -> -08:00 {2519888400}
2050-03-13T03:00:00-07:00 [2050-03-13T10:00:00Z] -08:00 -> -07:00 {2530778400}
2050-11-06T01:00:00-08:00 [2050-11-06T09:00:00Z] -07:00 -> -08:00 {2551338000}

Here's the Chronos code that generates the list of offset transitions:
  | transition prevOffset printPolicy |
printPolicy := ChronosPrintPolicy iso8601.
transition := Timepoint year: 1881 day: 1 timeZone: #'America/Los_Angeles'.
[prevOffset := transition offset.
transition := transition nextOffsetTransitionDateAndTimeIfNone: [].
transition == nil or: [transition year > 2050]]
whileFalse:
[Transcript
cr; show: transition printString;
tab; show: ' ['; show: transition asUT % printPolicy; show: ']';
tab; show: prevOffset timeZoneOffsetPrintString;
show: ' -> '; show: transition offset timeZoneOffsetPrintString;
tab; show: ' {';
show: (transition secondsSince: ChronosSystemClock unixEpoch) printString;
show: '}'].



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-31

Filenames and Inter-Smalltalk Portability

Troy Brumley, in his blog post File access demo from VW to Squeak (2006-06-03), picks up on some example VisualWorks code posted by James Robertson a day earlier (Simple File I/O Demo, 2006-06-02.) Troy's examples show how to code the same thing in Squeak.

But there is another option: Use the Passport inter-Smalltalk portability library that was implemented in order to make it possible to host The Chronos Date/Time Library on VisualWorks, Squeak and Dolphin. Using Passport, the code will work unchanged in VisualWorks, Squeak and Dolphin.

Here's the Passport version (with Jim's original variable names unchanged, even though they aren't the best names for use with Passport):

"write a simple file"
file := 'myTestFile.txt' asResourcePath.
stream := file writeStream.
stream nextPutAll: 'Line 1'.
stream nextPut: Character cr.
stream nextPutAll: 'Line 2'.
stream nextPut: Character cr.
stream nextPutAll: 'Line 3'.
stream nextPut: Character cr.
stream close.

"read the entire file"
file := 'myTestFile.txt' asResourcePath.
contents := file value.
^contents.

"read line by line"
file := 'myTestFile.txt' asResourcePath.
stream := file readStream.
lines := OrderedCollection new.
[stream atEnd]
whileFalse: [| line |
line := UtilityFunction nextLineFrom: stream.
lines add: line].
stream close.
^lines.

"finding the file"
directory := '.' asResourcePath.
files := OrderedCollection new.
directory contentsDo: [:resourcePath | ('myTest*' match: resourcePath suffixString) ifTrue: [files add: resourcePath]].
^files.

(To see Jim's original VW code, follow this link: Simple File I/O Demo (VW code))

Passport not only makes it possible to write the same file name logic portably among different Smalltalk versions, it also makes it possible to use the same code regardless of whether the host operating system is Unix, MacOS X or Windows. But to do that, there are some constraints:

Firstly, either you can't use absolute pathnames, or else the absolute path must be constructed at run time by appending a well-known path suffix onto a path prefix that is supplied as a parameter at run time (and which will necessarily differ from one host OS to another, and often also even between different computers, even when they all use the same OS.)

Secondly, the relative path suffix must be constructed using a Pathname, as in the following example:
| resourcePath stream |
resourcePath := (Pathname fromString: 'test/folder1') asResourcePath.
resourcePath makeDirectory.
resourcePath := resourcePath, 'junk.txt'.
"The ResoucePath is now [./test/folder1/junk.txt]"
stream := resourcePath newWriteStream.
stream nextPutAll: 'This is a test.'.
stream close.


Typically, one would construct a ResourcePath using a path prefix obtained from your application's configuration parameters, using a well-known (or deterministically computable) path suffix. For example, the Chronos Date/Time Library might construct the path to a Chronos time zone ruleset file as follows:

(ResourcePath
namespace:
(EnvironmentFacade current
valueOfEnvironmentVariableAt: 'CHRONOS_PATH')
pathname:
(Pathname
fromString: 'time-zones/rulesets/Pacific/Honolulu'))
appendingExtension: 'tzn'

On the Windows machine I use for Chronos development, doing a "print it" on the above example would have the following result: H:\Dev\Smalltalk\Chronos\Chronos Versions\VisualWorks\Chronos\time-zones\rulesets\Pacific\Honolulu.tzn.

The Squeak version of Passport is available as two of the files in the version of Chronos.zip distributed for Squeak (namely, "Passport-Kernel.st" and "Passport-Squeak.st.") It's also available from SqueakSource (in the Chronos project.)

The VisualWorks version can be obtained from the Cincom Public StORE. It can also be filed out or parcelled out after installing the VisualWorks version of Chronos (it's in the packages Passport-Kernel and Passport-Kernel-VW.)

The Dolphin version of Passport is available on request (requests (at) chronos-st (dot) org).


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-09-18

Julian Day 2,454,000

At noon of 21 September 2006 begins Julian Day Number 2,454,000. A day and a half later, the Autumnal Equinox will occur (2006-09-23T04:03 Universal Time.)

The Hebrew New Year starts at sundown on 22 September (a.k.a Rosh Hoshana.) Note that the first month of the Hebrew year is traditionally known as the seventh month, not the first month.

Julian Day Numbers are an integer count of days since a specific epoch date. Julian Day Zero begins at Noon on 14 November 4714 BC, according to the proleptic Gregorian Calendar--or at Noon on 1 January 4713 BC, according to the proleptic Julian Calendar. Julian Day Zero was a Monday.

A Julian Date is a count of days, including any fractional part of the day, since -4713-11-24T12:00:00+0000 (24 Nov -4713 12:00:00 Universal Time, using Astronomical year numbering, where the year prior to the year 0001 is the year 0000, and not the year 1 BC, as would be traditional.)

It is common, but nevertheless technically incorrect, to refer to an ordinal date (Year-DayOfYear) as a "Julian Date."

Using a Julian Day Number to specify a date, or a Julian Date to specify a precise point in time, is useful for two reasons:

1) It permits dates to specified without reference to any particular calendrical system; and

2) It vastly simplifies astronomical calculations, and other computations where the amount of time between two dates needs to be computed.

The Julian Day system of specifying dates was invented by the astronomer Joseph Scaliger in 1583 (the year after the Gregorian Calendar Reform was put into effect in the Catholic countries of Europe.)

The epoch day of the Julian Day system was chosen to be the most recent day on which three calendrical cycles all were at their respective zero points. The three cycles are the 15-year Indiction Cycle (important in Roman tax law,) the 19-year Metonic Cycle (important for obtaining approximate synchronization of lunar and solar calendars,) and the 28-year Solar Cycle (all possible patterns of Julian Calendar dates and days-of-the-week recur once every 28 years.)

The "Julian" in "Julian Day" refers to Scaliger's father, and not to either Julius Caesar or to the Julian Calendar.

Julian Day 2,454,000 corresponds to the following dates in selected calendars (as computed by the Chronos Date/Time Library):

2006-09-21 AD [Gregorian]
0163-10-14 BE [Bahai]
1723-01-11 AM [Coptic]
1999-01-11 ZH [Ethiopic]
5766-06-28 AM [Hebrew]
1928-06-30 AS [Indian Civil]
1427-08-27 AH [Islamic (Fatimid)]
2006-09-08 AD [Julian]
2759-09-08 AUC [Julian (Imperial)]
1385-06-30 AP [Persian]
6242-09-30 SY [Solarian]
2006-264 [Gregorian-ordinal date]
2006-W38-4 [ISO]
J.D. 2454000 [Julian Day]
732574 days since Gregorian Epoch (1 January 0001 AD)
38614:00:00:00 days:hh:mm:ss.s.. since 1901-01-01T00:00:00Z (ST80 epoch)
1158796800 seconds since 1970-01-01T00:00:00Z (Unix epoch)
128032704000000000 100-nanosecond ticks since 1601-01-01T00:00:00Z (MS WIndows epoch)


2006-08-20

128000000000000000

Microsoft NT counts time as the number of 100-nanosecond ticks since 1601-01-01T00:00:00Z (1 January 1601 00:00:00 Universal Time.) I just happened to notice that the NT clock struck 128000000000000000 a few days ago, specifically at 2006-08-14T03:33:20+00:00 (14 August 2006 03:33:20 Universal Time.)

You can easily compute that using the following Chronos code:


ChronosFunction
daysAndSecondsSinceStartOfDayFromSeconds: 128000000000000000 // 10000000
into: [:days :seconds |
Timepoint
daysSinceEpoch: days + (YearMonthDay year: 1601 day: 1) daysSinceEpoch
seconds: seconds
timeZone: #UT]


If you'd prefer to have the result computed relative to your local time zone, use the following variation:


ChronosFunction
daysAndSecondsSinceStartOfDayFromSeconds: 128000000000000000 // 10000000
into: [:days :seconds |
Timepoint
utDaysSinceEpoch: days + (YearMonthDay year: 1601 day: 1) daysSinceEpoch
seconds: seconds]


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-04-15

Daylight Saving Chaos

Just saw this post from Stilgherrian on the mess caused by a this-year-only change in the date for transitioning from DST back to standard time in Australia.

Key points:


  • Because the Windows fix wasn’t security-related, it wasn’t labelled “critical” — so it wasn’t downloaded automatically by Windows Update.

  • Even downloading the daylight saving Hotfix and digesting Microsoft’s easy to follow (ahem!) overview wan’t enough. [Stilgherrian's] own properly-patched Windows XP test computer still reverted to standard time anyway.

  • Microsoft’s patch has to be removed [next year], because Windows can’t cope with different rules for different years.

Macs and Unixes also had problems.

I've added Stilgherrian's post to the "Cautionary Tales" page of the Chronos web site.


2006-04-02

North American Time Zones Switch to Daylight Saving (Summer) Time

Did those of you living in North America remember to move your clocks ahead by one hour?

The Chronos Date/Time Library makes it easy to see the transition as it rolls from one time zone to the next. [Note: Most time zones transition to and from Daylight Saving Time at the same local time, not at the same moment in Universal Time. The major exceptions are the European time zones. All countries in the European Union transition at the same moment according to Universal Time, without regard to local time.]

Here's how Chronos shows the situation just before my local time zone switches to Daylight Saving Time:

Sun, 02 Apr 2006 21:55:02 +1200 (NZST: Pacific/Auckland | New Zealand Time)
Sun, 02 Apr 2006 19:55:02 +1000 (EST: Australia/Sydney | AUS Eastern Time)
Sun, 02 Apr 2006 18:55:02 +0900 (JST: Asia/Tokyo | Tokyo Time)
Sun, 02 Apr 2006 17:55:02 +0800 (WST: Australia/Perth | W. Australia Time)
Sun, 02 Apr 2006 17:55:02 +0800 (HKT: Asia/Hong_Kong)
Sun, 02 Apr 2006 15:25:02 +0530 (IST: Asia/Calcutta | India Time)
Sun, 02 Apr 2006 13:55:02 +0400 (MSD: Europe/Moscow | Russian Time)
Sun, 02 Apr 2006 12:55:02 +0300 (IDT: Asia/Jerusalem | Israel Time)
Sun, 02 Apr 2006 11:55:02 +0200 (CEST: Europe/Amsterdam)
Sun, 02 Apr 2006 10:55:02 +0100 (BST: Europe/London | London Time)
Sun, 02 Apr 2006 09:55:02 +0000 (UT: Universal Time)
Sun, 02 Apr 2006 06:55:02 -0300 (BRT: America/Sao_Paulo | E. South America Time)
Sun, 02 Apr 2006 06:55:02 -0300 (ART: America/Argentina/Buenos_Aires)
Sun, 02 Apr 2006 05:55:02 -0400 (EDT: America/New_York | Eastern Time)
Sun, 02 Apr 2006 04:55:02 -0500 (CDT: America/Chicago | Central Time)
Sun, 02 Apr 2006 03:55:02 -0600 (MDT: America/Denver | Mountain Time)
Sun, 02 Apr 2006 01:55:02 -0800 (PST: America/Los_Angeles | Pacific Time)
Sat, 01 Apr 2006 23:55:02 -1000 (HST: Pacific/Honolulu | Hawaiian Time)

Note that most of the other North American time zones have already switched as of the time shown above. For example, New York is usually 3 hours ahead of California, but at the moment depicted in the above it is 4 hours ahead.

And here's the situation as of the moment of transition:

Sun, 02 Apr 2006 22:00:00 +1200 (NZST: Pacific/Auckland | New Zealand Time)
Sun, 02 Apr 2006 20:00:00 +1000 (EST: Australia/Sydney | AUS Eastern Time)
Sun, 02 Apr 2006 19:00:00 +0900 (JST: Asia/Tokyo | Tokyo Time)
Sun, 02 Apr 2006 18:00:00 +0800 (WST: Australia/Perth | W. Australia Time)
Sun, 02 Apr 2006 18:00:00 +0800 (HKT: Asia/Hong_Kong)
Sun, 02 Apr 2006 15:30:00 +0530 (IST: Asia/Calcutta | India Time)
Sun, 02 Apr 2006 14:00:00 +0400 (MSD: Europe/Moscow | Russian Time)
Sun, 02 Apr 2006 13:00:00 +0300 (IDT: Asia/Jerusalem | Israel Time)
Sun, 02 Apr 2006 12:00:00 +0200 (CEST: Europe/Amsterdam)
Sun, 02 Apr 2006 11:00:00 +0100 (BST: Europe/London | London Time)
Sun, 02 Apr 2006 10:00:00 +0000 (UT: Universal Time)
Sun, 02 Apr 2006 07:00:00 -0300 (BRT: America/Sao_Paulo | E. South America Time)
Sun, 02 Apr 2006 07:00:00 -0300 (ART: America/Argentina/Buenos_Aires)
Sun, 02 Apr 2006 06:00:00 -0400 (EDT: America/New_York | Eastern Time)
Sun, 02 Apr 2006 05:00:00 -0500 (CDT: America/Chicago | Central Time)
Sun, 02 Apr 2006 04:00:00 -0600 (MDT: America/Denver | Mountain Time)
Sun, 02 Apr 2006 03:00:00 -0700 (PDT: America/Los_Angeles | Pacific Time)
Sun, 02 Apr 2006 00:00:00 -1000 (HST: Pacific/Honolulu | Hawaiian Time)

The offset of America/Los_Angeles is now -7 hours, instead of -8 hours--and the New York time zone is again only 3 hours ahead of San Francisco's.


2006-02-09

Chronos/VisualWorks Compatibility

A lot of work has been put into type-compatibility and interoperability between the Chronos objects that represent dates, times-of-day and date-and-time values and the analogous objects natively present in VisualWorks.

Firstly, instances of Chronos.ChronosTimezone (aliased by Smalltalk.Timezone,) Chronos.TimeOfDay, Chronos.YearMonthDay and Chronos.Timepoint (aliased by Smalltalk.DateAndTime) all respond to the message #asNative with a conversion of themselves into an equivalent instance of Core.TimeZone, Core.Time, Core.Date or Core.Timestamp (respectively.)

If one were using a version of Chronos hosted in some Smalltalk environment other than VisualWorks, the #asNative message would result in an equivalent instance of whatever class is natively used in that environment to represent the same value as is represented by the Chronos object that receives the message #asNative.

Of course, such conversions may lose information, since the Chronos objects have more semantic content that do the equivalent objects native to either VisualWorks or to any of the other Smalltalk environments.

Secondly, instances of Core.TimeZone, Core.Time, Core.Date and Core.Timestamp respond to the message #asChronosValue with a conversion of themselves into an equivalent instance of Chronos.ChronosTimezone, Chronos.TimeOfDay, Chronos.YearMonthDay or Chronos.Timepoint (respectively.)

Conversion from native date/time objects to the equivalent Chronos objects are semantically lossless. For example, when #asChronosValue is sent to a Core.Timestamp, the response is a Chronos.Timepoint that is not bound to any particular time zone, and is therefore invariant to nominal time--which is precisely the semantics of a Core.Timestamp.

Additionally, Core.Time, Core.Date and Core.Timestamp all provide additional protocol for converting themselves into analogous Chronos objects:


  • #as: -- Answers the conversion of the receiver into either a Chronos.YearMonthDay or a Chronos.Timepoint (depending on whether the receiver is or is not an instance of Core.Date) that represents the same absolute date (and time-of-day, if applicable) as the receiver, but represented according to the calendar specified by the argument

  • #asDateAndTime -- Answers the conversion of the receiver into a Chronos.Timepoint that represents the same local date and time-of-day as the receiver in the current default Chronos time zone; the answered Timepoint will have Universal-Time-invariant semantics

  • #asNominalDateAndTime -- Answers the conversion of the receiver into a Chronos.Timepoint that represents the same nominal date and time-of-day as the receiver; the answered Timepoint will have nominal-time-invariant semantics

  • #in: -- Answers the conversion of the receiver into a Chronos.Timepoint that represents the same local date and time-of-day as the receiver in the the Chronos time zone specified by the argument; the answered Timepoint will have Universal-Time-invariant semantics, unless the argument is <ChronosTimezone nominal>

Chronos.TimeOfDay, Chronos.YearMonthDay and Chronos.Timepoint also respond to the same four messages, with the same semantics.

Thirdly, for almost every instance and class method defined in the inheritance hierarchy of Core.Date there is a corresponding method with the same selector in the inheritance hierarchy of Chronos.YearMonthDay, and for almost every instance and classs method defined in the inheritance hierarchy of Core.Timestamp there is a corresponding method with the same selector in the inheritance hierarchy of Chronos.Timepoint. And all such methods have the same semantics. There are a few exceptions--but almost all of those are private methods (although even some of those are included.)

For example, the following statements/expressions will all work correctly:

YearMonthDay newDay: 1461 year: 2000

YearMonthDay fromDays: Date today asDays

YearMonthDay today subtractDate: Date today

Date today subtractDate: YearMonthDay today

YearMonthDay today previous: #Saturday

Timepoint fromDate: Date today andTime: Time now

Timepoint fromSeconds: Timestamp now asSeconds

YearMonthDay today printFormat: #(1 2 3 $- 2 2)

Locale current printAsTime: Timepoint now policyNamed: #long

YearMonthDay readFromString: Date today printString

(Timezone at: 'Australia/Adelaide')
convertGMT: Time secondClock
do: [:date :seconds |
Array
with: date
with: (Time fromSeconds: seconds)]

ChronosTimezone default
convertToGMT: Timestamp now asSeconds
do: [:date :seconds |
Array
with: date
with: (Time fromSeconds: seconds)]



2006-02-03

Why Can’t The Windows Registry Timezones be used?

A poster with the handle iwonder started a thread on the ASP.net forum, titled On Time Zones and Daylight Saving Time (Summer Time).. (I've added the whole thread to the "Cautionary Tales" section of the Chronos web site.) In a subsequent post to the thread, he has a subsection titled "Why Can’t The Windows Registry Timezones be used?", where he states:


First, and foremost the 75 windows registry timezones only are setup to support current year. Meaning that there is no historical reference. In fact, when a given locale changes their time zone daylight saving (summer time) rules, these rules need to be manually ‘fixed’ in the registry. These types of corrections are not distributed by Microsoft as updates to anyone. The corrections appear in KB articles, but the corrections could easily be missed by folks that are not in the locale affected. As each year starts, new rules are in place, which may not calculate the correct date time adjustments for any past years. Actually, any date time adjustment based on the registry alone results in a date time factored with the current years’ rules. Not exactly the result time sensitive sites would require.

Secondly, the windows registry is not totally accurate. Independent tests show it to be around 75-80% accurate at any point in time. Also, the time zones do not represent every time zone on the globe, just the more commonly used zones are included. The UNIX / Linux TZInfo database includes many more time zone descriptions, but there is a question of just how many require support. Obviously, MS made the determination that only 75 of the more than 108 zones in the TZInfo database really require support. It’s a question of judgement.

Lastly, web applications are not really meant to have access to the windows registry. It’s really a question of security, but I can’t imagine any host provider wanting to allow a web application to manipulate the registry. In fact, I’m not sure if the Code Access Security model will even allow the type of registry read and write permission required to effectively use and maintain a registry timezone information.

Correct answers in date/time computations are just as important as correct answers in issuing payroll checks, crediting interest earned, sending out invoices, computing work schedules, sheduling airline flights, administering the right medications to the right patients at the right time, getting a spaceship, rocket or missile to its intended destination at the right time, or even just dividing one number by another.

When I hear people say otherwise, there's no doubt in my mind that what I'm really hearing is an emotional reaction against the unwanted complexity of the date/time domain. But neither reality nor complexity goes away just because you wish things were otherwise.

Time is complex. Deal with it. The Universe doesn't care about your preferences, and will happily bite you in a private part of your anatomy if you can't bring yourself to accept and handle the world as it is.


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.