Advanced Search
Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

Previous Book Contents Book Index Next

Inside Macintosh: Operating System Utilities /
Chapter 4 - Date, Time, and Measurement Utilities


Using the Date, Time, and Measurement Utilities

This section describes how to

Getting the Current Date and Time

The Date, Time, and Measurement Utilities provide

You can access the date-time information through a date-time record, representing the date and time, or you can access the date-time information through a standard date-time value, a 32-bit integer representing the number of seconds since midnight, January 1, 1904.

To obtain the current date-time information, you can use the GetDateTime and GetTime procedures. The GetDateTime procedure requires that you pass it a standard date-time value as a parameter. Listing 4-1 shows how you can get the current date-time information, expressed as a number of seconds. The application-defined procedure MyCurrentDateTimeInt returns in the long integer the number of seconds elapsed since midnight, January 1, 1904.

Listing 4-1 Getting the current date and time with the GetDateTime procedure

PROCEDURE MyCurrentDateTimeInt (VAR myStandardDateTime: LongInt);
BEGIN
   GetDateTime(myStandardDateTime);
END;
The GetTime procedure requires that you pass it a date-time record as a parameter, and it fills in the fields of this record appropriately. Listing 4-2 shows how you can get the current date-time information, expressed as a date and time. The application-defined procedure MyCurrentDateTimeRec returns in the fields of the date-time record the current date and time.

Listing 4-2 Getting the current date and time with the GetTime procedure

PROCEDURE MyCurrentDateTimeRec (VAR myDateTime: DateTimeRec);
BEGIN
   GetTime(myDateTime);
END;
If you need to access the date-time information through a long date-time value or a long date-time record, see "Converting Date-Time Formats" beginning on page 4-12 for more information about converting date-time formats.

Setting the Current Date and Time

Your application can change the current date-time information stored in both the system global variable Time and in the clock chip by calling either the SetDateTime function or the SetTime procedure. The SetDateTime function requires a 32-bit integer as a parameter. The SetTime procedure requires a date-time record as a parameter.

Note
If you are using formats other than a date-time value or a date-time record to access date-time information, you must first convert these formats into a standard date-time value or a date-time record before you can write the new date-time information to the clock chip. See "Converting Date-Time Formats" beginning on page 4-12 for more information about converting date-time formats.
Listing 4-3 shows an application-defined function that uses the SetDateTime function to change the current date and time to 5:50 A.M. on April 5, 1994.

Listing 4-3 Changing the current date and time with the SetDateTime function

FUNCTION MyChangeDateTimeInt: OSErr;
VAR
   myDateTimeInt: LongInt;
   myErr:         OSErr;
BEGIN
   myDateTimeInt := $A9C6AC88;
   myErr := SetDateTime(myDateTimeInt);
END;
Listing 4-4 shows an application-defined procedure that uses the SetTime function to change the current date and time to 5:50 A.M. on April 5, 1994.

Listing 4-4 Changing the current date and time with the SetTime function

PROCEDURE MyChangeDateTimeRec;
VAR
   myDateTimeRec: DateTimeRec;
   myErr:         OSErr;
BEGIN
   WITH myDateTimeRec DO
   BEGIN
      year := 1994;
      month := 4;
      day := 5;
      hour := 5;
      minute := 50;
      second := 0;
      dayOfWeek := 3;
   END;
   SetTime(myDateTimeRec);
END;
IMPORTANT
Users can change the current date and time stored in both the system global variable Time and in the clock chip by using the General Controls control panel, Date & Time control panel, or the Alarm Clock desk accessory. In general, your application should not directly change the current date-time information. If your application does need to modify the current date-time information, it should instruct the user how to change the date and time.

Converting Date-Time Formats

The Date, Time, and Measurement Utilities provide four routines--the DateToSeconds, SecondsToDate, LongDateToSeconds, and LongSecondsToDate procedures--that you can use to convert date-time formats. You can convert a date and time to a number of seconds and a number of seconds to a date and time.

Note that when you call one of these routines, system software uses the DateToSeconds, SecondsToDate, LongDateToSeconds, and LongSecondsToDate procedures provided by the current script system.

