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 --
GetDateTimeandGetTime--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
GetDateTimeandGetTimeprocedures. TheGetDateTimeprocedure 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 procedureMyCurrentDateTimeIntreturns 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;TheGetTimeprocedure 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 procedureMyCurrentDateTimeRecreturns 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 variableTimeand in the clock chip by calling either theSetDateTimefunction or theSetTimeprocedure. TheSetDateTimefunction requires a 32-bit integer as a parameter. TheSetTimeprocedure 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.
![]()
SetDateTimefunction 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 theSetTimefunction 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
Timeand 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, andLongSecondsToDateprocedures--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, andLongSecondsToDateprocedures 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.
![]()
SecondsToDateprocedure to convert a number of seconds to a date and time, and theDateToSecondsprocedure to convert a date and time to a number of seconds. Listing 4-5 shows an application-defined procedure,MyConvertSecondsAndDates, that uses theSecondsToDateandDateToSecondsprocedures to manipulate the date-time information. After calling theGetDateTimeprocedure,MyConvertSecondsAndDatescalls theSecondsToDateprocedure to convert the number of seconds (returned by theGetDateTimeprocedure) to a date and time. TheMyConvertSecondsAndDatesprocedure manipulates theyearfield in the date-time record and then callsDateToSecondsto 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 theLongSecondsToDateprocedure to convert a number of seconds to a date and time and use theLongDateToSecondsprocedure 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
lHighfield of a long date-time conversion record (described on page 4-25) to 0 and thelLowfield to the total number of seconds since midnight, January 1, 1904. Then copy the value of thecfield 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
oldDatefield of theLongDateRecto the date-time record, and set theeraAltfield 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
yearthroughdayOfWeekfields 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
DateToSecondsprocedure to convert the date and time to a number of seconds. Then set thelHighfield of a long date-time conversion record (described on page 4-25) to 0 and thelLowfield 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
SecondsToDateprocedure to translate the number of seconds to a date and time. Then set theoldDatefield of the long date-time record to the date-time record, and set theeraAltfield to 0.- To access date-time information through a date-time value instead of long date-time record, use the
LongDateToSecondsprocedure to translate the date and time to a number of seconds. Then truncate the long date-time value (returned by theLongDateToSecondsprocedure) 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, andLongTimeStringroutines. (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, orsecondfield 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
monthfield, values greater than 12 cause a wraparound to a future year and month.- In the
dayfield, values greater than the number of days in a given month cause a wraparound to a future month and day.- In the
hourfield, values greater than 23 cause a wraparound to a future day and hour.- In the
minutefield, values greater than 59 cause a wraparound to a future hour and minute.- In the
secondsfield, values greater than 59 cause a wraparound to a future minute and seconds.
DateToSecondsandSecondsToDateprocedures to calculate the 300th day of 1994. Set themonthfield of the date-time record to 1 and theyearfield to 1994. To find the 300th day of 1994, set thedayfield 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
DateToSecondsprocedure). 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 theSecondsToDateprocedure). 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;TheDateToSecondsprocedure converts the date and time to the number of seconds elapsed since midnight, January 1, 1904, and theSecondsToDateprocedure converts the number of seconds back to a date and time. After the conversions, the values in theyear,month,day, anddayOfWeekfields of themyDateTimeRecrecord 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, andsecondfields 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
LongDateToSecondsandLongSecondsToDateprocedures 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;TheLongDateToSecondsprocedure converts the date and time to the number of seconds, and theLongSecondsToDateprocedure converts the number of seconds back to a date and time. After the conversions, the value in thedayOfWeekfield of themyLongDateRecrecord represent the day of the week corresponding to July 4, 1776. If the values in thehour,minute, andsecondfields 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, andpmfields 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 theyearfield'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
dayOfYearandweekOfYearfields 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, thedayOfYearfield value is 1, and theweekOfYearvalue is also 1. Converting this date to the Gregorian calendar results in June 20, 1993. ThedayOfYearfield value is then 171, and theweekOfYearvalue 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 theItl1ExtRecdata 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
ReadLocationandWriteLocationprocedures. 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 thehourfield--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, theFractvalue 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
Fixeddata type, then to theFractdata type. You can use the Operating System Utilities routinesLong2FixandFix2Fractto accomplish this task. Listing 4-8 is an application-defined procedure that converts San Francisco's latitude and longitude toFractvalues, then writes theFractvalues to parameter RAM using theWriteLocationprocedure.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 theReadLocationprocedure. To convert these values to a degrees format, you need to convert theFractvalues first to theFixeddata type, then to theLongIntdata type. You can use the Mathematical and Logical Utilities routinesFract2FixandFix2Longto accomplish this task. (For more information on theFractdata type and the conversion routinesLong2Fix,Fix2Fract,Fract2Fix, andFix2Long, see the chapter "Mathematical and Logical Utilities" in this book.)The
gmtDeltafield 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 writesgmtDeltawhile 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 containinggmtDeltabecause 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 theIsMetricfunction to determine if the measurement system needs to be the metric system or the English system. TheIsMetricfunction 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
IsMetricfunction 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 themetricSysfield 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 theMicrosecondsprocedure to obtain the number of elapsed microseconds since system startup time. You can use the value returned by theMicrosecondsprocedure 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 functionMyCalulateElapsedTimefunction uses the returned value of theMicrosecondsprocedure to compute the time it takes to execute theDoMyEventprocedure.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 themyStartTimeparameter from the value in themyEndTimeparameter.