/* +INCOMPLETE -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 */ /******************************************************************************* * c0xcalendar.c * Proposed ISO/IEC C (C0X) calendar types and functions. * * This source file implements several proposed calendar functions as a * proof of concept; most of the algorithms used are for illustration only * and are not meant for production-quality systems. * * Functions that are implemented: * _calendardiff() * _calendarformat() * _getcalendar() * _getcalendarinfo() * _getcalendartime() * _getcalendartype() * _setcalendartime() * _setcalendarzone() * * Functions that are not implemented: * _calendarscanf() * _checkcalendar() * * Notes * The following preprocessor macro definitions must be modified to suit * your runtime environment: * * _GREGORIAN_TIME_MAX * _GREGORIAN_TIME_MIN * * Unmodified, this header file assumes by default that the 'time_t' is * defined according to POSIX specifications, i.e., that 'time_t' is type * 'signed int', that only positive time values are valid, and that * system times are a linear count of seconds since 1970-01-01 00:00:00 Z. * It is also assumed that type 'signed int' is either 32 or 64 bits wide. * * These functions are implemented on top of the date/time functions that * exist for struct 'tm' in the standard ISO C library. A quality * implementation would most likely not be implemented in this fashion. * * This code is written to conform to the ISO C89 standard, rather than * C99, since most C compilers as of this writing do not conform to the * ISO C99 standard. * * References * See . * * See also * c0xcalendar.h * c0xtimezone.c * *------------------------------------------------------------------------------- * History * 0.1, 2002-01-24, David R Tribble. * First attempt. Some functions work. * * 0.2, 2002-01-28, David R Tribble. * Some more functions work. * * 0.3, 2002-02-07, David R Tribble. * Changed member '_timezone.tz_sec_west' to '.tz_min_west'. * Added more code for '_calendardiff()'. * * 0.4, 2002-02-12, David R Tribble. * Changed the function signature of '_setcalendarzone()'. * * 0.5, 2002-02-17, David R Tribble. * Defined some constants as non-macros. * Removed 'xxx_vers' struct members and 'XXX_VERS' constants. * * 0.6, 2002-03-05, David R Tribble. * Added 'ci_time_min' and 'ci_time_max' members to '_calendarinfo'. * Added '_XXX_VS' version macros. * Added 'restrict' qualifiers to function prototypes. * Added the '_CAL_ERA_COMMON' constant. *******************************************************************************/ /* Identification */ static const char rev[] = "@(#)$Header: c0xcalendar.c 0.6 2002-03-05 $"; /* Standard includes */ #include #include #include #include #include #include #include /* Local keywords */ /* Note: This does not seem to work for Microsoft C/C++ Compiler (v11) */ #if __STDC_VERSION__ < 199901 #define restrict #endif /* Local includes */ #include "c0xcalendar.h" /******************************************************************************* * Public constants *******************************************************************************/ const char _cal_name_gregorian[] = "Gregorian"; const char _tz_local[] = ""; const char _tz_utc[] = "UTC"; /******************************************************************************* * Shared constants *******************************************************************************/ extern const struct _timezone _cal_zones[]; /* See "c0xtimezone.c" */ /******************************************************************************* * Private constants *******************************************************************************/ #define _GREGORIAN_YEAR_MIN 1601 #define _GREGORIAN_YEAR_MAX 3200 #ifndef _GREGORIAN_TIME_MIN #define _GREGORIAN_TIME_MIN ((time_t)0) #endif #ifndef _GREGORIAN_TIME_MAX #if INT_MAX == 0x7FFFFFFF #define _GREGORIAN_TIME_MAX ((time_t)INT_MAX) #elif INT_MAX > 0x7FFFFFFF #define _GREGORIAN_TIME_MAX\ ((time_t)(_GREGORIAN_YEAR_MAX+1-1900) * 3652425/10000 * 86400 - 1) #else #error Cannot determine a default value for _GREGORIAN_TIME_MAX #endif #endif /*------------------------------------------------------------------------------ * _cal_info[] * Array of calendar information objects, one for each supported calendric * system. */ static const struct _calendarinfo _cal_info[] = { #if _CALENDARINFO_VS > 20020223 #error struct _calendarinfo has changed #endif /* [0] */ { _CAL_TYPE_GREGORIAN, /* ci_type */ _cal_name_gregorian, /* ci_name */ _GREGORIAN_TIME_MIN, /* ci_time_min */ _GREGORIAN_TIME_MAX, /* ci_time_max */ 1, /* ci_era_max */ _GREGORIAN_YEAR_MIN, /* ci_year_min */ _GREGORIAN_YEAR_MAX, /* ci_year_max */ 1, /* ci_mon_min */ 12, /* ci_mon_max */ 1-1, /* ci_week_min */ 52+1, /* ci_week_max */ 1, /* ci_mday_min */ 31, /* ci_mday_max */ 1, /* ci_yday_min */ 365+1, /* ci_yday_max */ 1, /* ci_wday_min */ 7, /* ci_wday_max */ 4, /* ci_wday_1st */ 0, /* ci_hour_min */ 24-1, /* ci_hour_max */ 60-1, /* ci_min_max */ 60-1, /* ci_sec_max */ 0, /* ci_leap_sec */ }, }; static const int ncal_info = /* Size of _cal_info[] array */ sizeof(_cal_info)/sizeof(_cal_info[0]); /*------------------------------------------------------------------------------ * cal_init * A default Gregorian calendar object value, used to initialize calendar * objects. */ static const struct _calendar cal_init = { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif _CAL_TYPE_GREGORIAN, /* cal_type */ { &_cal_zones[0], /* cal_zone[0] */ &_cal_zones[0], /* cal_zone[1] */ }, _CAL_ERA_COMMON, /* cal_era */ _CAL_YR_ERROR, /* cal_year */ 0, /* cal_mon */ 0, /* cal_mday */ 0, /* cal_yday */ 0, /* cal_wday */ 0, /* cal_week */ 0, /* cal_hour */ 0, /* cal_min */ 0, /* cal_sec */ 0, /* cal_nsec */ 0, /* cal_isdst */ 0, /* cal_leap_yr */ }; static const short months[] = { 31, /* Jan */ 28, /* Feb */ 31, /* Mar */ 30, /* Apr */ 31, /* May */ 30, /* Jun */ 31, /* Jul */ 30, /* Aug */ 30, /* Sep */ 31, /* Oct */ 30, /* Nov */ 31, /* Dec */ }; /******************************************************************************* * Private functions *******************************************************************************/ /*------------------------------------------------------------------------------ * reset_calendar() * Initializes the members of a calendar object to default values. * Note: This does not alter the 'cal_zone[*]' members. * * Parameter cal * Points to a calendar object to be initialized. */ static void reset_calendar(struct _calendar *cal) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif const struct _timezone * tz0; const struct _timezone * tz1; /* Save the timezone settings */ tz0 = cal->cal_zone[0]; tz1 = cal->cal_zone[1]; /* Initialize the calendar object */ memcpy(cal, &cal_init, sizeof(*cal)); /* Restore the timezone settings */ cal->cal_zone[0] = tz0; cal->cal_zone[1] = tz1; } /*------------------------------------------------------------------------------ * tm_to_cal() * Converts a 'tm' structure into a '_calendar' structure. * Note: This does not alter the 'cal_zone[*]' members. * * Parameter cal * Points to a calendar object to be initialized. * * Parameter ts * Points to a tm object. */ static void tm_to_cal(struct _calendar *cal, const struct tm *ts) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif int yr; int wday; int dst; /* Fill the contents of 'cal' from 'ts' */ yr = ts->tm_year+1900; cal->cal_year = yr; cal->cal_mon = ts->tm_mon+1; cal->cal_mday = ts->tm_mday; cal->cal_yday = ts->tm_yday+1; wday = (ts->tm_wday != 0 ? ts->tm_wday : 7); cal->cal_wday = wday; cal->cal_week = (ts->tm_yday - wday + 7+4)/7; cal->cal_hour = ts->tm_hour; cal->cal_min = ts->tm_min; cal->cal_sec = ts->tm_sec; cal->cal_nsec = 0; dst = ts->tm_isdst; cal->cal_isdst = (dst < 0 ? -1 : dst > 0 ? 1 : 0); cal->cal_era = (cal->cal_year > 0 ? _CAL_ERA_COMMON : 1); cal->cal_leap_yr = (yr%400 == 0 or yr%100 != 0 and yr%4 == 0); } /*------------------------------------------------------------------------------ * cal_to_tm() * Converts a '_calendar' structure into a 'tm' structure. * * Parameter ts * Points to a tm object to be initialized. * * Parameter cal * Points to a calendar object. */ static void cal_to_tm(struct tm *ts, const struct _calendar *cal) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif int dst; int dstp; int west; /* Fill the contents of 'ts' from 'cal' */ ts->tm_year = cal->cal_year-1900; ts->tm_mon = cal->cal_mon-1; ts->tm_mday = cal->cal_mday; ts->tm_yday = cal->cal_yday-1; ts->tm_wday = cal->cal_wday%7; ts->tm_hour = cal->cal_hour; ts->tm_min = cal->cal_min; ts->tm_sec = cal->cal_sec; dst = cal->cal_isdst; dstp = (dst > 0 ? 1 : 0); west = 0; if (cal->cal_zone[dstp] != NULL) west = cal->cal_zone[dstp]->tz_min_west; ts->tm_isdst = (dst < 0 ? -1 : dst > 0 ? west : 0); } /*------------------------------------------------------------------------------ * gregorian_getcal() * Initialize a calendar object with default values for a Gregorian * calendar and a specified timezone. * * Parameter cal * Points to a calendar object to be initialized. * * Parameter info * Points to a Gregorian calendar information object. * * Parameter tz * A two-element array pointing to two timezone objects, the first without * DST in effect, and the second with DST rules in effect. The pointers * may be null, but 'tz' itself must not be null. * * Returns * Zero on success, otherwise a nonzero value on error (e.g., an invalid * argument, or no such calendar or timezone with a matching name was * found). */ static int gregorian_getcal(struct _calendar *cal, const struct _calendarinfo *info, const struct _timezone *tz[2]) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif #if DEBUG printf("$ gregorian_getcal(cal=%08p, info=%08p," " tz=%08p{[0]=%08p,[1]=%08p})\n", cal, info, tz, (tz != NULL ? tz[0] : NULL), (tz != NULL ? tz[1] : NULL)); #endif /* Set the calendar members */ memcpy(cal, &cal_init, sizeof(*cal)); cal->cal_type = info->ci_type; cal->cal_zone[0] = tz[0]; cal->cal_zone[1] = tz[1]; /* Done */ return (0); } /*------------------------------------------------------------------------------ * gregorian_settime() * Establishes a new date for a Gregorian calendar date object by * converting a system time to its equivalent Gregorian calendar date * value. * * Parameter cal * Points to a Gregorian calendar date object, whose members will be set to * correspond to the system time value 't'. * * Parameter t * A system time to convert. * * Returns * Zero on success, otherwise a nonzero value on error. */ static int gregorian_settime(struct _calendar *cal, time_t t) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif struct tm ts; int dst; int tzoff; const struct _timezone * zones[2]; #if DEBUG printf("$ gregorian_settime(cal=%08p, t=%ld)\n", cal, (long)t); #endif /* Check args */ if (cal == NULL) return (-1); if (t < _GREGORIAN_TIME_MIN or t > _GREGORIAN_TIME_MAX) return (-1); /* Convert 't' into a broken-down time (relative to GMT) */ ts = *gmtime(&t); /* Retrieve the timezone settings */ zones[0] = cal->cal_zone[0]; if (zones[0] == NULL) zones[0] = &_cal_zones[0]; zones[1] = cal->cal_zone[1]; if (zones[1] == NULL) zones[1] = &_cal_zones[0]; /* Apply the calendar's timezone setting */ dst = (ts.tm_isdst > 0 ? 1 : 0); tzoff = zones[dst]->tz_min_west; ts.tm_hour -= (int)(tzoff/60); ts.tm_min -= (int)(tzoff%60); #if DEBUG printf("$$ tzoff=%d, tm_hour=%+02d tm_min=%+02d\n", tzoff, ts.tm_hour, ts.tm_min); #endif /* Normalize the broken-down time members */ mktime(&ts); /* Update the calendar object */ tm_to_cal(cal, &ts); /* Done */ return (0); } /*------------------------------------------------------------------------------ * gregorian_gettime() * Converts a Gregorian calendar date into its equivalent system time * value, after normalizing its members to fall within their normal value * ranges. * * Parameter cal * Points to a Gregorian calendar date object, whose members are * normalized. * * Parameter t * Points to a system time value to set. * * Returns * Zero on success, otherwise a nonzero value on error. */ static int gregorian_gettime(struct _calendar *cal, time_t *t) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif struct tm ts; time_t tv; /* Check args */ if (cal == NULL) return (-1); /* Check special cases */ if (cal->cal_year == _CAL_YR_ERROR) return (-1); if (cal->cal_year < _GREGORIAN_YEAR_MIN or cal->cal_year > _GREGORIAN_YEAR_MAX) return (-1); /* Convert the broken-down date into a system time */ memset(&ts, '\0', sizeof(ts)); ts.tm_year = cal->cal_year-1900; ts.tm_mon = cal->cal_mon-1; ts.tm_mday = cal->cal_mday; ts.tm_hour = cal->cal_hour; ts.tm_min = cal->cal_min; ts.tm_sec = cal->cal_sec; ts.tm_isdst = 0; /* (cal->cal_isdst > 0 ? 1 : 0) ? */ #if DEBUG printf("$ gettime: %+04d:%+02d:%+02d %+02d:%+02d:%+02d\n", ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec); #endif /* Normalize and convert */ tv = mktime(&ts); #if DEBUG printf("$ gettime: %+ld\n", (long)tv); #endif if (tv == (time_t)(-1)) return (-1); /* Update the broken-down date with normalized values */ tm_to_cal(cal, &ts); /* Set the converted system time value */ if (t != NULL) *t = tv; return (0); } /*------------------------------------------------------------------------------ * gregorian_check() * Determines if a calendar object represents a valid date and time. * * Parameter cal * Points to a calendar object. * * Returns * Zero if the calendar object represents a valid date and time, otherwise * a nonzero value. */ static int gregorian_check(const struct _calendar *cal) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif /*+INCOMPLETE, use months[] and cal_init */ fprintf(stderr, "Not implemented: gregorian_check()\n"); return (-1); } /*------------------------------------------------------------------------------ * gregorian_setzone() * Establishes a new timezone setting for a calendar date. * * First undoes the effects of the last timezone that was applied to the * calendar date object, then applies the effects of the specified timezone * to it. * * Parameter cal * Points to the calendar date object for which to establish a new timezone * setting. * * Parameter tz * Points to a two-element array of pointers to timezone structures to * apply to the calendar date object. These may be null, in which case all * timezone effects are removed from the calendar date object. (Note that * 'tz' itself must not be null.) * * Returns * Zero on success, otherwise a nonzero value on error. */ static int gregorian_setzone(struct _calendar *cal, const struct _timezone *tz[2]) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif #if _TIMEZONE_VS > 20020223 #error struct _timezone has changed #endif int dst; time_t t; const struct _timezone * ozones[2]; #if DEBUG printf("$ gregorian_setzone(cal=%08p, tz=%08p)\n", cal, tz); #endif /* Retrieve the old timezone settings */ ozones[0] = cal->cal_zone[0]; if (ozones[0] == NULL) ozones[0] = &_cal_zones[0]; ozones[1] = cal->cal_zone[1]; if (ozones[1] == NULL) ozones[1] = &_cal_zones[0]; /* Undo the effects of the current timezone setting */ if (cal->cal_year != _CAL_YR_ERROR) { dst = cal->cal_isdst; dst = (dst > 0 ? 1 : 0); dst = ozones[dst]->tz_min_west; cal->cal_min += dst/60; dst = dst%60; cal->cal_sec += dst; } cal->cal_zone[0] = NULL; cal->cal_zone[1] = NULL; /* Normalize the adjusted calendar date */ if (cal->cal_year != _CAL_YR_ERROR) { if (gregorian_gettime(cal, &t) != 0) return (-1); } /* Apply the effects of the new timezone */ if (cal->cal_year != _CAL_YR_ERROR) { dst = cal->cal_isdst; dst = (dst > 0 ? 1 : 0); dst = tz[dst]->tz_min_west; cal->cal_min -= dst/60; dst = dst%60; cal->cal_sec -= dst; } cal->cal_zone[0] = tz[0]; cal->cal_zone[1] = tz[1]; /* Reset and normalize the new calendar date */ if (cal->cal_year != _CAL_YR_ERROR) return (gregorian_gettime(cal, &t)); else return (0); } /*------------------------------------------------------------------------------ * gregorian_diff() * Computes the difference between broken-down calendar date 'a' and date * 'b', placing the result into the broken-down calendar date value 'dif'. * The result is such that adding each member of the broken-down date 'dif' * to date 'a' and then normalizing would result in a date equal to the * normalized value of date 'b'. * * Parameter dif * Pointer to a calendar object, which will be filled with the computed * difference between dates 'a' and 'b'. This pointer can be null. * * Parameter a * Pointer to the first calendar object. * * Parameter b * Pointer to the second calendar object. * * Returns * Zero (0) if dates 'a' and 'b' are the same, or * -1 if date 'a' comes before date 'b', or * +1 if date 'a' comes after date 'b', or * a negative value (other than -1) if date 'a' or 'b' is an invalid date * or some other error occurs. */ static int gregorian_diff(struct _calendar */*restrict*/ dif, const struct _calendar *a, const struct _calendar *b) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif struct _calendar dif2; /* Check args */ if (a == NULL or b == NULL) return (INT_MIN); if (a->cal_type != _CAL_TYPE_GREGORIAN or b->cal_type != _CAL_TYPE_GREGORIAN) return (INT_MIN); /* Check for special cases */ if (a->cal_year == _CAL_YR_ERROR or b->cal_year == _CAL_YR_ERROR) return (INT_MIN); /* Initialize the difference object */ if (dif == NULL) dif = &dif2; /* Use a dummy difference object */ memcpy(dif, &cal_init, sizeof(*dif)); /*+INCOMPLETE */ fprintf(stderr, "Not implemented: gregorian_diff()\n"); return (INT_MIN); } /*------------------------------------------------------------------------------ * gregorian_format() * Converts a calendar date into a string, formatting it according to a * format specification control string. * * Parameter buf * Points to a string buffer to be formatted. Must not be null. * * Parameter max * Specifies the maximum number of characters (including a terminating '\0' * null character) that can be written into string buffer 'buf'. * * Parameter fmt * Points to a control string that specifies how the target buffer 'buf' is * to be formatted. Format date specifier sequences start with "%", * optionally followed by a sequence of decimal digits that specify the * width of the date field, followed by one or two format type characters. +INCOMPLETE * ... * * Parameter cal * Points to a calendar date object. Must not be null. * * Returns * The number of characters written into string 'buf' if successful, * otherwise a negative value representing the negative index of the first * character within the format control string 'fmt' that caused an error. */ static int gregorian_format(char */*restrict*/ buf, size_t max, const char *fmt, const struct _calendar *cal) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif int fi; int bi; struct tm ts; const struct _timezone * zones[2]; #if DEBUG printf("$ gregorian_format(buf=%08p, max=%lu, fmt=%08p=\"%.1000s\"," " cal=%08p)\n", buf, (unsigned long)max, fmt, (fmt != NULL ? fmt : ""), cal); #endif /* Check args */ if (cal == NULL) return (-1); if (buf == NULL) return (-1); if (fmt == NULL) return (0); if (max == 0) return (0); /* Check for special dates */ if (cal->cal_year == _CAL_YR_ERROR) return (-1); /* Convert 'cal' into 'ts' */ cal_to_tm(&ts, cal); zones[0] = cal->cal_zone[0]; if (zones[0] == NULL) zones[0] = &_cal_zones[0]; /* Kludge */ zones[1] = cal->cal_zone[1]; if (zones[1] == NULL) zones[1] = &_cal_zones[0]; /* Kludge */ /* Parse the formatting string */ for (fi = 0, bi = 0; fmt[fi] != '\0' and bi < max; fi++) { #if DEBUG printf("$ '%c'\n", fmt[fi]); #endif if (fmt[fi] == '%') { int j; int k; int width; char fillch; int dst; char spec[7+1]; char res[80+1]; /* Parse a format specifier */ j = 0; spec[j++] = fmt[fi]; fi++; width = 0; if (isdigit(fmt[fi])) { /* This format specifier has a width */ while (isdigit(fmt[fi])) { width = 10*width + (fmt[fi]-'0'); fi++; } if (width > sizeof(res)-1) /* Kludge */ width = sizeof(res)-1; } spec[j++] = fmt[fi]; if (fmt[fi] == 'E' or fmt[fi] == 'O') { /* Two-character format specifier */ spec[j++] = fmt[fi++]; } spec[j] = '\0'; #if DEBUG printf("$ spec [%s]\n", spec); #endif if (!isprint(fmt[fi])) { /* Error, bad format spec char */ return (-fi); } /* Format a substring using a single format specifier */ fillch = ' '; switch (fmt[fi]) { case 'f': /* Special case '%[d+]f', era name */ sprintf(res, "%s", (cal->cal_era > _CAL_ERA_COMMON ? "BC" : "AD")); #if DEBUG printf("$ %%%ds >[%s]\n", width, res); #endif break; case 's': /* Special case '%[d+]s', subseconds */ sprintf(res, "%09ld", cal->cal_nsec); #if DEBUG printf("$ %%%ds >[%s]\n", width, res); #endif fillch = '0'; break; case 'z': /* Special case '%[d+]z', timezone offset */ dst = cal->cal_isdst; dst = (dst > 0 ? 1 : 0); dst = zones[dst]->tz_min_west; k = (dst < 0 ? -dst : dst); sprintf(res, "%c%02d%02d", (dst < 0 ? '-' : '+'), k/60, k%60); #if DEBUG printf("$ %%%dz >[%s]\n", width, res); #endif break; case 'Z': /* Special case '%[d+]Z', timezone name */ dst = cal->cal_isdst; dst = (dst > 0 ? 1 : 0); strcpy(res, zones[dst]->tz_abbr); #if DEBUG printf("$ %%%dZ >[%s]\n", width, res); #endif break; case 'C': case 'd': case 'g': case 'G': case 'H': case 'I': case 'j': case 'm': case 'M': case 'r': case 'S': case 'u': case 'U': case 'V': case 'w': case 'W': case 'y': case 'Y': fillch = '0'; /* Fall though */ default: /* Format according to a single format specifier */ k = strftime(res, sizeof(res), spec, &ts); #if DEBUG printf("$ %%%d%c >[%s]\n", width, fmt[fi], res); #endif break; } /* Move the formatted result into 'buf' */ k = strlen(res); if (width == 0) width = k; #if DEBUG printf("$ len(res)=%d, width=%d\n", k, width); #endif if (fillch == '0') { /* Insert padding chars on the left */ while (width > k and bi < max) { buf[bi++] = fillch; width--; } } for (k = 0; res[k] != '\0' and k < width and bi < max; k++) buf[bi++] = res[k]; #if DEBUG printf("$ k=%d, width=%d, bi=%d/%d\n", k, width, bi, max); #endif if (fillch == ' ') { /* Insert padding chars on the right */ while (k < width and bi < max) { buf[bi++] = fillch; k++; } } } else { /* Non-format specifier character */ if (bi < max) buf[bi++] = fmt[fi]; } } /* Done */ buf[bi] = '\0'; return (bi); } /*------------------------------------------------------------------------------ * gregorian_scan() */ static int gregorian_scan(const char *buf, const char *fmt, struct _calendar *cal) { #if _CALENDAR_VS > 20020223 #error struct _calendar has changed #endif /*+INCOMPLETE */ fprintf(stderr, "Not implemented: gregorian_scan()\n"); return (-1); } /******************************************************************************* * Private types *******************************************************************************/ /*------------------------------------------------------------------------------ * struct FuncVector * Contains pointers to functions that implement the calendar functions for * a given calendar type. * * See also * vect[] */ struct FuncVector { /* Implementation of '_calendardiff()' */ int (*diff)(struct _calendar */*restrict*/ dif, const struct _calendar *a, const struct _calendar *b); /* Implementation of '_calendarformat()' */ int (*format)(char */*restrict*/ buf, size_t max, const char *fmt, const struct _calendar *cal); /* Implementation of '_calendarscanf()' */ int (*scan)(const char *buf, const char *fmt, struct _calendar *cal); /* Implementation of '_checkcalendar()' */ int (*check)(const struct _calendar *cal); /* Implementation of '_getcalendar()' */ int (*getcal)(struct _calendar *cal, const struct _calendarinfo *type, const struct _timezone *tz[2]); /* Implementation of '_getcalendartime()' */ int (*gettime)(struct _calendar *cal, time_t *t); /* Implementation of '_setcalendartime()' */ int (*settime)(struct _calendar *cal, time_t t); /* Implementation of '_setcalendarzone()' */ int (*setzone)(struct _calendar *cal, const struct _timezone *tz[2]); }; /******************************************************************************* * Private constants *******************************************************************************/ /*------------------------------------------------------------------------------ * vect[] * Contains pointers to functions that implement the calendar functions for * each implemented calendar type. * * See also * vect[] */ static const struct FuncVector vect[] = { /* [0], "Gregorian" */ { &gregorian_diff, /* diff */ &gregorian_format, /* format */ &gregorian_scan, /* scan */ &gregorian_check, /* check */ &gregorian_getcal, /* getcal */ &gregorian_gettime, /* gettime */ &gregorian_settime, /* settime */ &gregorian_setzone, /* setzone */ }, }; /******************************************************************************* * Public functions *******************************************************************************/ /*------------------------------------------------------------------------------ * _calendardiff() * Computes the difference between broken-down calendar date 'a' and date * 'b', placing the result into the broken-down calendar date value 'dif'. * The result is such that adding each member of the broken-down date 'dif' * to date 'a' and then normalizing would result in a date equal to the * normalized value of date 'b'. * * Parameter dif * Pointer to a calendar object, which will be filled with the computed * difference between dates 'a' and 'b'. This pointer can be null. * * Parameter a * Pointer to a calendar object. * * Parameter b * Pointer to another calendar object. * * Returns * Zero (0) if dates 'a' and 'b' are the same, or * -1 if date 'a' comes before date 'b', or * +1 if date 'a' comes after date 'b', or * a negative value (other than -1) if date 'a' or 'b' is an invalid date * or some other error occurs. */ int _calendardiff(struct _calendar */*restrict*/ dif, const struct _calendar *a, const struct _calendar *b) { int type; /* Check args */ if (a == NULL or b == NULL) return (INT_MIN); type = a->cal_type; if (type != _CAL_TYPE_GREGORIAN or b->cal_type != type) return (INT_MIN); /* Compute a calendar date difference */ return (vect[type].diff(dif, a, b)); } /*------------------------------------------------------------------------------ * _calendarformat() * Converts a calendar date into a string, formatting it according to a * format specification control string. * * Parameter buf * Points to a string buffer to be formatted. Must not be null. * * Parameter max * Specifies the maximum number of characters (including a terminating '\0' * null character) that can be written into string buffer 'buf'. * * Parameter fmt * Points to a control string that specifies how the target buffer 'buf' is * to be formatted. Format date specifier sequences start with "%", * optionally followed by a sequence of decimal digits that specify the * width of the date field, followed by one or two format type characters. +INCOMPLETE * ... * * Parameter cal * Points to a calendar date object. Must not be null. * * Returns * The number of characters written into string 'buf' if successful, * otherwise a negative value representing the negative index of the first * character within the format control string 'fmt' that caused an error. */ int _calendarformat(char */*restrict*/ buf, size_t max, const char *fmt, const struct _calendar *cal) { int type; /* Check args */ if (cal == NULL) return (-1); if (fmt == NULL) return (-1); if (buf == NULL) return (-1); type = cal->cal_type; if (type != _CAL_TYPE_GREGORIAN) return (-1); if (max == 0) return (0); /* Handle special cases */ if (cal->cal_year == _CAL_YR_ERROR) { /* Erroneous time value */ return (-1); } /* Format a string from a calendar date */ return (vect[type].format(buf, max, fmt, cal)); } /*------------------------------------------------------------------------------ * _calendarscanf() * +INCOMPLETE */ int _calendarscanf(const char *buf, const char *fmt, struct _calendar *cal) { int type; /* Check args */ if (cal == NULL) return (-1); if (fmt == NULL) return (-1); if (buf == NULL) return (-1); type = cal->cal_type; if (type != _CAL_TYPE_GREGORIAN) return (-1); /* Parse a date string into a calendar date */ return (vect[type].scan(buf, fmt, cal)); } /*------------------------------------------------------------------------------ * _checkcalendar() * +INCOMPLETE */ int _checkcalendar(const struct _calendar *cal) { /*+INCOMPLETE */ fprintf(stderr, "Not implemented: _checkcalendar()\n"); return (-1); } /*------------------------------------------------------------------------------ * _getcalendar() * Locate a calendar of a type matching the name 'name', for a timezone * matching the name 'tzname', initializing struct 'cal' with default * values. * * Parameter cal * Points to a calendar object to be initialized. * * Parameter name * Points to a string containing the name of a calendric system (e.g., * "Gregorian"). Can be empty ("") or null, in which case it is assumed to * be the same as constant '_CAL_NAME_GREGORIAN'. * * Parameter tzname * Points to a string containing the name of a timezone (e.g., "EST"). * Can be empty ("") or null, in which case it is assumed to be the same as * constant '_TZ_LOCAL'. * * Returns * Zero on success, otherwise a nonzero value on error (e.g., an invalid * argument, or no such calendar or timezone with a matching name was * found). */ int _getcalendar(struct _calendar *cal, const char *name, const char *tzname) { int type; const struct _calendarinfo * info; const struct _timezone * zones[2]; #if DEBUG printf("$ _getcalendar(cal=%08p, name=%08p=\"%.200s\"," " tz=%08p=\"%.200s\")\n", cal, name, (name != NULL ? name : ""), tzname, (tzname != NULL ? tzname : "")); #endif /* Check args */ if (cal == NULL) return (-1); if (name == NULL or name[0] == '\0') name = _CAL_NAME_GREGORIAN; if (tzname == NULL or tzname[0] == '\0') tzname = _TZ_LOCAL; /* Check for a supported calendar type */ info = _getcalendarinfo(name); if (info == NULL) return (-1); type = info->ci_type; /* Find a supported timezone type */ if (_gettimezone(zones, tzname) != 0) return (-1); /* Set calendar members */ return (vect[type].getcal(cal, info, zones)); } /*------------------------------------------------------------------------------ * _getcalendarinfo() * +INCOMPLETE */ const struct _calendarinfo * _getcalendarinfo(const char *name) { int i; #if DEBUG printf("$ _getcalendarinfo(name=%08p=\"%s\")\n", name, (name != NULL ? name : "")); #endif /* Locate a supported calendric system */ for (i = 0; i < ncal_info; i++) { if (strcmp(name, _cal_info[i].ci_name) == 0) return (&_cal_info[i]); } /* No matching calendar name found */ return (NULL); } /*------------------------------------------------------------------------------ * _getcalendartime() * Converts a calendar date into its equivalent system time value, after * normalizing its members to fall within their normal value ranges. * * Parameter cal * Points to a calendar date object, whose members are normalized. * * Parameter t * Points to a system time value to set. * * Returns * Zero on success, otherwise a nonzero value on error. */ int _getcalendartime(struct _calendar *cal, time_t *t) { time_t tv; /* Check args */ if (cal == NULL) return (-1); if (cal->cal_type != _CAL_TYPE_GREGORIAN) return (-1); /* Handle special cases */ if (cal->cal_year == _CAL_YR_ERROR) { /* Erroneous time value */ tv = _TIME_ERROR; } else { /* Normalize the calendar date and convert it into a system time */ return (vect[cal->cal_type].gettime(cal, t)); } /* Set the converted system time value */ if (t != NULL) *t = tv; return (0); } /*------------------------------------------------------------------------------ * _getcalendartype() * +INCOMPLETE */ const struct _calendarinfo * _getcalendartype(int type) { /* Check for a supported calendar type */ if (type != _CAL_TYPE_GREGORIAN) return (NULL); return (&_cal_info[type]); } /*------------------------------------------------------------------------------ * _setcalendartime() * Establishes a new date for a calendar date object by converting a system * time to its equivalent calendar date value. * * Parameter cal * Points to a calendar date object, whose members will be set to * correspond to the system time value 't'. * * Parameter t * A system time to convert. * * Returns * Zero on success, otherwise a nonzero value on error. */ int _setcalendartime(struct _calendar *cal, time_t t) { #if DEBUG printf("$ _setcalendartime(cal=%08p, t=%ld)\n", cal, (long)t); #endif /* Check args */ if (cal == NULL) return (-1); if (cal->cal_type != _CAL_TYPE_GREGORIAN) { #if DEBUG printf("$$ cal_type=%d (!= %d)\n", cal->cal_type, _CAL_TYPE_GREGORIAN); #endif return (-1); } /* Handle special cases */ if (t == _TIME_ERROR) { reset_calendar(cal); cal->cal_year = _CAL_YR_ERROR; return (0); } else { /* Establish a new date and time for the calendar date */ return (vect[cal->cal_type].settime(cal, t)); } } /*------------------------------------------------------------------------------ * _setcalendarzone() * Establishes a new timezone setting for a calendar date. * * First undoes the effects of the last timezone that was applied to the * calendar date object, then applies the effects of the specified timezone * to it. * * Parameter cal * Points to the calendar date object for which to establish a new timezone * setting. * * Parameter tz * Points to a two-element array of pointers to timezone structures to * apply to the calendar date object. These may be null, in which case all * timezone effects are removed from the calendar date object. * * Returns * Zero on success, otherwise a nonzero value on error. */ int _setcalendarzone(struct _calendar *cal, const struct _timezone *tz[2]) { int type; const struct _timezone * zones[2]; /* Check args */ if (cal == NULL) return (-1); if (tz != NULL) { zones[0] = tz[0]; zones[1] = tz[1]; } else { zones[0] = NULL; zones[1] = NULL; } /* Check for a supported calendar type */ type = cal->cal_type; if (type != _CAL_TYPE_GREGORIAN) return (-1); /* Apply a new timezone to the calendar date */ return (vect[type].setzone(cal, zones)); } /* End c0xcalendar.c */ /* -----BEGIN PGP SIGNATURE----- Version: PGPfreeware 7.0.3 for non-commercial use iQA/AwUBPJgB7HS9RCOKzj55EQIY5gCbBIELKhWOJ4V+CH2KO/m8T4AwqrYAnilO /JDtStxiUSngt5BFBHITna4N =9hOm -----END PGP SIGNATURE----- */