Note
The routines that convert between time formats assume that each day contains 86,400 seconds. Occasionally (approximately once each two years) astronomers add a second to either June 31 or December 31 to compensate for imperfections in the earth's rotation. If you need to compute the exact number of seconds between two points in time, you might need to take these occasional additions into account. The routines that convert between formats are designed not to provide astronomical accuracy, but merely to convert data between one data structure and another.
If you use a standard date-time value or a date-time record to access date-time information, you can use the SecondsToDate procedure to convert a number of seconds to a date and time, and the DateToSeconds procedure to convert a date and time to a number of seconds. Listing 4-5 shows an application-defined procedure, MyConvertSecondsAndDates, that uses the SecondsToDate and DateToSeconds procedures to manipulate the date-time information. After calling the GetDateTime procedure, MyConvertSecondsAndDates calls the SecondsToDate procedure to convert the number of seconds (returned by the GetDateTime procedure) to a date and time. The MyConvertSecondsAndDates procedure manipulates the year field in the date-time record and then calls DateToSeconds to convert the date and time back into a number of seconds. The SetDateTime procedure writes the new date-time information to the clock chip.

Listing 4-5 Manipulating date-time information

PROCEDURE MyConvertSecondsAndDates;
VAR
   myDateTimeRec:       DateRec;
   mySeconds:           DateTime;
   myErr:               OSErr;
BEGIN
   GetDateTime(mySeconds);
   SecondsToDate(mySeconds, myDateTimeRec); 
   WITH myDateTimeRec DO
      year := year + 1;
   DateToSeconds (myDateTimeRec, mySeconds);
   myErr := SetDateTime(mySeconds);
END;
If you access date-time information through a long date-time value or a long date-time record, you can use the LongSecondsToDate procedure to convert a number of seconds to a date and time and use the LongDateToSeconds procedure to convert a date and time to a number of seconds.

If the type of data structure that you are using to access date-time information is insufficient, you can use a different date-time structure.

The Gregorian calendar is the default for converting to and from the long date-time forms. The current range allowed in conversion is roughly 30,000 B.C. to 30,000 A.D.

To present a date and time value as a date and time text string, you need to use Text Utilities routines, such as the DateString, TimeString, StringToDate, StringToTime, LongDateString, and LongTimeString routines. (Note that the date-string conversion routines do not append strings for A.D. or B.C.) For a complete description of these routines, see Inside Macintosh: Text.

Calculating Dates

In the date-time record and long date-time record, any value in the month, day, hour, minute, or second field that exceeds the maximum value allowed for that field, will cause a wraparound to a future date and time when you modify the date-time format.

You can use these wraparound facts to calculate and retrieve information about a specific date. For example, you can use a date-time record and the DateToSeconds and SecondsToDate procedures to calculate the 300th day of 1994. Set the month field of the date-time record to 1 and the year field to 1994. To find the 300th day of 1994, set the day field of the date-time record to 300. Initialize the rest of the fields in the record to values that do not exceed the maximum value allowed for that field. (Refer to the description of the date-time record on page 4-23 for a complete list of possible values).

To force a wrap-around, first convert the date and time (in this example, January 1, 1994) to the number of seconds elapsed since midnight, January 1, 1904 (by calling the DateToSeconds procedure). Once you have converted the date and time to a number of seconds, you convert the number of seconds back to a date and time (by calling the SecondsToDate procedure). The fields in the date-time record now contain the values that represent the 300th day of 1994. Listing 4-6 shows an application-defined procedure that calculates the 300th day of the Gregorian calendar year using a date-time record.

Listing 4-6 Calculating the 300th day of the year

PROCEDURE MyCalculate300Day;
VAR
   myDateTimeRec:    DateTimeRec;
   mySeconds:        LongInt;
BEGIN
   WITH myDateTimeRec DO
   BEGIN
      year := 1994;
      month := 1;
      day := 300;
      hour := 0;
      minute := 0;
      second := 0;
      dayOfWeek := 1;
   END; 
   DateToSeconds (myDateTimeRec, mySeconds);
   SecondsToDate (mySeconds, myDateTimeRec);
END;
The DateToSeconds procedure converts the date and time to the number of seconds elapsed since midnight, January 1, 1904, and the SecondsToDate procedure converts the number of seconds back to a date and time. After the conversions, the values in the year, month, day, and dayOfWeek fields of the myDateTimeRec record represent the year, month, day of the month, and day of the week for the 300th day of 1994. If the values in the hour, minute, and second fields do not exceed the maximum value allowed for each field, the values remain the same after the conversions (in this example, the time is exactly 12:00 A.M.).

