Chapter 11

Expressing Time

Time can be an important element in the Telescript environment. You may need to know when an object was created or when an action occurred. You may need to figure out how much time has elapsed between two actions. Or you may want to use time as an attribute that differentiates one object from earlier- or later-created objects of the same class. In other words, you may want to add a unique time stamp to an object.

The Telescript language provides two classes whose instances can express a time: Time and CalendarTime. This chapter shows you how to use both.

An Overview of Time and Calendar Time

The following class hierarchy shows the inheritance of Time and CalendarTime:

Object

* CalendarTime
* Time (Ordered, Protected)

Time

The most fundamental of these two classes is Time. When you instantiate Time, the object you create captures the time of initialization and stores the time as a time value. (The time of initialization is provided by the platform on which the Telescript engine is running.) The time value is a number of days and seconds elapsed since a fixed reference time.

Because Time inherits from the mix-in superclass Protected, a time object's time value can't be modified. This means that the time supplied at initialization is fixed for the life of a time object. Because Time also inherits from the mix-in superclass Ordered, you can use ordered operations to compare two time objects and see if one time is before, after, or simultaneous with another time.

Time's own operations allow you to determine the interval in seconds between the time values of two time objects; they also let you adjust the time value of a time object by incrementing or decrementing it to create a new time object.

CalendarTime

A time object's value doesn't have much meaning to human eyes--it's a number of days and seconds from an arbitrary fixed time. To turn a time object into meaningful data for humans, you can convert it into an instance of the class CalendarTime.

CalendarTime defines an object with a set of read/write attributes that each contain a different facet of a date and time: one attribute contains the year, another contains the month of the year, another the day of the month, another the hour of the day, another the minute of the hour, another the second of the minute, and so on. Because CalendarTime is not protected, you may modify any of these attributes, adding hours to a time, for example, or subtracting months from the date. This allows you to use a calendar time for record keeping or time calculations--adding or subtracting different units of time as you see fit.

It's possible to exceed meaningful values by modifying calendar time attributes. If, for example, you add 25 days to a calendar time whose date is June 23rd, the calendar time will have a date of June 48th. You can, at any time, normalize a calendar time object. Normalization converts out-of-range attribute values into meaningful time and date values. In the previous example, June 48th would be normalized to July 18th because June 48th goes 18 days past the 30 days allotted to June. CalendarTime's normalize operation takes into account months with different numbers of days, leap years, and other quirks of standard timekeeping.

If you want to compare two calendar times, you can convert them both to time objects. You can then check to see if one is before, after, or simultaneous to the other, or you can find out the difference in time between the two objects.

The Time Class

The Time class defines an object that has no attributes. It has a single property--its time value which, as you'll recall, is a number of days and seconds elapsed since a fixed time. That fixed time is not defined by the Telescript language, so its value depends on the Telescript implementation used where a Time object is instantiated. (The most common implementation uses a fixed time of midnight, November 17, 1858 A.D.) Because the fixed time is arbitrary, you can't count on it being the same from one implementation of the Telescript language to another, and shouldn't look on a time value as a concrete time.

The time value has two parts: days, which is the number of Julian days that have elapsed since the fixed time; and seconds, which is the number of seconds that have passed since midnight of the current day.

A time object's time value is created at the moment of initialization. The value is determined using the Coordinated Universal Time (UTC) at that moment. You might know UTC better as Greenwich Mean Time, the current time in Greenwich, England, with no modifications made for daylight savings time. When the time object is instantiated, its time value is the number of Julian days from Telescript's fixed time to the current UTC date, and the number of seconds from midnight of the current date to the UTC time at initialization.

The accuracy of a time value is completely dependent on the platform. If the date and time kept by the platform isn't set properly, or if the platform runs slow or fast as it keeps time, the platform may supply an inaccurate time when a time object is instantiated. This means that three simultaneously instantiated time objects from three different platforms may have three different time values. This is an important consideration to keep in mind when time objects travel from one Telescript engine to another. Comparing time objects from two different engines can't be guaranteed to give you completely accurate results.

