Technical Notes
OverviewStructure format:
typedef struct {
unsigned char day; /* day (1-31) */
unsigned char month; /* month (1-12) */
unsigned int year; /* year (1980-2099) */
unsigned char day_of_week; /* day-of-week (0-6) */
} std_date;
Integer format: unsigned int bits 0x09-0x0F year (0-119, number of years since 1980) bits 0x05-0x08 month (1-12) bits 0x00-0x04 day (1-31) Julian format: unsigned int days elapsed since 01-01-1980Note that dates in the Julian or integer formats can be compared directly to determine relative order. Julian dates may also be subtracted or adjusted directly. The std_date structure is defined in the DATETIME.H header file. All of these date formats support dates from 01-01-1980 through 12-31-2099.
Structure format:
typedef struct {
unsigned char hseconds; /* hundredths of seconds (0-99) */
unsigned char seconds; /* seconds (0-59) */
unsigned char minutes; /* minutes (0-31) */
unsigned char hours; /* hours (0-255) */
} std_time;
Integer format: unsigned int bits 0x0B-0x0F hours (0-23) bits 0x05-0x0A minutes (0-59) bits 0x00-0x04 2-second increments (0-29) Hundredths of seconds: unsigned long hundredths of seconds elapsed since midnight (0-92159999) Timer ticks: unsigned long number of timer ticks elapsed since midnight (0-1573039)Note that times in all formats except the structure format can be compared directly to determine relative order. Hundredths or timer ticks may also be subtracted or adjusted directly. Times in the structure format can be added to or subtracted from other times in the same format using functions in this unit. The std_time structure is defined in the DATETIME.H header file.
_ticks_to_time and _time_to_ticks, when used in conjunction with _get_clock and _set_clock, are faster than the _get_time and _set_time functions, respectively. _get_time and _set_time should be used when code size is more important than speed.
dtc_addr is initialized with the address of the default dtcopts structure. The dtcopts structure is defined in DATETIME.H, as follows:
typedef struct {
char dtc_dtformat; /* date format MDY/DMY/YMD (country) */
char dtc_dtdelim; /* date delimiter (country) */
char dtc_tmformat /* time format 12/24-hour (country) */
char dtc_tmdelim; /* time delimiter (country) */
char dtc_hdelim; /* delimiter for 100ths (default country decimal delimiter) */
char *dtc_amstr; /* address of AM string (default string is " AM") */
char *dtc_pmstr; /* address of PM string (default string is " PM") */
} dtcopts;
The default dtcopts structure (in the library) is supplied with standard U.S.A. defaults. The DTC_DEFAULT symbolic constant (defined in DATETIME.H) may be specified for any field which accepts a pointer. This causes the date/time conversion functions to use the default string, shown in the comments above, for each field in which it is specified.
The DTCOPTS structure fields are defined as follows:
dtc_dtformat DOS Date format. The default is U.S. format (0). This field is updated by _dtc_init. DOS is documented as supporting the following formats:
0 = U.S. (MM/DD/YY) 1 = European (DD/MM/YY) 2 = Japanese (YY/MM/DD)
0 = 12-hour format (HH:MM:SS.hh AM/PM) 1 = 24-hour format (HH:MM:SS.hh)
The format of the resulting date or time string depends on the current dtcopts structure as well as date and time formatting flags which are specified for each conversion. The current date/time conversion options structure specifies global formatting options such as the country-default time format, the time delimiter character, and the 100ths separator character. These options may be accessed or modified through the dtc_addr variable. (See dtc_addr, above, for further information.) Date and time formatting flags are specified using the following symbolic constants (defined in DATETIME.H):
Date field order flags:
Time format flags:
Only those subfields specified in the formatting flags are actually read and stored; the remaining fields are neither read nor stored. This means that only the specified portions of the destination structure are modified on a successful read. It also means that the input value must be in the specified format or the field values may be interpreted incorrectly.
An invalid format error is returned if fewer fields were read than expected, a required field is zero or empty, or no interpretable characters were found in a field. For example, if three date fields are expected when calling ASC_TO_DATE, then "//92", "2/A/92", or "11/12" would generate invalid format errors. An overflow error condition is also returned if numeric overflow occurs in any required field (i.e., the read value exceeds the maximum allowed value for that field).
If a date or time string contains more fields than are required for the read, the leading fields must match the specified format and trailing fields are ignored. For example, if DATE_MDY+DATE_NOYR were specified, then "12/31" and "12/31/95" would both be read correctly. If DATE_MDY+DATE_NODAY were specified (only month and year expected), an input string of "12/31" would be interpreted as representing month 12 of the year 2031.
Year values in date strings are read in whatever format is encountered (2-digit or 4-digit) regardless of the format specified in the date format flags. When 2-digit year values are converted, values from 80 to 99 are assumed to represent the years 1980 through 1999; other values represent years from 2000 to 2079.
The time format (12- or 24-hour) specified in the format flags is significant. If 12-hour format is specified, then the scripted input function requires a am/pm indicator after the time. This indicator must match the am/pm strings specified in the current date/time conversion options structure, or it must match "a", "p", "am", or "pm" (case-insensitive comparison) with at most one space character between the last digit of the time and the am/pm indicator. An invalid format error is returned if no am/pm indicator is found when 12-hour time format has been specified. This error is also returned if TIME_FLEXHR is specified, the hour value is greater than 12, and a trailing am/pm string is encountered. If 24-hour format is specified, then no attempt is made to match am/pm strings.
These functions do not automatically skip leading white space characters. To skip white space characters, call _str_skipw or _str_skips before calling these formatting functions.
_tmr_reset and _tmr_read measure elapsed time. _tmr_reset establishes the starting point from which elapsed time is measured. Elapsed time may then be read at any time by calling _tmr_read. The timing sequence is not interrupted when _tmr_read is called. Subsequent calls to _tmr_read continue to report elapsed time from the initial starting point. _tmr_reset may be called at any time to establish a new starting point for the timing sequence. Interrupts must be enabled for these functions to work properly. These functions only support timing sequences of less than 24 hours. Longer sequences may be tracked by periodically calling _tmr_read, adding the elapsed time into the total elapsed time, and then calling _tmr_reset to establish a new starting point.
_tmr_reset and _tmr_read depend on the system clock. This means that they have a maximum resolution of .0549 seconds with an accuracy of ñ .0549 seconds.
_sleep_sec and _sleep_msec suspend execution for a specified period of time. _sleep_sec is a simple function which has a resolution of 1 clock tick (.0549 seconds) with an effective accuracy of ñ .0549 seconds. _sleep_msec is about four times as large as _sleep_sec, but it has millisecond resolution (.001 seconds) with an accuracy of better than ñ .0005 seconds. Since _sleep_sec depends on the system clock, interrupts must be enabled when it is called. _sleep_msec, on the other hand, depends on the "millisecond timer", which measures elapsed time by continuously monitoring system timer zero. Therefore, _sleep_msec may be used with interrupts disabled to obtain even greater accuracy.
_init_msec is provided for use with _sleep_msec and other functions which use the millisecond timer. _init_msec calibrates the millisecond timer by determining the operating mode and reload count of system timer zero. This allows _sleep_msec and other millisecond timer functions to operate properly even if the system timer has been reprogrammed. _init_msec should always be called after reprogramming timer zero. If _init_msec is never called, _sleep_msec assumes that timer zero is operating in the system default mode (mode three with a reload value of zero).
#include
#include
#include
#define CR 13
void main()
{
char *strindx;
char day, datebuf[5];
unsigned num_days, curr_days, future_days;
static std_date date;
_get_date(&date);
curr_days = _date_to_days(&date); /* get current number of days since 01-01-1980 */
do
{
_put_newline();
do {
_put_str("Enter month: ");
date.month = _dec_to_c(_get_str(datebuf, 2), &strindx);
} while (date.month < 1 || date.month > 12);
do {
_put_str("Enter day: ");
date.day = _dec_to_c(_get_str(datebuf, 2), &strindx);
} while (date.day < 1 || date.day > 31);
do {
_put_str("Enter year: ");
date.year = _dec_to_i(_get_str(datebuf, 4), &strindx);
} while (date.year < 1980 || date.year > 2080);
future_days = _date_to_days(&date); /* number of days from 01-01-1980 to ?date */
num_days = future_days - curr_days; /* num_days = days remaining until ?date */
_put_newline();
_put_str("Number of days to ");
_days_to_date(future_days,&date); /* convert to actual date (June 31 = July 1) */
_put_str(_asc_date(datebuf,&date)); _put_str(" = ");
_put_str(_i_to_dec(num_days, datebuf)); /* display number of days to ?date */
_put_newline();
_put_str("The day is: ");
switch (_get_day(&date))
{
case 0: _put_str("Sunday"); break;
case 1: _put_str("Monday"); break;
case 2: _put_str("Tuesday"); break;
case 3: _put_str("Wednesday"); break;
case 4: _put_str("Thursday"); break;
case 5: _put_str("Friday"); break;
case 6: _put_str("Saturday"); break;
default: break;
}
_put_str("\n\n\r to continue: ");
} while(_get_chre() == CR);
}
The sample program shown above (CALCDAY.C) is provided on the distribution diskettes and may be compiled and linked using the following Microsoft C and Borland C command lines:
Microsoft C:
cl /c /I\msc\include /I\sa\include calcday.c
link calcday,calcday,,\sa\lib\_sas \msc\lib\slibce
Borland C:
bcc /c /ms /I\bc\include /I\sa\include calcday.c
tlink \bc\lib\c0s calcday,calcday,,\sa\lib\_sas \bc\lib\cs