Important: The information in this document is obsolete and should not be used for new development.
Using the Date, Time, and Measurement Utilities
This section describes how to
- get the current date and time
- set the current date and time
- calculate days and dates mathematically
- convert between date-time formats
- convert to different calendar systems
- read and store geographic location and time-zone data
- determine which measurement system to use
- determine the number of elapsed microseconds
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.
- a function--
ReadDateTime
--that system software uses at system startup time to copy the current date-time information from the clock chip into low memory. This low-memory copy of the current date-time is accessible through the global variableTime
. You application should never need to use this function.- two procedures --
GetDateTime
andGetTime
--that allow you to access the current date-time information stored in the global variableTime
.
To obtain the current date-time information, you can use the
GetDateTime
andGetTime
procedures. TheGetDateTime
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 procedureMyCurrentDateTimeInt
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;TheGetTime
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 procedureMyCurrentDateTimeRec
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 variableTime
and in the clock chip by calling either theSetDateTime
function or theSetTime
procedure. TheSetDateTime
function requires a 32-bit integer as a parameter. TheSetTime
procedure requires a date-time record as a parameter.
Listing 4-3 shows an application-defined function that uses the
- 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.
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 theSetTime
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--theDateToSeconds
,SecondsToDate
,LongDateToSeconds
, andLongSecondsToDate
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
, andLongSecondsToDate
procedures provided by the current script system.
If you use a standard date-time value or a date-time record to access date-time information, you can use the
- 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.
SecondsToDate
procedure to convert a number of seconds to a date and time, and theDateToSeconds
procedure to convert a date and time to a number of seconds. Listing 4-5 shows an application-defined procedure,MyConvertSecondsAndDates
, that uses theSecondsToDate
andDateToSeconds
procedures to manipulate the date-time information. After calling theGetDateTime
procedure,MyConvertSecondsAndDates
calls theSecondsToDate
procedure to convert the number of seconds (returned by theGetDateTime
procedure) to a date and time. TheMyConvertSecondsAndDates
procedure manipulates theyear
field in the date-time record and then callsDateToSeconds
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 theLongSecondsToDate
procedure to convert a number of seconds to a date and time and use theLongDateToSeconds
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 access a number of seconds through a long date-time value instead of a standard date-time value, set the
lHigh
field of a long date-time conversion record (described on page 4-25) to 0 and thelLow
field to the total number of seconds since midnight, January 1, 1904. Then copy the value of thec
field into a variable of typeLongDateTime
.- To access a date and time through a long date-time record instead of a date-time record, set the
oldDate
field of theLongDateRec
to the date-time record, and set theeraAlt
field to 0, indicating that the date you have specified is A.D.- To access a number of seconds through a standard date-time value instead of a long date-time value, truncate the long date-time value to just the low-order 32 bits. The year of the date being converted must fall within 1904 to 2040 of the Gregorian calendar.
This type of conversion is important when you work with a script system that uses a calendar system other than the Gregorian. Because you cannot write a long date-time value to the clock chip, you must first convert the long date-time value (if possible) to a standard date-time value. See "Working With Different Calendar Systems" beginning on page 4-16 for more information about calendar systems.- To access a date and time through a date-time record instead of a long date-time record, truncate the long date-time record so just the
year
throughdayOfWeek
fields are left. Once again, the year of the date being converted must fall within 1904 to 2040 of the Gregorian calendar.- To access date-time information through a long date-time value instead of a date-time record, use the
DateToSeconds
procedure to convert the date and time to a number of seconds. Then set thelHigh
field of a long date-time conversion record (described on page 4-25) to 0 and thelLow
field to the total number of seconds since midnight, January 1, 1904.- To access date-time information through a long date-time record (described on page 4-26) instead of a standard date-time value, use the
SecondsToDate
procedure to translate the number of seconds to a date and time. Then set theoldDate
field of the long date-time record to the date-time record, and set theeraAlt
field to 0.- To access date-time information through a date-time value instead of long date-time record, use the
LongDateToSeconds
procedure to translate the date and time to a number of seconds. Then truncate the long date-time value (returned by theLongDateToSeconds
procedure) to just the low-order 32 bits. The year of the date being converted must fall within 1904 to 2040 in the Gregorian calendar.
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
, andLongTimeString
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 themonth
,day
,hour
,minute
, orsecond
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
- In the
month
field, values greater than 12 cause a wraparound to a future year and month.- In the
day
field, values greater than the number of days in a given month cause a wraparound to a future month and day.- In the
hour
field, values greater than 23 cause a wraparound to a future day and hour.- In the
minute
field, values greater than 59 cause a wraparound to a future hour and minute.- In the
seconds
field, values greater than 59 cause a wraparound to a future minute and seconds.
DateToSeconds
andSecondsToDate
procedures to calculate the 300th day of 1994. Set themonth
field of the date-time record to 1 and theyear
field to 1994. To find the 300th day of 1994, set theday
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 theSecondsToDate
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;TheDateToSeconds
procedure converts the date and time to the number of seconds elapsed since midnight, January 1, 1904, and theSecondsToDate
procedure converts the number of seconds back to a date and time. After the conversions, the values in theyear
,month
,day
, anddayOfWeek
fields of themyDateTimeRec
record represent the year, month, day of the month, and day of the week for the 300th day of 1994. If the values in thehour
,minute
, andsecond
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
andLongSecondsToDate
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;TheLongDateToSeconds
procedure converts the date and time to the number of seconds, and theLongSecondsToDate
procedure converts the number of seconds back to a date and time. After the conversions, the value in thedayOfWeek
field of themyLongDateRec
record represent the day of the week corresponding to July 4, 1776. If the values in thehour
,minute
, andsecond
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 thedayOfYear
,weekOfYear
, andpm
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 theyear
field's range in the date-time record.
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.
- 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.)
Converting from one calendar system to another produces different values in the
dayOfYear
andweekOfYear
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, thedayOfYear
field value is 1, and theweekOfYear
value is also 1. Converting this date to the Gregorian calendar results in June 20, 1993. ThedayOfYear
field value is then 171, and theweekOfYear
value is 26. Table 4-2 shows these values.
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.
- 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.
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
- 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.
'itl1'
resource, defined by theItl1ExtRec
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
andWriteLocation
procedures. These procedures use the geographic location record (of date typeMachineLocation
) 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 thehour
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, theFract
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 theFract
data type. You can use the Operating System Utilities routinesLong2Fix
andFix2Fract
to accomplish this task. Listing 4-8 is an application-defined procedure that converts San Francisco's latitude and longitude toFract
values, then writes theFract
values to parameter RAM using theWriteLocation
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 theReadLocation
procedure. To convert these values to a degrees format, you need to convert theFract
values first to theFixed
data type, then to theLongInt
data type. You can use the Mathematical and Logical Utilities routinesFract2Fix
andFix2Long
to accomplish this task. (For more information on theFract
data type and the conversion routinesLong2Fix
,Fix2Fract
,Fract2Fix
, andFix2Long
, 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 ofgmtDelta
.
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 writinggmtDelta
, you should preserve the value ofdlsDelta
. Listing 4-10 shows an application-defined procedure that writesgmtDelta
while preserving the value ofdlsDelta
.
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 containinggmtDelta
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 theIsMetric
function to determine if the measurement system needs to be the metric system or the English system. TheIsMetric
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 themetricSys
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 theMicroseconds
procedure to obtain the number of elapsed microseconds since system startup time. You can use the value returned by theMicroseconds
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 procedureDoMyEvent
. The application-defined functionMyCalulateElapsedTime
function uses the returned value of theMicroseconds
procedure to compute the time it takes to execute theDoMyEvent
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 themyStartTime
parameter from the value in themyEndTime
parameter.