The Timing of Time

Hernan Wilkinson hernan.wilkinson at mercapsoftware.com
Thu Apr 20 22:19:31 UTC 2006


Hi Jeff, I will answer your questions over Alan's responses to make it 
easy to compare both solutions. (I hope Alan you don't mind...)

Alan Lovejoy wrote:

>Hi Jeff,
>
>You're asking the right questions.
>  
>
I agree, good questions.

>Jeff: "So how do these various packages handle indexing data by time?"
>
>Well, I won't presume to speak for the other packages, but here's the
>situation in Chronos:
>
>Firstly, Chronos dates, times, durations and time periods are all immutable
>objects, 
>
Chalten too. I believe that is a very important characteristic.

>and fully support comparison operations--including answering a hash
>code.  So they can be safely added to Sets, or used as keys in a Dictionary.
>  
>
The same for Chalten.

>Date equality works correctly between different calendars, and Timepoint
>equality works correctly between different time zones and/or different
>calendars.
>  
>
Chalten does not have support for time zone or different calendars. It 
just support the Gregorian Calendar; we are planning to add support to 
other calendars  and time zone but in different packages... it is not 
common to use other calendars than the Gregorian and we don't want to 
make the most commonly used abstraction difficult to understand because 
of that support.

>It's easy to convert a Chronos YearMonthDay or Timepoint into a count of
>days, or to convert a count of days into a YearMonthDay or Timepoint:
>
>	YearMonthDay daysSinceEpoch: YearMonthDay today daysSinceEpoch.
>
>  
>
Same for Chalten. For example:
   ( January first, 2006) numberOfDaysFromBaseDate --> Returns  "38351 
days".

But as you can see it returns a Measure. They also are immutable, 
comparable, can be put in Sets, Dictionaries, etc.
I believe that the fact that we return a Measure and not just a numbers 
makes "explicit" the meaning of the returned object. It is not just 
38351 but "38351 days"
What's the advantage? Well, there are many, for example:
    1) If you see the message #numberOfDaysFromBaseDate we, as 
programmer, know that it will return a number or a measure in this case, 
but what happens if you just see the object "38351"? You will not be 
able to know if they are days, seconds, years or what ever meaning it 
has. If instead you see "38351 days", now you know you are dealing with 
days. But not only we as programmers will notice that, the whole idea of 
Aconcagua is that the computer will handle any arithmetic mistake you 
can have mixing measure of different units. Using measures, you will 
never mix days and years. For example:
          a) If the model returns numbers instead of measures, you could 
write:
            ( January first, 2006) numberOfDaysFromBaseDate + ( January 
first, 2006) year yearsFromBaseDate --> That is 38351 + 106 =  40357
            There is no way to know from the result that you added days 
and years.
          b) If the model returns measures:
            ( January first, 2006) numberOfDaysFromBaseDate + ( January 
first, 2006) year yearsFromBaseDate --> It will return "38351 days + 106 
years"
           As you can see, it does not mix up days and years because 
they are not interchangeable. A year can be 365 or 366 days in the 
Gregorian calendar.
    2) Because we use measures, if you want to know the number of hours 
from the base date, you can do:
       ( January first, 2006) numberOfDaysFromBaseDate convertTo: 
TimeUnits hour --> Returns  " 920424 hours".
       Or seconds:
       ( January first, 2006) numberOfDaysFromBaseDate convertTo: 
TimeUnits hour --> Returns  " 3313526400 seconds"
       There is no need for a class Duration or special messages to get 
the number of seconds from base date, etc.
   

>So the problem of indexing by a date resolves to the problem of indexing by
>an integer. It's also possible to convert a date into a count of months or
>years since the calendar epoch, and to convert a count of months or years
>since a calendar epoch into a date.
>
>Both YearMonthDay and Timepoint (which inherits from YearMonthDay) support
>date and time arithmetic operations:
>
>	#addingYears:
>	#subtractingYears:
>	#addingMonths:
>	#subtractingMonths:
>	#addingDays:
>	#subtractingDays:
>	#addingYears:months:days:
>	#subtractingYears:months:days:
>	#addingHours:
>	#subtractingHours:
>	#addingMinutes:
>	#subtractingMinutes:
>	#addingSeconds:
>	#subtractingSeconds:
>	#addingSeconds:nanonseconds:
>	#subtractingSeconds:nanoseconds:
>
>  
>
Well, here is an example of how we simplified the protocol of all 
PointInTime objects using measures. For the same porpouse that Alan 
shows in Chronos, we have only two messages: #next: and #previous: (we 
don't use add and subtract for semantinc issues, but that another 
problem it does not make sense to talk about now). For example:

    GregorianDate today next: 10 * TimeUnits day   -> Will return " 
April 30, 2006" if today is " April 20, 2006"
    GregorianDate today previous: 10 * TimeUnits day   -> Will return "  
April 10, 2006" if today is " April 20, 2006"
    GregorianDate today next: -10 * TimeUnits day   -> Will return 
"April 10, 2006" if today is " April 20, 2006"
    GregorianDate today next: 48 * TimeUnits hour   -> Will return " 
