X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Ffmt_date.c;h=6d37bd7449e8f414c5fbe269dfaa77d71f4a0627;hb=fb6f6fa4ec4e3277e30d84326d48e6850822d318;hp=0475f91cf3b0c3a91c410078d5ed72874e769b19;hpb=1e32899153e9e52aaec1e651e0c33a563b8aaed8;p=citadel.git diff --git a/webcit/fmt_date.c b/webcit/fmt_date.c index 0475f91cf..6d37bd744 100644 --- a/webcit/fmt_date.c +++ b/webcit/fmt_date.c @@ -1,148 +1,174 @@ /* - * $Id$ + * Copyright (c) 1996-2012 by the citadel.org team + * + * This program is open source software. You can redistribute it and/or + * modify it under the terms of the GNU General Public License, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ -/** - * \defgroup FormatDates Miscellaneous routines formating dates - * \ingroup Calendaring - */ -/*@{*/ + #include "webcit.h" #include "webserver.h" -typedef unsigned char byte; /**< a byte. */ -char *wdays[7]; -char *months[12]; +#ifdef HAVE_USELOCALE +extern locale_t *wc_locales; +#endif + +typedef unsigned char byte; #define FALSE 0 /**< no. */ #define TRUE 1 /**< yes. */ -/** - * \brief Format a date/time stamp for output - * \param buf the output buffer - * \param thetime time to convert to string - * \param brief do we want compact view????? +/* + * Wrapper around strftime() or strftime_l() + * depending upon how our build is configured. + * + * s String target buffer + * max Maximum size of string target buffer + * format strftime() format + * tm Input date/time + */ +size_t wc_strftime(char *s, size_t max, const char *format, const struct tm *tm) +{ + +#ifdef ENABLE_NLS +#ifdef HAVE_USELOCALE + if (wc_locales[WC->selected_language] == NULL) { + return strftime(s, max, format, tm); + } + else { + return strftime_l(s, max, format, tm, wc_locales[WC->selected_language]); + } +#else + return strftime(s, max, format, tm); +#endif +#else + return strftime(s, max, format, tm); +#endif +} + + + +/* + * Format a date/time stamp for output */ -void fmt_date(char *buf, time_t thetime, int brief) +void webcit_fmt_date(char *buf, size_t siz, time_t thetime, int Format) { struct tm tm; struct tm today_tm; time_t today_timet; - int hour; - char calhourformat[16]; - static char *ascmonths[12] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } ; - - if (ascmonths[0] == NULL) { - ascmonths[0] = _("Jan"); - ascmonths[1] = _("Feb"); - ascmonths[2] = _("Mar"); - ascmonths[3] = _("Apr"); - ascmonths[4] = _("May"); - ascmonths[5] = _("Jun"); - ascmonths[6] = _("Jul"); - ascmonths[7] = _("Aug"); - ascmonths[8] = _("Sep"); - ascmonths[9] = _("Oct"); - ascmonths[10] = _("Nov"); - ascmonths[11] = _("Dec"); - }; - - get_preference("calhourformat", calhourformat, sizeof calhourformat); + int time_format; + time_format = get_time_format_cached (); today_timet = time(NULL); localtime_r(&today_timet, &today_tm); localtime_r(&thetime, &tm); - hour = tm.tm_hour; - if (hour == 0) - hour = 12; - else if (hour > 12) - hour = hour - 12; - - buf[0] = 0; - - if (brief) { - /** If date == today, show only the time */ - if ((tm.tm_year == today_tm.tm_year) - &&(tm.tm_mon == today_tm.tm_mon) - &&(tm.tm_mday == today_tm.tm_mday)) { - if (!strcasecmp(calhourformat, "24")) { - sprintf(buf, "%2d:%02d", - tm.tm_hour, tm.tm_min - ); + /* + * DATEFMT_FULL: full display + * DATEFMT_BRIEF: if date == today, show only the time + * otherwise, for messages up to 6 months old, + * show the month and day, and the time + * older than 6 months, show only the date + * DATEFMT_RAWDATE: show full date, regardless of age + * DATEFMT_LOCALEDATE: show full date as prefered for the locale + */ + + switch (Format) { + case DATEFMT_BRIEF: + if ((tm.tm_year == today_tm.tm_year) + &&(tm.tm_mon == today_tm.tm_mon) + &&(tm.tm_mday == today_tm.tm_mday)) { + if (time_format == WC_TIMEFORMAT_24) + wc_strftime(buf, siz, "%k:%M", &tm); + else + wc_strftime(buf, siz, "%l:%M%p", &tm); } - else { - sprintf(buf, "%2d:%02d%s", - hour, tm.tm_min, - ((tm.tm_hour >= 12) ? "pm" : "am") - ); - } - } - - /** Otherwise, for messages up to 6 months old, show the - * month and day, and the time */ - else if (today_timet - thetime < 15552000) { - if (!strcasecmp(calhourformat, "24")) { - sprintf(buf, "%s %d %2d:%02d", - ascmonths[tm.tm_mon], - tm.tm_mday, - tm.tm_hour, tm.tm_min - ); + else if (today_timet - thetime < 15552000) { + if (time_format == WC_TIMEFORMAT_24) + wc_strftime(buf, siz, "%b %d %k:%M", &tm); + else + wc_strftime(buf, siz, "%b %d %l:%M%p", &tm); } else { - sprintf(buf, "%s %d %2d:%02d%s", - ascmonths[tm.tm_mon], - tm.tm_mday, - hour, tm.tm_min, - ((tm.tm_hour >= 12) ? "pm" : "am") - ); + wc_strftime(buf, siz, "%b %d %Y", &tm); } - } - - /** older than 6 months, show only the date */ - else { - sprintf(buf, "%s %d %d", - ascmonths[tm.tm_mon], - tm.tm_mday, - tm.tm_year + 1900 - ); - } + break; + case DATEFMT_FULL: + if (time_format == WC_TIMEFORMAT_24) + wc_strftime(buf, siz, "%a %b %d %Y %T %Z", &tm); + else + wc_strftime(buf, siz, "%a %b %d %Y %r %Z", &tm); + break; + case DATEFMT_RAWDATE: + wc_strftime(buf, siz, "%a %b %d %Y", &tm); + break; + case DATEFMT_LOCALEDATE: + wc_strftime(buf, siz, "%x", &tm); + break; } - else { - if (!strcasecmp(calhourformat, "24")) { - sprintf(buf, "%s %d %d %2d:%02d", - ascmonths[tm.tm_mon], - tm.tm_mday, - tm.tm_year + 1900, - tm.tm_hour, tm.tm_min - ); - } - else { - sprintf(buf, "%s %d %d %2d:%02d%s", - ascmonths[tm.tm_mon], - tm.tm_mday, - tm.tm_year + 1900, - hour, tm.tm_min, ((tm.tm_hour >= 12) ? "pm" : "am") - ); - } +} + + +/* + * Try to guess whether the user will prefer 12 hour or 24 hour time based on the locale. + */ +long guess_calhourformat(void) { + char buf[64]; + struct tm tm; + memset(&tm, 0, sizeof tm); + wc_strftime(buf, 64, "%X", &tm); + if (buf[strlen(buf)-1] == 'M') { + return 12; } + return 24; } +/* + * learn the users timeformat preference. + */ +int get_time_format_cached (void) +{ + long calhourformat; + int *time_format_cache; + time_format_cache = &(WC->time_format_cache); + if (*time_format_cache == WC_TIMEFORMAT_NONE) + { + get_pref_long("calhourformat", &calhourformat, 99); + + /* If we don't know the user's time format preference yet, + * make a guess based on the locale. + */ + if (calhourformat == 99) { + calhourformat = guess_calhourformat(); + } + + /* Now set the preference */ + if (calhourformat == 24) + *time_format_cache = WC_TIMEFORMAT_24; + else + *time_format_cache = WC_TIMEFORMAT_AMPM; + } + return *time_format_cache; +} -/** - * \brief Format TIME ONLY for output - * \param buf the output buffer - * \param thetime time to format into buf +/* + * Format TIME ONLY for output + * buf the output buffer + * thetime time to format into buf */ -void fmt_time(char *buf, time_t thetime) +void fmt_time(char *buf, size_t siz, time_t thetime) { struct tm *tm; int hour; - char calhourformat[16]; - - get_preference("calhourformat", calhourformat, sizeof calhourformat); - + int time_format; + + time_format = get_time_format_cached (); buf[0] = 0; tm = localtime(&thetime); hour = tm->tm_hour; @@ -151,13 +177,13 @@ void fmt_time(char *buf, time_t thetime) else if (hour > 12) hour = hour - 12; - if (!strcasecmp(calhourformat, "24")) { - sprintf(buf, "%2d:%02d", + if (time_format == WC_TIMEFORMAT_24) { + snprintf(buf, siz, "%d:%02d", tm->tm_hour, tm->tm_min ); } else { - sprintf(buf, "%d:%02d%s", + snprintf(buf, siz, "%d:%02d%s", hour, tm->tm_min, ((tm->tm_hour > 12) ? "pm" : "am") ); } @@ -166,128 +192,116 @@ void fmt_time(char *buf, time_t thetime) -/** - * \brief Break down the timestamp used in HTTP headers +/* + * Break down the timestamp used in HTTP headers * Should read rfc1123 and rfc850 dates OK - * \todo FIXME won't read asctime + * FIXME won't read asctime * Doesn't understand timezone, but we only should be using GMT/UTC anyway - * \param buf time to parse - * \return the time found in buf */ -time_t httpdate_to_timestamp(const char *buf) +time_t httpdate_to_timestamp(StrBuf *buf) { time_t t = 0; struct tm tt; - char *c; - char tz[256]; + const char *c; /** Skip day of week, to number */ - for (c = buf; *c != ' '; c++) + for (c = ChrPtr(buf); *c != ' '; c++) ; c++; + + memset(&tt, 0, sizeof(tt)); /* Get day of month */ tt.tm_mday = atoi(c); for (; *c != ' ' && *c != '-'; c++); c++; - /** Get month */ + /* Get month */ switch (*c) { - case 'A': /** April, August */ + case 'A': /* April, August */ tt.tm_mon = (c[1] == 'p') ? 3 : 7; break; - case 'D': /** December */ + case 'D': /* December */ tt.tm_mon = 11; break; - case 'F': /** February */ + case 'F': /* February */ tt.tm_mon = 1; break; - case 'M': /** March, May */ + case 'M': /* March, May */ tt.tm_mon = (c[2] == 'r') ? 2 : 4; break; - case 'J': /** January, June, July */ + case 'J': /* January, June, July */ tt.tm_mon = (c[2] == 'n') ? ((c[1] == 'a') ? 0 : 5) : 6; break; - case 'N': /** November */ + case 'N': /* November */ tt.tm_mon = 10; break; - case 'O': /** October */ + case 'O': /* October */ tt.tm_mon = 9; break; - case 'S': /** September */ + case 'S': /* September */ tt.tm_mon = 8; break; default: return 42; - break; /** NOTREACHED */ + break; /* NOTREACHED */ } c += 4; tt.tm_year = 0; - /** Get year */ + /* Get year */ tt.tm_year = atoi(c); for (; *c != ' '; c++); c++; if (tt.tm_year >= 1900) tt.tm_year -= 1900; - /** Get hour */ + /* Get hour */ tt.tm_hour = atoi(c); for (; *c != ':'; c++); c++; - /** Get minute */ + /* Get minute */ tt.tm_min = atoi(c); for (; *c != ':'; c++); c++; - /** Get second */ + /* Get second */ tt.tm_sec = atoi(c); for (; *c && *c != ' '; c++); - /** Got everything; let's go */ - /** First, change to UTC */ - if (getenv("TZ")) - sprintf(tz, "TZ=%s", getenv("TZ")); - else - strcpy(tz, "TZ="); - putenv("TZ=UTC"); + /* Got everything; let's go. The global 'timezone' variable contains the + * local timezone's offset from UTC, in seconds, so we apply that to tm_sec. + * This produces an illegal value for tm_sec, but mktime() will normalize + * it for us. This eliminates the need to temporarily switch the environment + * variable TZ to UTC, which is good because it fails to switch back on + * some systems. + */ tzset(); + tt.tm_sec = tt.tm_sec - (int)timezone; t = mktime(&tt); - putenv(tz); - tzset(); return t; } - -/** - * /brief Initialize the strings used to display months and weekdays. - */ -void initialize_months_and_days(void) { - wdays[0] = _("Sunday"); - wdays[1] = _("Monday"); - wdays[2] = _("Tuesday"); - wdays[3] = _("Wednesday"); - wdays[4] = _("Thursday"); - wdays[5] = _("Friday"); - wdays[6] = _("Saturday"); - - months[0] = _("January"); - months[1] = _("February"); - months[2] = _("March"); - months[3] = _("April"); - months[4] = _("May"); - months[5] = _("June"); - months[6] = _("July"); - months[7] = _("August"); - months[8] = _("September"); - months[9] = _("October"); - months[10] = _("November"); - months[11] = _("December"); +void LoadTimeformatSettingsCache(StrBuf *Preference, long lvalue) +{ + int *time_format_cache; + + time_format_cache = &(WC->time_format_cache); + if (lvalue == 24) + *time_format_cache = WC_TIMEFORMAT_24; + else + *time_format_cache = WC_TIMEFORMAT_AMPM; } +void +InitModule_DATETIME +(void) +{ + RegisterPreference("calhourformat", _("Time format"), PRF_INT, LoadTimeformatSettingsCache); + -/*@}*/ +}