Similarly, you can use a long date-time record and the LongDateToSeconds and LongSecondsToDate procedures to compute the day of the week corresponding to a given date. Listing 4-7 shows an application-defined procedure that computes and retrieves the name of the day for July 4, 1776. Note that because the year is prior to 1904, it is necessary to use a long date-time record.

Listing 4-7 Computing the day of the week

PROCEDURE DoDayCalc;
VAR
   myLongDateRec: LongDateRec;
   myLongSeconds: LongDateTime;
   myDayOfWeek:   Integer;
BEGIN
   WITH myLongDateRec DO
   BEGIN
      era := 0;         /*initialize era field*/
      year := 1776;
      month := 7;
      day := 4;
      hour := 0;        /*initialize hour field*/
      minute := 0;      /*initialize minute field*/
      second := 0;      /*initialize second field*/
      dayOfWeek := 1;   /*initialize dayOfWeek field*/
      dayOfYear := 1;   /*initialize dayOfYear field*/
      weekOfYear := 1;  /*initialize weekOfYear field*/
      pm := 1;          /*initialize pm field*/
   END;
   LongDateToSeconds (myLongDateRec, myLongSeconds);
   LongSecondsToDate (myLongSeconds, myLongDateRec);
   myDayOfWeek := myLongDateRec.dayOfWeek;
END;
The LongDateToSeconds procedure converts the date and time to the number of seconds, and the LongSecondsToDate procedure converts the number of seconds back to a date and time. After the conversions, the value in the dayOfWeek field of the myLongDateRec record represent the day of the week corresponding to July 4, 1776. If the values in the hour, minute, and second fields do not exceed the maximum value allowed for each field, the values remain the same after the conversions (in this example, the time is exactly 12:00 A.M.). The values in the dayOfYear, weekOfYear, and pm fields correspond to the date July 4, 1776 and the time 12:00 A.M.

Working With Different Calendar Systems

The additional fields and wider ranges allowed by the long date-time record can help you to do calculations and conversions for different calendar systems. For example, the date January 1, 1993 in the Gregorian calendar year converts to 7 Rajab 1413 in the Arabic Civil Lunar Calendar (CLC) and 4 Tevet 5753 in the Jewish calendar; the years 1413 and 5753 are outside of the year field's range in the date-time record.

Note
Depending on the country, the change from the Julian calendar to the Gregorian calendar occurred in different years. In western European countries, the change occurred in 1582; in Russia, the calendar changed in 1918. In these countries, dates before the calendar change should use the Julian calendar for conversion. (The Julian calendar differs from the Gregorian calendar by three days every four centuries.)
In addition, the beginning of the year for one calendar system falls on different dates in other calendar systems. Table 4-1 shows the equivalent dates for the first day of the calendar year in the Gregorian, Arabic CLC, and Jewish calendars.
Table 4-1 Equivalent dates in the Gregorian, Arabic CLC, and Jewish calendars
Gregorian calendarArabic CLCJewish calendar
January 1, 19937 Rajab 14134 Tevet 5753
June 20, 19931 Muharram 14141 Tammuz 5753
September 16, 199329 Rabi I 14141 Tishri 5754

Converting from one calendar system to another produces different values in the dayOfYear and weekOfYear fields of a long date-time record. For example, assuming all the data for the date 1 Muharram 1414 is correctly put into a long date-time record, the dayOfYear field value is 1, and the weekOfYear value is also 1. Converting this date to the Gregorian calendar results in June 20, 1993. The dayOfYear field value is then 171, and the weekOfYear value is 26. Table 4-2 shows these values.
Table 4-2 Values for the dayOfYear and weekOfYear fields for the date 1 Muharram 1414 and equivalent values in the Gregorian calendar
LongDateRec fieldArabic CLCGregorian calendar
dayOfYear1171
weekOfYear126

Note
Language-specific information, such as the name of the day, name of the month, and so on, are stored in the international resources. The international resources are provided by a script system, and the information in these resources varies according to the language associated with the script system.
Table 4-3 shows how some of the fields in the long date-time record are set to show the first day of the year 1414 in the Arabic CLC and the equivalent dates in the Gregorian and Jewish calendars.
Table 4-3 Comparison of settings in fields of the long date-time record for Arabic CLC, Gregorian, and Jewish calendars
Field of a long date-time recordArabic CLC calendarGregorian calendarJewish calendar
era000
year141319935753
month16 
day121 
...   
dayOfWeek423
dayOfYear1172 
weekOfYear126 