(A timely aside: If you're interested in reckoning time and date in a computer environment, and in how a Julian day value is reckoned, you'll find technical details in "Calendrical Calculations" by Nachum Dershowitz and Edward M. Reingold in Software--Practice and Experience, Volume 20, Number 9, September 1990, pp. 899-928. You'll find that the standard Julian days reference time is noon, January 1, 4713 B.C., a date that leads to extremely large day values in a time value. Midnight, November 17, 1858 A.D., the fixed time commonly used in today's Telescript implementations, turns out to be a convenient fixed time for reckoning contemporary time values. That's because it's exactly 2,400,000.5 days later than the standard fixed time, so it allows day values to be expressed in integers of reasonable size.)

Initializing a Time Object

When you initialize a time object, you supply no initialization arguments. For example

creates a time object whose time value is set at the moment of initialization.

Time objects are also created as the result of two operations that you'll read about later in this chapter. If you call adjust on an existing time object, you create a new time object based on the first time object. If you call asTime on a calendar time object, you convert it to a new time object. In both cases, the newly initialized time objects have times that are based on the responder, not on the moment of initialization.

Adjusting a Time

Once a time object is created, its time value is protected and can't be changed. You can, however, create a new time object whose time value is an adjustment of the time value of a second time object. To do so, you use the adjust operation, which accepts a single argument: a positive or negative integer that gives the number of seconds you want to use to adjust the original time value. This statement

creates a new time object twoMinutesLater whose time value is 120 seconds later than the time value of rightNow.

You can, if you want, use the same variable name to discard the original time object and use the adjusted time object in its place:

Determining an Interval of Time

If you want to determine the interval of time between the time values of two time objects, you can use the interval operation. interval accepts a single argument: a time object. It calculates the interval of time between the argument time object and the responder time object by subtracting the argument's time value from the responder's time value. It returns an integer that is the number of seconds difference between the two time values. The following statement, for example, determines the interval of time between rightNow and twoMinutesLater:

The interval operation here subtracts the time value of rightNow from the time value of twoMinutesLater. If both those time objects were defined as they were in previous examples, the operation would return a value of 120 (the number of seconds difference between the two) so timeElapsed would be equal to 120.

Comparing Two Times

It's often useful to compare two time objects to see if one is before, after, or equal (simultaneous) to the other. The operations maximum, minimum, and order--all inherited from the mix-in superclass Ordered--allow you to make time comparisons. Ordered has been specialized in Time to compare the time values of time objects.

Because time objects are comparable, you can use them with relationship operators such as <, >, and ==. If one time is after a second time, it is greater than the second time; if it's before a second time, it's less than the second time; if it's simultaneous with the second time, it's equal to the second time.

Converting a Time Object to a Calendar Time

If you want to convert a time object to a calendar time, Time offers two conversion operations: asCalendarTime and localize. asCalendarTime accepts no arguments and returns a calendar time object that expresses the same second of time that the time object expresses. The calendar time object's attributes are all normalized (no out-of-range values are present), and its expressed time doesn't show any permanent or seasonal offsets. In other words, it shows UTC: Greenwich Mean Time without any Daylight Savings Time.

localize also converts a time object to a calendar time. It accepts no arguments and returns a calendar time object that expresses the same second of time that the time object expresses. Its attributes are all normalized. The main difference between localize and asCalendarTime is that localize expresses any permanent or seasonal offsets in effect on the Telescript engine's platform. It reflects the time zone where the platform is located and the daylight savings time (DST) offset if DST is in effect.

The CalendarTime Class

The CalendarTime class defines an object that expresses time through its read/write attributes: time in years, months, days, hours, minutes, and seconds. These attributes also provide information about the time zone where the time is expressed, and any daylight savings time adjustments made to the time.

You may change the time expressed by a calendar time. (Compare this to a time object, whose time value is unchangeable once the object is created.) To change a calendar time, you write a new value to one of its attributes. You can read any aspect of a calendar time--the month, the hour, or time zone, for example--by simply reading the appropriate attribute.

Initializing a Calendar Time