April 22, 2006" if today is " April 20, 2006"
    (See that we move the point in time by hours, not days... or)
    GregorianDate today next:  86400 * TimeUnits second   -> Will return 
"April 21, 2006" if today is " April 20, 2006"

We can do the same with years, months, days, times, etc. For example:
    GregorianYear current next: 10 * TimeUnits year -->Will return Year 2016
    GregorianYear current previous: 24 * TimeUnits month -->Will return 
Year 2004

    GregorianDay today next: 3 * TimeUnits day --> Will return "Sunday" 
if today is  "Thursday"
    GregorianMonth current previous: 2 * TimeUnits month --> Will return 
" February" if the current month is " April"
    etc.

>Or, durational objects can be used instead:
>
>	Timepoint now + (ScientificDuration microseconds: 75)
>	YearMonthDayToday - (CalendarDuration months: 3)
>	Timepoint now + (CivilDuration years: 5 months: -3 days: 10 hours:
>-1 minutes: 30 seconds: -11)
>
>A method to compute the Timepoint N periods after a base timepoint might
>look like this:
>
>SomeClass>>at: index
>	^baseTimepoint + (duration * index)
>  
>
With Chalten it would be:

SomeClass>>at: index
	^basePointInTime numberOfDaysFromBaseDate + (duration * index)

We do not implemented the #+, #-, etc. messages in PointInTime objects 
because those message do not keep the same semantic as the arithmetic 
message. For example. if you do "1 + 2" you get "3". That is, you are 
adding number, you get a number. But for dates is not the same... For 
example "GregorianDate today + 10 days" would not return a measure of 
days but a new date, so it does not match the semantics of the 
arithmetic +.
Of course this is a decision we made and not everybody could like or 
agree with that... but we believe it makes more clear the model's language.

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

    GregorianTimespan from: GregorianDate today duration: 20 * TimeUnits 
day --> Returns  "20 days from April 20, 2006"
    GregorianTimespan from: GregorianYear current duration: 10 * 
TimeUnits year --> Returns  " 10 years from Year 2006"
    GregorianTimespan from: GregorianMonth current duration: 2 * 
TimeUnits month --> Returns  " 2 months from April"
    GregorianTimespan from: GregorianDay today duration: 2 * TimeUnits 
day --> Returns  " 2 days from Thursday"
    GregorianTimespan from: GregorianDayOfMonth today duration: 25 * 
TimeUnits day --> Returns  " 25 days from April 20"
    GregorianTimespan from: GregorianMonthOfYear current duration: 12 * 
TimeUnits month --> Returns  " 12 months from April of Year 2006"
    GregorianTimespan from: TimeOfDay now duration: 12 * TimeUnits hour 
--> Returns  " 12 hours from 18:53:33" (I wrote the mail at 18:53:33)
    GregorianTimespan from: GregorianDateTime now duration: 12 * 
TimeUnits hour --> Returns  " 12 hours from April 20, 2006 18:54:14"

Look how easy is to use the same abstraction, GregorianTimespan, to any 
type of PointInTime. I think we could achieve that because we are using 
measures and all point in times are polymorphic. (By the way, we use 
GregorianTimespan because Chronology already defines Timespan)
We also have time intervals, that are instances of the class 
"ArihtmeticObjectInterval" (I think not a good name). We could not use 
the Interval class for many reason. Anyway, ArihtmeticObjectInterval is 
an interval of any type of Magnitude, for example Measures, Numbers and 
of course PointInTimes. For example:
    GregorianDate today to: December first, 2006 --> Returns an interval 
from today to December fist 2006 by 1 day
    GregorianDate today to: December first, 2006 by: 10 * TimeUnits day 
--> Returns an interval from today to December fist 2006 with steps of 
10 days

    GregorianMonth current to: December by: 2 * TimeUnits month --> Now, 
and interval of months.... the same for other time points...

Because these objects are intervals, they are polymorphic with Collection:

    (GregorianDate today to: December first, 2006) select: [ :aDate | 
aDate isMonday ] --> Returns all Mondays from today to December first
    (GregorianYear current to: (GregorianYear number: 3000) ) select: [ 
:aYear | aYear isLeap ] --> Returns all leap year up to year 3000
    (January first, 2006 to: GregorianDate today) collect: [ :aDate | 
aDate distanceTo: GregorianDate today ] --> Returns an array with 
measures from 109 days to 0 days