Note
The Arabic script system supports two lunar calendars: the astronomical lunar calendar (ALC) and the civil lunar calendar (CLC). The Macintosh user may choose either of the Arabic calendars or the Gregorian calendar by clicking buttons in the Arabic Calendar control panel.
The Hebrew script system supports the Jewish calendar besides the Gregorian calendar.
For more information on the different calendar systems supported by localized versions of the Macintosh system software, see Guide to Macintosh Software Localization.
For calendars that have more than seven day names and 12 month names (for example, the Jewish calendar sometimes has 13 months), you use the 'itl1' resource, defined by the Itl1ExtRec data type. To get more information on the format of the 'itl1' resource, see the appendix "International Resources" in Inside Macintosh: Text.

Handling Geographic Location and Time-Zone Data

Geographic locations and time zones can affect date and time information. For example, time-zone information can be used to derive the Greenwich mean time (GMT) at which a document or mail message was created. With this information, when the document is received by an application or user in a different time zone, the creation date and time are correct. Otherwise, documents can appear to be created after they are read (for example, a user creates a message in Tokyo on Tuesday and sends it to San Francisco, where it is received and read on Monday). Geographic location information can also be used by applications that require it.

The geographic location and time-zone information for a particular Macintosh computers are stored in parameter RAM. You can work with this information through the ReadLocation and WriteLocation procedures. These procedures use the geographic location record (of date type MachineLocation) to help you read and store latitude, longitude, daylight saving time (DST), and GMT values.

TYPE MachineLocation =              {geographic location record}
RECORD
   latitude:         Fract;         {latitude}
   longitude:        Fract;         {longitude}
   CASE Integer OF
   0:
      (dlsDelta:     SignedByte);   {daylight saving time}
   1:
      (gmtDelta:     LongInt);      {Greenwich mean time}
END;
The daylight savings time value is a signed byte value that you can use to specify the offset for the hour field--whether to add 1 hour, subtract 1 hour, or make no change at all.

The Greenwich mean time value is in seconds east of GMT. For example, San Francisco is at -28,800 seconds (8 hours * 3,600 seconds per hour) east of GMT.

If the geographic location record has never been set, all fields contain 0.

Generally, latitude and longitude are measured in degrees. These values also can be thought of as fractions of a great circle.

Latitude and longitude information is stored in the geographic location record as values of type Fract. These values give accuracy to within 1 foot, which should be sufficient for most purposes. For example, the Fract value 1.0 equals 90 degrees; -1.0 equals -90 degrees; and -2.0 equals -180 degrees.

To store latitude and longitude values, you need to convert them first to the Fixed data type, then to the Fract data type. You can use the Operating System Utilities routines Long2Fix and Fix2Fract to accomplish this task. Listing 4-8 is an application-defined procedure that converts San Francisco's latitude and longitude to Fract values, then writes the Fract values to parameter RAM using the WriteLocation procedure.

Listing 4-8 Converting latitude and longitude to Fract values

PROCEDURE MyConvertLatLong;
VAR
   myLatitude, myLongitude:         LongInt;
   fixedLatitude, fixedLongitude:   Fixed;
   latFract, longFract:             Fract;
   myLocation:                      MachineLocation;
BEGIN
   myLatitude:= 37.48;                    {degrees latitude}
   myLongitude:= 122.24;                  {degrees longitude}
   {convert from long to fixed data type}
   fixedLatitude:= Long2Fix(myLatitude);
   fixedLongitude:= Long2Fix(myLongitude);

   {convert from fixed to Fract data type}
   latFract:= Fix2Frac(fixedLatitude);    
   longFract:= Fix2Frac(fixedLongitude);

   {write latitude and logitude to myLocation}
   myLocation.latitude:= latFract;
   myLocation.longitude:= longFract;

   {write latitude and longitude to parameter RAM}
   WriteLocation(myLocation);             