When you initialize a calendar time in a constructor expression, you supply no initialization arguments. The calendar time you create has attributes that are all set to nil, so the object doesn't express any time at all on initialization. For example,

creates a calendar time named whatTime whose attributes are all nil.

If you create a calendar time by converting a time object (using the operations localize or asCalendarTime on the time object), the calendar time created has all of its attributes set to express a single moment in time--the time expressed by the converted time object. If you want to create a calendar time that expresses the time of its creation, you can initialize a time object, convert it, and discard the original time object as shown in this statement:

The statement creates a time object, calls localize on it to return a calendar time, then assigns the calendar time to the variable nowCalendarTime. The original time object is discarded; the end result is a calendar time named nowCalendarTime whose expressed time is the moment of its creation.

Working with Date Attributes

CalendarTime has five attributes that express aspects of the date. Each attribute can be nil or a negative or positive integer value. Each attribute has a range of values that are considered normal--that is, a value that has meaning for the aspect of date expressed by the attribute. The attribute can have a value outside of the normal range; if so, the value will be adjusted appropriately if the calendar time object is normalized.

You can change the date expressed by a calendar time by writing new values to year, month, or day, or by modifying existing values (adding or subtracting years, months, or days, for example). Writing new values to dayOfWeek or dayOfYear has no effect on the values of the other date attributes; these two attributes are used only to show different aspects of the date, and are best treated as read-only attributes.

Working with Time Attributes

CalendarTime also has three attributes that express aspects of time--that is, increments of time smaller than a day. These attributes, like the date attributes, can be nil, or a negative or positive integer value. Each attribute has a normal range of values.

You can change the time expressed by a calendar time by writing new values to hour, minute, or second, or by modifying their existing values.

Working with Time Offset Attributes

The expression of time changes from place to place around the globe, depending on the time zone where the time is expressed and whether or not daylight savings time (DST) is in effect there. These two factors are time offsets--that is, they modify the current time as it's expressed in UTC. For example, a time in UTC may be 5:34:13 a.m. on July 6, 1997. Seven time zones away, in Deer Lodge, Montana, the same time is 9:34:13 p.m. on July 5, 1997. The two offsets that create the local time in Deer Lodge are the time zone offset (-7 hours) and the DST offset (-1 hour). They combine to create a local time that's eight hours earlier than UTC.

CalendarTime has two attributes that express time offsets. Each can be nil or a negative or positive integer value, and each has a normal range of values.

It's important to note that zone and dst are not linked to the time and date attributes during normalization. If you change the zone value, for example, by adding 60 (the effect of moving another time zone to the west), the hour and minute attributes are unchanged--that is, they don't reflect the change in time zone, and won't even if the calendar time is normalized. The offset attributes are strictly present for reference so you can read them to see what offsets are in effect for the date and time expressed in the other attributes. If you change offset attributes to express a calendar time with a new time zone or DST offset, it's your responsibility to change the hour and minute attributes to match the change to new offsets.

Normalizing Calendar Time

A calendar time may have any one of its attributes modified so that its value falls outside of its normal range of values. An hour attribute of 15, for example, may be incremented by 63 to a new value of 78, which falls well outside of hour's normal range of 0-23. To turn out-of-range values into meaningful values, CalendarTime offers the normalize operation.

normalize accepts no arguments. When it executes, it checks each attribute for out-of-range or nil values. If an attribute has an out-of-range value, normalize brings the value into the normal range. If an attribute has a nil value, normalize turns it into a 1 or a 0 depending on the attribute (as shown later). normalize returns a boolean: true if it made changes to any of the calendar time's attributes, false if it made no changes.

Normalizing Out-of-Range Values

When normalize brings an out-of-range value into the normal range, it increments or decrements the next-greater attribute to maintain the overall expressed time. For example, if it normalizes an hour value of 78, it realizes that 78 hours is equal to 3 days (72 hours) and 6 hours, so it adds 3 to the day attribute and sets hour equal to 6.

normalize starts its work with the smallest time attribute and works to the greatest date attribute: it begins with second, moves to minute, then hour, day, month, and finally year. It independently normalizes the offset attributes (zone and dst) without regard to the date and time attributes. And it calculates dayOfWeek and dayOfYear from the values of the normalized day, month, and year attributes.