>Does that answer your question about indexing?
>
>Jeff: "What should happen if you start at January 28'th and advance by a
>month and then go back by a month?
>Jan28 --> Feb28 --> (Jan 28 or Jan 31?)   Can we agree on some
>conventions? "
>
>(YearMonthDay year: 2006 month: 1 day: 28) + 1 months =>  2006-02-28
>(YearMonthDay year: 2006 month: 1 day: 31) + 1 months =>  2006-02-28
>(YearMonthDay year: 2006 month: 1 day: 28) + 1 months - 1 months  =>
>2006-01-28
>(YearMonthDay year: 2006 month: 1 day: 31) + 1 months - 1 months  =>
>2006-01-28
>
>(Note: "1 months" works in VW and Dolphin, but not in Squeak, where
>"(CalendarDuration months: 1)" must be used instead--or you could add a
>#months and #years method to Integer yourself.)
>  
>
In Chalten we have a set of units that are interchangeable for years, 
moths, decades, etc. and another for days, hours, seconds, weeks, etc. 
Notice that time units are not the same as time points (there is an 
object for years, ie. GregorianYear, and there is a unit to measure 
years, ie. TimeUnit year)
That means that measures expressed in days can not be converted to 
months or years and vicersa. (Is 30 days a month? or 31 days? etc.)
Because of the irregularity of the Gregorian calendar we decided not to 
allow movements of point in times with measure of units of less 
granularity that the point in time granularity. For example:
    GregorianDate today next: 1 * TimeUnits second --> Is not allowed
But we added for convenience that possibility of doing:
    (January thirtyfirst, 2006 next: 1 * TimeUnits month) --> Returns 
February 28, 2006
    (January thirtyfirst, 2006 next: 1 * TimeUnits month) previous: 1 * 
TimeUnits month --> Returns January 28, 2006
I don't like this behavior but we added because we people are used to it.

Well, the mail became longer that I expected. I hope it helps you to 
understand Chalten and also compare it with other solutions.

Bye,
Hernan

>Chronos implements date arithmetic to satisfy the typical business use case.
>If you want "scientific" behavior, use a ScientificDuration.  If you want a
>"month" to always be 30 days, use a "monthDuration" value defined as
>"CalendarDuration days: 30."
>
>But these questions just scratch the surface.  There's a lot more than can
>and should be asked.
>
>--Alan
>
>-----Original Message-----
>From: squeak-dev-bounces at lists.squeakfoundation.org
>[mailto:squeak-dev-bounces at lists.squeakfoundation.org] On Behalf Of Jeffrey
>J. Hallman
>Sent: Wednesday, April 19, 2006 9:10 AM
>To: squeak-dev at lists.squeakfoundation.org
>Subject: Re: The Timing of Time
>
>I'm enjoying the discussion about calendars and times, but I'd like to
>direct some attention to the idea of a TimeIndex.  I came at this from
>another direction, thinking of a TimeSeries as data indexed by time, which
>leads to the idea of a TimeIndex.
>
>In my current implementation, a TimeIndex has two instance variables: a freq
>symbol (such as #weeklyMonday, #monthly, #hourly, etc.) and an integer
>period, which represents the number of periods elapsed since the base period
>for that freq. TimeIndex understands '+' and '-', so if 'z'
>is the weeklyMonday time index for April 17 2006, then (z - 4) asYmd yields
>20060320, and so on.
>
>My TimeSeries class is a subclass of Matrix with an additional instance
>variable called 'start' which is a TimeIndex, and has accessors like
>
>TimeSeries>>atTimeIndex:
>TimeSeries>>atTimeIndex:put:
>TimeSeries>>atTimeIndex:column:
>TimeSeries>>atTimeIndex:column:put:
>
>I've found this scheme works pretty well, and will probably generalize it a
>bit more by making TimeIndex an abstract class with subclasses for different
>kinds of sequences.
>
>I think a package that wants to handle date and time issues should have
>something like a TimeIndex in it that allows sequences of various
>frequencies to be defined.  At the same time, the implementation should be
>simple enough to be easily understood, and indexing into a TimeSeries should
>be very fast.  If indexing isn't fast, we'll end up with ugly code in every
>TimeSeries method that that converts TimeIndex'es to row numbers and back.
>Trust me, you really don't want that.
>
>So how do these various packages handle indexing data by time?
>
>A second issue is how to handle date arithmetic.  Some durations are based
>on linear time (e.g., weeks, seconds, hours, etc.) and others are based on
>the calendar (months, years, etc.).  The distinction between these two leads
>to questions like: What should happen if you start at January 28'th and
>advance by a month and then go back by a month?
>Jan28 --> Feb28 --> (Jan 28 or Jan 31?)   Can we agree on some
>conventions?
>
>Jeff Hallman
>
>
>
>
>
>  
>


-- 
______________________________
Lic. Hernán A. Wilkinson
Gerente de Desarrollo y Tecnología
Mercap S.R.L.
Tacuari 202 - 7mo Piso - Tel: 54-11-4878-1118
Buenos Aires - Argentina
http://www.mercapsoftware.com
--------------------------------------------------------------------- 
Este mensaje es confidencial. Puede contener informacion amparada 
por el secreto profesional. Si usted ha recibido este e-mail por error, 
por favor comuniquenoslo inmediatamente via e-mail y tenga la 
amabilidad de eliminarlo de su sistema; no debera copiar el mensaje 
ni divulgar su contenido a ninguna persona. Muchas gracias. 
 
This message is confidential. It may also contain information that is 
privileged or otherwise legally exempt from disclosure. If you have 
received it by mistake please let us know by e-mail immediately and 
delete it from your system; you should also not copy the message nor 
disclose its contents to anyone. Thanks. 
 --------------------------------------------------------------------- 




More information about the Squeak-dev mailing list