END;
To read the latitude and longitude values from parameter RAM, you use the ReadLocation procedure. To convert these values to a degrees format, you need to convert the Fract values first to the Fixed data type, then to the LongInt data type. You can use the Mathematical and Logical Utilities routines Fract2Fix and Fix2Long to accomplish this task. (For more information on the Fract data type and the conversion routines Long2Fix, Fix2Fract, Fract2Fix, and Fix2Long, see the chapter "Mathematical and Logical Utilities" in this book.)

The gmtDelta field of the geographic location record is a 3-byte value contained in a long word, so you must take care to get and set it properly. Listing 4-9 shows an application-defined function for obtaining the value of gmtDelta.

Listing 4-9 Getting gmtDelta

FUNCTION MyGetGmtDelta (myLocation: MachineLocation): LongInt;
VAR
   internalGmtDelta: LongInt;
BEGIN
   WITH myLocation DO 
   BEGIN
      internalGmtDelta := BitAnd(gmtDelta, $00FFFFFF);
      IF BitTst(internalGmtDelta, 23) THEN   
                                       {test sign extend bit}
         internalGmtDelta := BitOr(internalGmtDelta, $FF000000);
      MyGetGmtDelta := internalGmtDelta;
   END;
END;
When writing gmtDelta, you should preserve the value of dlsDelta. Listing 4-10 shows an application-defined procedure that writes gmtDelta while preserving the value of dlsDelta.

Listing 4-10 Setting gmtDelta

PROCEDURE MySetGmtDelta (VAR myLocation: Location; 
                          myGmtDelta: LongInt);
VAR
   tempSignedByte: SignedByte;
BEGIN
   WITH myLocation DO 
   BEGIN
      tempSignedByte := dlsDelta;         {preserve dlsDelta}
      gmtDelta := myGmtDelta;             {write gmtDelta}
      dlsDelta := tempSignedByte;         {restore dlsDelta}
   END;
END;
Note that you should mask off the top byte of the long word containing gmtDelta because it is reserved.

Determining the Measurement System

To implement measuring devices in applications, such as rulers in a word processor or in drawing applications, you need to determine which measurement system your application should use. You can use the IsMetric function to determine if the measurement system needs to be the metric system or the English system. The IsMetric function reads the numeric-format resource (resource type 'itl0') of the current script system to determine whether the user is using the metric system or the English system.

Listing 4-11 shows an application-defined procedure that uses the result of the IsMetric function to determine which application-defined ruler setup to use for a document window.

Listing 4-11 Getting the current units of measurement

PROCEDURE DoRuler (window: WindowPtr);
VAR
   myMeasure: BOOLEAN;           {response returned by IsMetric}
BEGIN
   myMeasure := IsMetric;
   IF myMeasure = TRUE THEN      {metric system is default}
      DoMetricRulerSetup         {set up metric system ruler}
   ELSE 
      DoEnglishRulerSetup;       {set up English system ruler}
END;
If you want to use a measurement system different from that of the current script, you need to override the value of the metricSys field in the current numeric-format resource (resource type 'itl0'). You can do this by using your own version of the numeric-format resource instead of the current script system's default international resources. See the chapter "Script Manager" in Inside Macintosh: Text for information on how to replace a script system's default international resources.

Determining the Number of Elapsed Microseconds

Your application can use the Microseconds procedure to obtain the number of elapsed microseconds since system startup time. You can use the value returned by the Microseconds procedure to time an event. For example, Listing 4-11 shows an application-defined function MyEventTimer that computes and returns the time it takes to execute an application-defined procedure DoMyEvent. The application-defined function MyCalulateElapsedTime function uses the returned value of the Microseconds procedure to compute the time it takes to execute the DoMyEvent procedure.

Listing 4-12 Timing an event using the Microseconds procedure

FUNCTION MyEventTimer: UnsignedWide;
VAR
   myStartTime:UnsignedWide;
   myEndTime: UnsignedWide;
BEGIN
   Microseconds(&myStartTime);
   DoMyEvent;
   Microseconds(&myEndTime);
   MyEventTimer := MyComputeElapsedTime(&myStartTime, &myEndTime);
END;
Because there is no compiler support for 64-bit integers, you must write an application-defined routine that calculates the elapsed time; you cannot obtain the elapsed time by subtracting the value in the myStartTime parameter from the value in the myEndTime parameter.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
6 JUL 1996

Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2004 Apple Computer, Inc.
All rights reserved. | Terms of use | Privacy Notice