normalize follows the rules of Gregorian calendar time when it normalizes out-of-range values. As an example of the way it works, consider a calendar time whose attributes are as follows:

year: 1996
month: 2
day: 27
hour: 21
minute: 34
second: 50
zone: 480
dst: 0
dayOfWeek: 3
In other words, it expresses the time 9:34:50 p.m. on Tuesday, February 27, 1996, the 58th day of the year. The time is expressed in Pacific Standard Time--the local time for time zone 8. There is no daylight savings time in effect.

Now let's say that the calendar time is incremented at different times: once to add two days to the day attribute, another time to add 58 hours to the hour attribute, and again to add 397 to the second attribute. The result is the following set of unnormalized attributes:

year: 1996
month: 2
day: 29
hour: 79
minute: 34
second: 447
zone: 480
dst: 0
dayOfWeek: 3
Two attributes now have out-of-range values: hour and second. day does not have an out-of-range attribute because 1996 is a leap year, so February has 29 days.

When normalize is called on the calendar time, the attributes are as follows:

year: 1996
month: 3
day: 3
hour: 7
minute: 41
second: 27
zone: 480
dst: 0
dayOfWeek: 1
It expresses the time 7:41:27 a.m. on Sunday, March 3, 1996--the 63rd day of the year as expressed in Pacific Standard time.

Let's look at the process of normalization:

normalize first checked the second value which, at 447, is past the upper limit of 60. It divided 447 by 60 to get 7 with a remainder of 27--in other words, 447 seconds equals 7 minutes and 27 seconds. It wrote 27 to second, then added 7 to minute to create a new minute value of 41.

Because minute is within normal range, normalize moved to hour which, at 79, is past the upper limit of 23. It turned the 79 hours into 3 days and 7 hours (3*24+7), wrote 7 to hour and added 3 to day to create a new day value of 32.

32 is three days past the upper limit of 29, so normalize turned it into 1 month and 3 days. It wrote 3 to day and added 1 to month to create a new month value of 3. Because month and year are within normal limits, normalize didn't change them.

The month and day values were changed, so the calendar time calculated new values for dayOfWeek and dayOfYear: 1 and 63 respectively. zone and dst were never out of normal range, so their values weren't changed.

If zone or dst have out-of-range values (not in the range of -720 to 720), normalize wraps the value around to the opposite side of the range as many times as necessary. A value of -780, for example, would change to 660; a value of 1560 would change to -600. zone and dst don't affect each other, so normalizing one offset value won't affect the value of the other.

Normalizing Nil Values

If normalize encounters nil values in calendar time attributes, it takes different actions depending on the attribute:

year: nil
month: nil
day: nil
hour: nil
minute: nil
second: nil
zone: nil
dst: nil
dayOfWeek: nil
After normalization, it has these values:

year: 1
month: 1
day: 1
hour: 0
minute: 0
second: 0
zone: 0
dst: 0
dayOfWeek: 2

Converting a Calendar Time

Although you can modify calendar times, you can't compare them or use them with relationship operators. To do so, you must first convert them to time objects.

Calling the operation asTime on a calendar time normalizes the calendar time and then converts it to a time object. The operation accepts no arguments and returns the converted time object.

Example Code

This example code deals with time. It creates both time objects and calendar times, then adjusts, increments, and compares them.

