//+INCOMPLETE /******************************************************************************* * c0xcalendar.c * Proposed ISO/IEC C 200X 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 * purposes only and are not meant for production-quality systems. * * Functions that are implemented: * calendardiff() * calendarformat() * calendarscanf() * getcalendarinfo() * getcalendartype() * gettimezoneoffset() * initcalendar() * mklongtime() * setcalendartime() * setcalendarzone() * * 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' type * 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 at least 32 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 * ISO C99, since most C compilers as of this writing do not conform to the * ISO C99 standard. * * Author * This code was written by David R. Tribble (david@tribble.com), Jul 2004 * thru Mar 2006. * It is hereby placed into the public domain, and may be used for all * purposes, both commercial and private. No warranty is provided for this * source code, and the author cannot be held liable for its use in any * way. * * References * See , Revision 2.3. * * See also * c0xcalendar.h * c0xtimezone.h * c0xlongtime.h */ /* Identification */ static const char REV[] = "@(#)drt/text/std/c0xcalendar.c $Revision: 1.4 $ $Date: 2009/08/24 02:25:03 $"; /* Standard includes */ #include #include #include #include #include #include #include /* Local includes */ #include "c0xlongtime.h" #include "c0xtimezone.h" #include "c0xcalendar.h" /******************************************************************************* * Public constants *******************************************************************************/ const char _c0x_cal_name_gregorian[] = "Gregorian"; /******************************************************************************* * Shared constants *******************************************************************************/ extern const struct timezone _cal_zones?[]; /* See "c0xtimezone.c" */ struct timezone gmt_zone; /******************************************************************************* * Private constants *******************************************************************************/ #define GREGORIAN_YEAR_MIN 1601 #define GREGORIAN_YEAR_MAX 3201 #ifndef GREGORIAN_TIME_MIN +REDO #define GREGORIAN_TIME_MIN ((time_t)0) #endif #ifndef GREGORIAN_TIME_MAX +REDO #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 * 24*60*60 - 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. * * Since * 1.1, 2004-07-09 */ static const struct calendarinfo cal_info[] = { #if _C0X_CALENDARINFO_VS > 20040801 #error struct calendarinfo has changed #endif /* [0] */ { /* ci__vers */ _C0X_CALENDARINFO_VS, /* ci_type */ _CAL_TYPE_GREGORIAN, /* ci_name */ _CAL_NAME_GREGORIAN, /* ci_time_min */ GREGORIAN_LONGTIME_MIN, /* ci_time_max */ GREGORIAN_LONGTIME_MAX, /* ci_era_min */ 0, /* ci_era_max */ 1, /* ci_year_min */ GREGORIAN_YEAR_MIN, /* ci_year_max */ GREGORIAN_YEAR_MAX, /* ci_mon_min */ 1, /* ci_mon_max */ 12, /* ci_week_min */ 1-1, /* ci_week_max */ 52+1, /* ci_mday_min */ 1, /* ci_mday_max */ 31, /* ci_yday_min */ 1, /* ci_yday_max */ 365+1, /* ci_wday_min */ 1, /* ci_wday_max */ 7, /* ci_wday_1st */ 4, /* ci_hour_min */ 0, /* ci_hour_max */ 24-1, /* ci_min_max */ 60-1, /* ci_sec_max */ 60-1, /* ci_leap_sec */ 0, /* ci__r */ { 0 }, }, }; static const int ncal_info = sizeof(cal_info)/sizeof(cal_info[0]); /*------------------------------------------------------------------------------ * gregorian_cal_init * A default Gregorian calendar object value, used to initialize calendar * objects. * * Since * 1.1, 2004-07-09 */ static const struct calendar gregorian_cal_init = { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif /* cal__vers*/ _C0X_CALENDARINFO_VS, /* cal_type */ _CAL_TYPE_GREGORIAN, /* cal_era */ _CAL_ERA_COMMON, /* cal_year */ _CAL_YR_ERROR, /* cal_mon */ 0, /* cal_week */ 0, /* cal_mday */ 0, /* cal_yday */ 0, /* cal_wday */ 0, /* cal_hour */ 0, /* cal_min */ 0, /* cal_sec */ 0, /* cal_nsec */ 0, /* cal_nmons */ 0, /* cal_nweeks */ 0, /* cal_ndays */ 0, /* cal_dsti */ 0, /* cal_leapsec */ INT_MIN, /* cal_zone */ NULL, /* cal__r */ { 0 }, }; /*------------------------------------------------------------------------------ * month[] * List of days in each month for the Gregorian calendar. * * Since * 1.1, 2004-07-09 */ 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. * * Notes * This function does not alter the 'cal_zone' member. * * Parameter date * Points to a calendar object to be initialized. * * Since * 1.1, 2004-07-09 */ static void reset_calendar(struct calendar *date) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if _C0X_TIMEZONE_VS > 20040709 #error struct timezone has changed #endif const struct timezone * zone; /* Initialize the calendar object */ zone = date->cal_zone; memcpy(date, &gregorian_cal_init, sizeof(*date)); date->cal_zone = zone; } /*------------------------------------------------------------------------------ * tm_to_cal() * Converts a 'tm' structure into a '_calendar' structure. * * Notes * This does not alter the 'cal_zone' member. * * Parameter date * Points to a calendar object to be initialized. * * Parameter ts * Points to a tm object. * * Since * 1.1, 2004-07-09 */ static void tm_to_cal(struct calendar *date, const struct tm *ts) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif int v; /* Fill the contents of 'cal' from 'ts' */ reset_calendar(date); v = ts->tm_year + 1900; date->cal_era = (v > 0 ? _CAL_ERA_COMMON : _CAL_ERA_COMMON+1); date->cal_year = (v > 0 ? v : -v + 1); date->cal_mon = ts->tm_mon + 1; date->cal_mday = ts->tm_mday; date->cal_yday = ts->tm_yday + 1; v = (ts->tm_wday != 0 ? ts->tm_wday : 7); date->cal_wday = v; date->cal_week = (ts->tm_yday - v + 7+4)/7; date->cal_hour = ts->tm_hour; date->cal_min = ts->tm_min; date->cal_sec = ts->tm_sec; date->cal_nsec = 0; v = ts->tm_isdst; date->cal_dsti = (v < 0 ? -1 : v > 0 ? 1 : 0); date->cal_leapsec = 0; } /*------------------------------------------------------------------------------ * cal_to_tm() * Converts a '_calendar' structure into a 'tm' structure. * * Parameter ts * Points to a tm object to be initialized. * * Parameter date * Points to a calendar object. * * Returns * Zero on success, otherwise -1. * * Since * 1.1, 2004-07-09 */ static int cal_to_tm(struct tm *ts, const struct calendar *date) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if _C0X_TIMEZONE_VS > 20040709 #error struct timezone has changed #endif int v; /* Validate the calendar date object */ if (date->cal_type != _CAL_TYPE_GREGORIAN) return -1; /* Check for special dates */ if (date->cal_year == _CAL_YR_ERROR) return -1; /* Fill the contents of 'ts' from 'cal' */ v = date->cal_year; v = (date->cal_era == _CAL_ERA_COMMON ? v : -v+1); ts->tm_year = v - 1900; ts->tm_mon = date->cal_mon - 1; ts->tm_mday = date->cal_mday; ts->tm_yday = date->cal_yday - 1; ts->tm_wday = date->cal_wday % 7; ts->tm_hour = date->cal_hour; ts->tm_min = date->cal_min; ts->tm_sec = date->cal_sec; v = 0; if (date->cal_zone != NULL) v = date->cal_zone->tz_z[date->cal_dsti].z_dst; ts->tm_isdst = v; return 0; } /*------------------------------------------------------------------------------ * gregorian_initcal() * Initialize a calendar object with default values for a Gregorian * calendar and a specified timezone. * * Parameter date * Points to a calendar object to be initialized. * * Parameter info * Points to a Gregorian calendar information object. * * Returns * Zero on success, otherwise a nonzero value on error (e.g., an invalid * argument, or no such calendar with a matching name was found). * * Since * 1.1, 2004-07-09 */ static int gregorian_initcal(struct calendar *date, const struct calendarinfo *info) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if DEBUG printf("$ gregorian_initcal(date=%08p, info=%08p\n", date, info); #endif /* Initialize the calendar members */ memcpy(date, &gregorian_cal_init, sizeof(*date)); date->cal_type = info->ci_type; /* 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 date * Points to a Gregorian calendar date object, whose members will be set to * correspond to the long time value 't'. * * Parameter zone * Points to a timezone object, or is null. * * Parameter t * A long time to convert. * * Returns * Zero on success, otherwise a nonzero value on error. * * Since * 1.3, 2006-03-11 */ static int gregorian_settime(struct calendar *date, const struct timezone *zone, longtime_t t) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if _C0X_TIMEZONE_VS > 20040709 #error struct timezone has changed #endif +REDO struct tm ts; int dst; int tzoff; +REDO const struct timezone * zones[2]; #if DEBUG printf("$ gregorian_settime(date=%08p, t=%" LLONG_FMT ")\n", date, (llong_t)t); #endif /* Check args */ if (date == 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) */ +REDO ts = *gmtime(&t); /* Retrieve the timezone setting */ +REDO zones[0] = date->cal_zone[0]; if (zones[0] == NULL) zones[0] = &_cal_zones[0]; zones[1] = date->cal_zone[1]; if (zones[1] == NULL) zones[1] = &_cal_zones[0]; /* Apply the calendar's timezone setting */ +REDO, using 'zone' arg 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 */ +REDO mktime(&ts); /* Update the calendar object */ tm_to_cal(date, &ts); /* Done */ return 0; } /*------------------------------------------------------------------------------ * gregorian_maketime() * Converts a Gregorian calendar date into its equivalent system time * value, after normalizing its members to fall within their normal value * ranges. * * Parameter date * Points to a Gregorian calendar date object, whose members are * normalized. * * Parameter zone * Points to a timezone object. * * Parameter t * Points to a long time value to set. * * Returns * Zero on success, otherwise a nonzero value on error. * * Since * 1.3, 2006-03-11 */ static int gregorian_maketime(struct calendar *date, const struct timezone *zone, longtime_t *t) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if _C0X_TIMEZONE_VS > 20040709 #error struct timezone has changed #endif struct tm ts; longtime_t tv; /* Check args */ if (date == NULL) return -1; /* Check special cases */ if (date->cal_year == _CAL_YR_ERROR) return -1; if (date->cal_year < GREGORIAN_YEAR_MIN or date->cal_year > GREGORIAN_YEAR_MAX) return -1; +REDO, adjusting for timezone /* Convert the broken-down date into a system time */ memset(&ts, '\0', sizeof(ts)); ts.tm_year = date->cal_year-1900; ts.tm_mon = date->cal_mon-1; ts.tm_mday = date->cal_mday; ts.tm_hour = date->cal_hour; ts.tm_min = date->cal_min; ts.tm_sec = date->cal_sec; +REDO ts.tm_isdst = 0; /* (date->cal_dst > 0 ? 1 : 0) ? */ #if DEBUG printf("$ maketime: %+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 /* Undo any timezone adjustment */ +REDO ...cal_zone/cal_dst... /* Normalize and convert */ +REDO tv = mktime(&ts); #if DEBUG printf("$ maketime: %+ld\n", (long)tv); #endif if (tv == (time_t)(-1)) return -1; /* Update the broken-down date with normalized values */ tm_to_cal(date, &ts); /* Set the converted system time value */ if (t != NULL) *t = tv; return 0; } /*------------------------------------------------------------------------------ * 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 date * Points to the calendar date object for which to establish a new timezone * setting. * * Parameter zone * Pointer to a timezone object to apply to the calendar date object, which * is assumed to have been previously initialized by a call to * '_gettimezone()'. This 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. * * Since * 1.1, 2004-07-09 */ static int gregorian_setzone(struct calendar *date, const struct timezone *zone) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if _C0X_TIMEZONE_VS > 20040709 #error struct timezone has changed #endif int dst; time_t t; const struct timezone * ozones[2]; #if DEBUG printf("$ gregorian_setzone(date=%08p, zone=%08p)\n", date, zone); #endif /* Retrieve the old timezone settings */ +REDO ozones[0] = date->cal_zone[0]; if (ozones[0] == NULL) ozones[0] = &_cal_zones[0]; ozones[1] = date->cal_zone[1]; if (ozones[1] == NULL) ozones[1] = &_cal_zones[0]; /* Undo the effects of the current timezone setting */ if (date->cal_year != _CAL_YR_ERROR) { +REDO dst = date->cal_isdst; dst = (dst > 0 ? 1 : 0); dst = ozones[dst]->tz_min_west; date->cal_min += dst/60; dst = dst%60; date->cal_sec += dst; } +REDO date->cal_zone[0] = NULL; date->cal_zone[1] = NULL; /* Normalize the adjusted calendar date */ if (date->cal_year != _CAL_YR_ERROR) { if (gregorian_maketime(date, zone, &t) != 0) return -1; } /* Apply the effects of the new timezone */ if (date->cal_year != _CAL_YR_ERROR) { +REDO dst = date->cal_isdst; dst = (dst > 0 ? 1 : 0); dst = zone[dst]->tz_min_west; date->cal_min -= dst/60; dst = dst%60; date->cal_sec -= dst; } +REDO date->cal_zone[0] = zone[0]; date->cal_zone[1] = zone[1]; /* Reset and normalize the new calendar date */ if (date->cal_year != _CAL_YR_ERROR) return gregorian_maketime(date, zone, &t); else return 0; } /*------------------------------------------------------------------------------ * gregorian_gettzoff() * Determines the offset in milliseconds from UTC of the time represented * by a calendar date. * * Parameter date * Points to the calendar date object for which to determine the timezone * and DST offset. * * Returns * The number of milliseconds offset from UTC of the calendar date object, * or '_TZ_ERROR' on error. * * Since * 1.1, 2004-07-09 */ static long gregorian_gettzoff(const struct calendar *date) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if _C0X_TIMEZONE_VS > 20040709 #error struct timezone has changed #endif /* Determine the UTC offset for the calendar's timezone+DST combination */ +REDO return _getutcoffset(date->cal_zone, date->cal_dst); } /*------------------------------------------------------------------------------ * 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. * * Since * 1.1, 2004-07-09 */ static int gregorian_diff(struct calendar *dif, const struct calendar *a, const struct calendar *b) { #if _C0X_CALENDAR_VS > 20060311 #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, &gregorian_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 date * Points to a calendar date object. Must not be null. * * Parameter zone * Points to a timezone object. Can 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. * * Since * 1.1, 2004-07-09 */ static int gregorian_format(char *buf, size_t max, const char *fmt, const struct calendar *date, const struct timezone *zone) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if _C0X_TIMEZONE_VS > 20040709 #error struct timezone 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\"," " date=%08p, zone=%08p)\n", buf, (unsigned long)max, fmt, (fmt != NULL ? fmt : ""), date, zone); #endif /* Check args */ if (date == NULL) return -1; if (buf == NULL) return -1; if (fmt == NULL) return 0; if (max == 0) return 0; if (zone == NULL) zone = ...GMT...; /* Check for special dates */ if (date->cal_year == _CAL_YR_ERROR) return -1; /* Convert 'cal' into 'ts' */ cal_to_tm(&ts, date); +REDO zones[0] = date->cal_zone[0]; if (zones[0] == NULL) zones[0] = &_cal_zones[0]; /* Kludge */ zones[1] = date->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', subseconds */ sprintf(res, "%09ld", date->cal_nsec); #if DEBUG printf("$ %%%df >[%s]\n", width, res); #endif fillch = '0'; break; case 'L': /* Special case '%[d+]L', leap seconds */ sprintf(res, "%d", date->cal_leapsec); #if DEBUG printf("$ %%%dL >[%s]\n", width, res); #endif fillch = '0'; break; case 'Q': /* Special case '%[d+]Q', era name */ sprintf(res, "%s", (date->cal_era > _CAL_ERA_COMMON ? "BC" : "AD")); #if DEBUG printf("$ %%%dQ >[%s]\n", width, res); #endif break; case 'z': /* Special case '%[d+]z', timezone offset */ dst = date->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': { +REDO /* Special case '%[d+]Zx', timezone name/abbreviation */ switch (fmt[++fi]) { case 'a': /* Special case '%[d+]Za', timezone abbreviation */ +REDO dst = date->cal_dst; dst = (dst > 0 ? 1 : 0); strcpy(res, zones[dst]->tz_abbr); #if DEBUG printf("$ %%%dZa >[%s]\n", width, res); #endif break; case 'n': /* Special case '%[d+]Zn', timezone full name */ +REDO dst = date->cal_dst; dst = (dst > 0 ? 1 : 0); strcpy(res, zones[dst]->tz_name); #if DEBUG printf("$ %%%dZn >[%s]\n", width, res); #endif break; default: ...error 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() * +INCOMPLETE * * Since * 1.1, 2004-07-09 */ static int gregorian_scan(const char *buf, const char *fmt, struct calendar *date, struct timezone *zone) { #if _C0X_CALENDAR_VS > 20060311 #error struct calendar has changed #endif #if _C0X_TIMEZONE_VS > 20040709 #error struct timezone has changed #endif /*+INCOMPLETE*/ fprintf(stderr, "Not implemented: gregorian_scan()\n"); return -1; } /******************************************************************************* * Private constants *******************************************************************************/ /*------------------------------------------------------------------------------ * vect[] * Contains pointers to functions that implement the calendar functions for * each implemented calendar type. * * See also * vect[] * * Since * 1.1, 2004-07-09 */ #define FuncVector_VS 20060311 struct FuncVector { /* Implementation of 'calendardiff()' */ int (*diff)(struct calendar *dif, const struct calendar *a, const struct calendar *b); /* Implementation of 'calendarformat()' */ int (*format)(char *buf, size_t max, const char *fmt, const struct calendar *date, const struct timezone *zone); /* Implementation of 'calendarscanf()' */ int (*scan)(const char *buf, const char *fmt, struct calendar *date, struct timezone *zone); /* Implementation of 'initcalendar()' */ int (*initcal)(struct calendar *date, const struct calendarinfo *type); /* Implementation of 'mklongtime()' */ int (*maketime)(struct calendar *date, const struct timezone *zone, longtime_t *t); /* Implementation of 'setcalendartime()' */ int (*settime)(struct calendar *date, const struct timezone *zone, longtime_t t); /* Implementation of 'setcalendarzone()' */ int (*setzone)(struct calendar *date, const struct timezone *zone); /* Implementation of 'gettimezoneoffset()' */ int (*gettzoff)(const struct calendar *date); }; static const struct FuncVector vect[] = { #if FuncVector_VS > 20060311 #error struct FuncVector has changed #endif /* [0], "Gregorian" */ { &gregorian_diff, /* diff */ &gregorian_format, /* format */ &gregorian_scan, /* scan */ &gregorian_initcal, /* initcal */ &gregorian_maketime, /* maketime */ &gregorian_settime, /* settime */ &gregorian_setzone, /* setzone */ &gregorian_gettzoff, /* gettzoff */ }, }; /******************************************************************************* * 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. * * Since * 1.1, 2004-07-09 */ int calendardiff(struct calendar *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 date * 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. * * Since * 1.1, 2004-07-09 */ int calendarformat(char *buf, size_t max, const char *fmt, const struct calendar *date, const struct timezone *zone) { int type; /* Check args */ if (date == NULL) return -1; type = date->cal_type; if (type != _CAL_TYPE_GREGORIAN) return -1; if (fmt == NULL) return -1; if (buf == NULL) return -1; if (max == 0) return 0; /* Handle special cases */ if (date->cal_year == _CAL_YR_ERROR) { /* Erroneous time value */ return -1; } /* Format a string from a calendar date */ return vect[type].format(buf, max, fmt, date, zone); } /*------------------------------------------------------------------------------ * calendarscanf() * +INCOMPLETE * * Since * 1.1, 2004-07-09 */ int calendarscanf(const char *buf, const char *fmt, struct calendar *date, struct timezone *zone) { int type; /* Check args */ if (date == NULL) return -1; if (fmt == NULL) return -1; if (buf == NULL) return -1; type = date->cal_type; if (type != _CAL_TYPE_GREGORIAN) return -1; /* Parse a date string into a calendar date and timezone */ return vect[type].scan(buf, fmt, date, zone); } /*------------------------------------------------------------------------------ * initcalendar() * Locate a calendar of a type matching the name 'name', for a timezone * matching the name 'tzname', initializing struct 'cal' with default * values. * * Parameter date * 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'. * * 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). * * Since * 1.1, 2004-07-09 */ int initcalendar(struct calendar *date, const char *name) { int type; const struct calendarinfo * info; +REDO #if DEBUG printf("$ initcalendar(date=%08p, name=%08p=\"%.200s\")\n", date, name, (name != NULL ? name : "")); #endif /* Check args */ if (date == NULL) return -1; if (name == NULL or name[0] == '\0') { /* Default local calendar is "Gregorian" */ name = _CAL_NAME_GREGORIAN; } /* Check for a supported calendar type */ info = getcalendarinfo(name); if (info == NULL) return -1; /* Set calendar members */ type = info->ci_type; return vect[type].initcal(date, info); } /*------------------------------------------------------------------------------ * getcalendarinfo() * +INCOMPLETE * * Since * 1.1, 2004-07-09 */ 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; } /*------------------------------------------------------------------------------ * mklongtime() * Converts a calendar date into its equivalent system time value, after * normalizing its members to fall within their normal value ranges. * * Parameter date * Points to a calendar date object, whose members are normalized. * * Parameter t * Points to a long time value to set. * * Returns * Zero on success, otherwise a nonzero value on error. * * Since * 1.1, 2004-07-09 */ int mklongtime(struct calendar *date, const struct timezone *zone, longtime_t *t) { longtime_t tv; /* Check args */ if (date == NULL) return -1; if (date->cal_type != _CAL_TYPE_GREGORIAN) return -1; /* Handle special cases */ if (date->cal_year == _CAL_YR_ERROR) { /* Erroneous time value */ tv = _LONGTIME_ERROR; } else { /* Convert the calendar date into a system time */ return vect[date->cal_type].maketime(date, zone, t); } /* Set the converted system time value */ if (t != NULL) *t = tv; return 0; } /*------------------------------------------------------------------------------ * getcalendartype() * +INCOMPLETE * * Since * 1.1, 2004-07-09 */ 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 date * Points to a calendar date object, whose members will be set to * correspond to the system time value 't'. * * Parameter zone * Pointes to a timezone object, or is null. * * Parameter t * A long time to convert. * * Returns * Zero on success, otherwise a nonzero value on error. * * Since * 1.3, 2006-03-11 */ int setcalendartime(struct calendar *date, const struct timezone *zone, longtime_t t) { #if DEBUG printf("$ setcalendartime(date=%08p, t=%" LLONG_FMT ")\n", date, (llong_t)t); #endif /* Check args */ if (date == NULL) return -1; if (date->cal_type != _CAL_TYPE_GREGORIAN) { #if DEBUG printf("$$ date_type=%d (!= %d)\n", date->cal_type, _CAL_TYPE_GREGORIAN); #endif return -1; } /* Handle special cases */ if (t == _LONGTIME_ERROR) { reset_calendar(date); date->cal_year = _CAL_YR_ERROR; return 0; } else { /* Establish a new date and time for the calendar date */ return vect[date->cal_type].settime(date, zone, 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 date * Points to the calendar date object for which to establish a new timezone * setting. * * Parameter zone * Points to a timezone object, which is assumed to have been previously * initialized by a call to '_gettimezone()'. This can 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. * * Since * 1.1, 2004-07-09 */ int setcalendarzone(struct calendar *date, const struct timezone *zone) { int type; /* Check args */ if (date == NULL) return -1; /* Check for a supported calendar type */ type = date->cal_type; if (type != _CAL_TYPE_GREGORIAN) return -1; /* Apply a new timezone to the calendar date */ return vect[type].setzone(date, zone); } /*------------------------------------------------------------------------------ * gettimezoneoffset() * Determines the offset in milliseconds from UTC of the time represented * by a calendar date. * * Parameter date * Points to the calendar date object for which to determine the timezone * and DST offset. * * Returns * The number of milliseconds offset from UTC of the calendar date object, * or '_TZ_ERROR' on error. * * Since * 1.1, 2004-07-09 */ long gettimezoneoffset(const struct calendar *date) { int type; /* Check args */ if (date == NULL) return _TZ_ERROR; /* Check for a supported calendar type */ type = date->cal_type; if (type != _CAL_TYPE_GREGORIAN) return _TZ_ERROR; /* Determine the timezone+DST offset of the calendar date */ return vect[type].gettzoff(date); } /* End c0xcalendar.c */