1	do{
2		/* declare variables */
3		firstTime, secondTime, thirdTime: Time;
4		humanTime: CalendarTime;

5		/* create a time variable, adjust it by 3 million seconds, compare
6		   adjusted time to original time */
7		firstTime = Time();
8		secondTime = firstTime.adjust(-3000000);
9		"Here are firstTime and secondTime".dump();
10		firstTime.dump();
11		secondTime.dump();
12		if firstTime > secondTime
13				{"firstTime comes after secondTime.".dump();}
14			else if firstTime < secondTime
15				{"firstTime comes before secondTime.".dump();}
16			else {"firstTime is simultaneous with secondTime".dump();};

17		/* convert original time to a localized calendar time */
18		humanTime = firstTime.localize();
19		"This is humanTime after conversion from firstTime".dump();
20		humanTime.dump();

21		/* subtract 3 million seconds from calendar time */
22		humanTime.second = humanTime.second - 3000000;
23		"This is humanTime after decrementing 3000000 seconds".dump();
24		humanTime.dump();

25		/* normalize calendar time */
26		humanTime.normalize();
27		"This is humanTime after normalization".dump();
28		humanTime.dump();

29		/* convert calendar time to time object, compare with original time */
30		thirdTime = humanTime.asTime();
31		"Here are firstTime and thirdTime".dump();
32		firstTime.dump();
33		thirdTime.dump();
34		String("There is a difference of ",
35			firstTime.interval(thirdTime).asString(),
36			" seconds from thirdTime to firstTime.").dump();
When it runs, you see these results:

String: <Here are firstTime and secondTime>
Time: <Modified Julian Day 50126, Helgeseconds 340320000>
Time: <Modified Julian Day 50091, Helgeseconds 1300320000>
String: <firstTime comes after secondTime.>
String: <This is humanTime after conversion from firstTime>
CalendarTime:
        year = 1996
        month = 2
        day = 12
        hour = 18
        minute = 21
        second = 48
        DST = 0
        timezone = -480
        dayOfWeek = 2
        dayOfYear = 43
String: <This is humanTime after decrementing 3000000 seconds>
CalendarTime:
        year = 1996
        month = 2
        day = 12
        hour = 18
        minute = 21
        second = -2999952
        DST = 0
        timezone = -480
        dayOfWeek = 2
        dayOfYear = 43
String: <This is humanTime after normalization>
CalendarTime:
        year = 1996
        month = 1
        day = 9
        hour = 1
        minute = 1
        second = 48
        DST = 0
        timezone = -480
        dayOfWeek = 3
        dayOfYear = 9
String: <Here are firstTime and thirdTime>
Time: <Modified Julian Day 50126, Helgeseconds 340320000>
Time: <Modified Julian Day 50091, Helgeseconds 1300320000>
This program creates a time object in line 7, then adjusts it in line 8 to create a second time object that is three million seconds (a little more than a month) less than the first time object. Lines 10 and 11 dump the time objects so you can see their values, then the code in lines 12-16 checks the relationship between the two times and reports on what it finds.

Line 18 converts the first time object into a localized calendar time (Pacific Standard time, in this case), then line 20 dumps the object so you can see its attributes.

In line 22, the program decrements three million seconds by subtracting 3000000 from the second attribute of the calendar time; line 24 dumps the calendar time so you can see its unnormalized second value.

Lines 26-28 normalize the calendar time and then dumps it. Notice that the subtraction of three million seconds, when normalized, creates an expression of time more than a month earlier than the calendar time when it was first converted.

Line 30 converts the altered calendar time back to a third time object. Because the calendar time was altered by subtracting three million seconds, it should be three million seconds before the first time object. Lines 32 and 33 dump the two time objects so you can see them. Line 35 subtracts the third time from the first time to get an interval of time in seconds between the two time objects, then converts the integer value to a string so that it may be dumped with the larger string concatenated in lines 34-36.

* * *

With this discussion of time, you've finished the last chapter of this section of the Telescript Reference Manual. You should have a firm grasp of Telescript syntax, some fundamental classes, and ways you can work with the features of those classes. In the next section, you'll learn more advanced classes, and will learn how to create classes of your own.

11 - Expressing Time
An Overview of Time and Calendar Time
Time
CalendarTime
The Time Class
Initializing a Time Object
Adjusting a Time
Determining an Interval of Time
Comparing Two Times
Converting a Time Object to a Calendar Time
The CalendarTime Class
Initializing a Calendar Time
Working with Date Attributes
Working with Time Attributes
Working with Time Offset Attributes
Normalizing Calendar Time
Normalizing Out-of-Range Values
Normalizing Nil Values
Converting a Calendar Time
Example Code

TS Ref - 26 JUN 1996

Generated with Harlequin WebMaker