1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Time utility functions
6 * Damon Chaplin (damon@ximian.com)
8 * (C) 2001 Ximian, Inc.
13 #define _XOPEN_SOURCE_EXTENDED 1 /* for strptime */
21 #ifdef HAVE_NL_LANGINFO
23 #endif /* HAVE_NL_LANGINFO */
27 #include <glib/gi18n-lib.h>
28 #include "e-time-utils.h"
29 #include "e-data-server-util.h"
36 /* The localtime() in Microsoft's C library is MT-safe */
37 #define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0)
42 get_locale_string (gint lctype)
44 gint nbytes = GetLocaleInfo (GetThreadLocale (), lctype, NULL, 0);
51 tem = g_malloc (nbytes);
53 if (GetLocaleInfo (GetThreadLocale (), lctype, tem, nbytes) == 0) {
58 quark = g_quark_from_string (tem);
61 return g_quark_to_string (quark);
65 translate_picture (const gchar *picture)
67 GString *s = g_string_new ("");
71 const gchar *q = picture + 1;
74 while (*picture == *q)
81 while (*picture && *picture != '\'') {
82 g_string_append_c (s, *picture);
90 g_string_append (s, "%d");
94 g_string_append (s, "%a");
103 g_string_append (s, "%m");
107 g_string_append (s, "%b");
110 picture += count - 1;
114 case 1: /* Last digit of year. Ugh... */
116 g_string_append (s, "%y");
119 g_string_append (s, "%Y");
122 picture += count - 1;
125 /* Era. Huh. Just ignore, as the era stuff
126 * implementation below depends on glibc.
128 picture += count - 1;
131 g_string_append (s, "%I");
132 picture += count - 1;
135 g_string_append (s, "%H");
136 picture += count - 1;
139 g_string_append (s, "%M");
140 picture += count - 1;
143 g_string_append (s, "%S");
144 picture += count - 1;
147 g_string_append (s, "%p");
148 picture += count - 1;
151 g_string_append_c (s, *picture);
158 quark = g_quark_from_string (s->str);
159 g_string_free (s, TRUE);
161 return g_quark_to_string (quark);
166 #ifndef HAVE_STRPTIME
168 /* strptime() implementation lifted from glibc */
170 enum ptime_locale_status { not, loc, raw };
172 /* Copyright (C) 2002, 2004 Free Software Foundation, Inc.
173 * This file is part of the GNU C Library.
175 * The GNU C Library is free software; you can redistribute it and / or
176 * under the terms of the GNU Lesser General Public License as published by
177 * the Free Software Foundation.
179 * The GNU C Library is distributed in the hope that it will be useful, but
180 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
181 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
184 * You should have received a copy of the GNU Lesser General Public License
185 * License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>.
186 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
187 * 02110 - 1301 USA. */
200 # include "../locale/localeinfo.h"
204 # if defined __GNUC__ || (defined __STDC__ && __STDC__)
205 # define __P(args) args
207 # define __P(args) ()
209 #endif /* Not __P. */
211 #if !defined HAVE_LOCALTIME_R && !defined localtime_r
213 # define localtime_r __localtime_r
215 /* Approximate localtime_r as best we can in its absence. */
216 # define localtime_r my_localtime_r
217 static struct tm *localtime_r __P ((const time_t *, struct tm *));
224 struct tm *l = localtime (t);
231 #endif /* HAVE_LOCALTIME_R && !defined (localtime_r) */
233 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
234 #if defined _LIBC && defined __GNUC__ && __GNUC__ >= 2
235 # define match_string(cs1, s2) \
236 ({ gsize len = strlen (cs1); \
237 gint result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
238 if (result) (s2) += len; \
241 /* Oh come on. Get a reasonable compiler. */
242 # define match_string(cs1, s2) \
243 (g_ascii_strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
245 /* We intentionally do not use isdigit() for testing because this will
246 * lead to problems with the wide character version. */
247 #define get_number(from, to, n) \
253 if (*rp < '0' || *rp > '9') \
257 val += *rp++ - '0'; \
258 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
259 if (val < from || val > to) \
263 # define get_alt_number(from, to, n) \
265 __label__ do_normal; \
267 if (*decided != raw) \
269 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
270 if (val == -1 && *decided != loc) \
275 if (val < from || val > to) \
281 get_number (from, to, n); \
286 # define get_alt_number(from, to, n) \
287 /* We don't have the alternate representation. */ \
288 get_number (from, to, n)
290 #define recursive(new_fmt) \
291 (*(new_fmt) != '\0' \
292 && (rp = __strptime_internal (rp, (new_fmt), tm, \
293 decided, era_cnt LOCALE_ARG)) != NULL)
296 /* This is defined in locale/C-time.c in the GNU libc. */
297 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
299 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
300 # define ab_weekday_name \
301 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
302 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
303 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
304 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
305 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
306 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
307 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
308 # define HERE_T_FMT_AMPM \
309 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
310 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
312 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
314 static gchar const weekday_name[][10] =
316 "Sunday", "Monday", "Tuesday", "Wednesday",
317 "Thursday", "Friday", "Saturday"
319 static gchar const ab_weekday_name[][4] =
321 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
323 static gchar const month_name[][10] =
325 "January", "February", "March", "April", "May", "June",
326 "July", "August", "September", "October", "November", "December"
328 static gchar const ab_month_name[][4] =
330 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
331 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
333 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
334 # define HERE_D_FMT "%m/%d/%y"
335 # define HERE_AM_STR "AM"
336 # define HERE_PM_STR "PM"
337 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
338 # define HERE_T_FMT "%H:%M:%S"
340 static const gushort __mon_yday[2][13] =
343 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
345 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
350 /* We use this code also for the extended locale handling where the
351 * function gets as an additional argument the locale which has to be
352 * used. To access the values we have to redefine the _NL_CURRENT
354 # define strptime __strptime_l
356 # define _NL_CURRENT(category, item) \
357 (current->values[_NL_ITEM_INDEX (item)].string)
358 # undef _NL_CURRENT_WORD
359 # define _NL_CURRENT_WORD(category, item) \
360 (current->values[_NL_ITEM_INDEX (item)].word)
361 # define LOCALE_PARAM , locale
362 # define LOCALE_ARG , locale
363 # define LOCALE_PARAM_PROTO , __locale_t locale
364 # define LOCALE_PARAM_DECL __locale_t locale;
365 # define HELPER_LOCALE_ARG , current
366 # define ISSPACE(Ch) __isspace_l (Ch, locale)
368 # define LOCALE_PARAM
370 # define LOCALE_PARAM_DECL
371 # define LOCALE_PARAM_PROTO
372 # define HELPER_LOCALE_ARG
373 # define ISSPACE(Ch) isspace (Ch)
377 /* Nonzero if YEAR is a leap year (every 4 years,
378 * except every 100th isn't, and every 400th is). */
379 # define __isleap(year) \
380 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
383 /* Compute the day of the week. */
385 day_of_the_week (struct tm *tm)
387 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
388 the difference between this data in the one on TM and so determine
390 gint corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
392 + (365 * (tm->tm_year - 70))
394 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
395 + (((corr_year / 4) / 25) / 4)
396 + __mon_yday[0][tm->tm_mon]
398 tm->tm_wday = ((wday % 7) + 7) % 7;
401 /* Compute the day of the year. */
403 day_of_the_year (struct tm *tm)
405 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
406 + (tm->tm_mday - 1));
415 __strptime_internal (rp,
419 era_cnt LOCALE_PARAM)
423 enum ptime_locale_status *decided;
428 struct locale_data *const current = locale->__locales[LC_TIME];
431 const gchar *rp_backup;
435 gint century, want_century;
437 gint have_wday, want_xday;
439 gint have_mon, have_mday;
440 gint have_uweek, have_wweek;
445 struct era_entry *era;
454 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
459 /* A white space in the format string matches 0 more or white
460 space in the input string. */
463 while (ISSPACE (*rp))
469 /* Any character but `%' must be matched by the same character
470 in the iput string. */
473 match_char (*fmt++, *rp++);
479 /* We need this for handling the `E' modifier. */
483 /* Make back up of current processing pointer. */
489 /* Match the `%' character itself. */
490 match_char ('%', *rp++);
494 /* Match day of week. */
495 for (cnt = 0; cnt < 7; ++cnt)
500 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
503 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
508 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
511 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
512 ab_weekday_name[cnt]))
517 #elif defined (G_OS_WIN32)
520 const gchar *locale_str;
522 locale_str = get_locale_string (LOCALE_SDAYNAME1 + cnt);
523 if (match_string (locale_str, rp))
526 && strcmp (locale_str, weekday_name[cnt]))
531 locale_str = get_locale_string (LOCALE_SABBREVDAYNAME1 + cnt);
532 if (match_string (locale_str, rp))
535 && strcmp (locale_str, ab_weekday_name[cnt]))
542 && (match_string (weekday_name[cnt], rp)
543 || match_string (ab_weekday_name[cnt], rp)))
550 /* Does not match a weekday name. */
558 /* Match month name. */
559 for (cnt = 0; cnt < 12; ++cnt)
564 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
567 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
572 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
575 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
581 #elif defined (G_OS_WIN32)
584 const gchar *locale_str;
586 locale_str = get_locale_string (LOCALE_SMONTHNAME1 + cnt);
587 if (match_string (locale_str, rp))
590 && strcmp (locale_str, month_name[cnt]))
595 locale_str = get_locale_string (LOCALE_SABBREVMONTHNAME1 + cnt);
596 if (match_string (locale_str, rp))
599 && strcmp (locale_str, ab_month_name[cnt]))
605 if (match_string (month_name[cnt], rp)
606 || match_string (ab_month_name[cnt], rp))
613 /* Does not match a month name. */
619 /* Match locale's date and time format. */
623 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
632 if (*decided == not &&
633 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
640 #elif defined (G_OS_WIN32)
644 g_strconcat (get_locale_string (LOCALE_SSHORTDATE),
646 get_locale_string (LOCALE_STIMEFORMAT),
648 const gchar *posix_d_t_fmt = translate_picture (d_t_fmt);
652 if (!recursive (posix_d_t_fmt))
661 if (*decided == not &&
662 strcmp (posix_d_t_fmt, HERE_D_T_FMT))
670 if (!recursive (HERE_D_T_FMT))
675 /* Match century number. */
679 get_number (0, 99, 2);
685 /* Match day of month. */
686 get_number (1, 31, 2);
692 if (!recursive ("%Y-%m-%d"))
700 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
710 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
717 #elif defined (G_OS_WIN32)
720 const gchar *picture;
721 const gchar *posix_d_fmt;
723 picture = get_locale_string (LOCALE_SSHORTDATE);
724 posix_d_fmt = translate_picture (picture);
726 if (!recursive (posix_d_fmt))
736 && strcmp (posix_d_fmt, HERE_D_FMT))
746 /* Match standard day format. */
747 if (!recursive (HERE_D_FMT))
753 /* Match hour in 24-hour clock. */
754 get_number (0, 23, 2);
759 /* Match hour in 12-hour clock. GNU extension. */
761 /* Match hour in 12-hour clock. */
762 get_number (1, 12, 2);
763 tm->tm_hour = val % 12;
767 /* Match day number of year. */
768 get_number (1, 366, 3);
769 tm->tm_yday = val - 1;
773 /* Match number of month. */
774 get_number (1, 12, 2);
775 tm->tm_mon = val - 1;
781 get_number (0, 59, 2);
786 /* Match any white space. */
787 while (ISSPACE (*rp))
791 /* Match locale's equivalent of AM/PM. */
795 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
797 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
801 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
803 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
810 #elif defined (G_OS_WIN32)
813 if (match_string (get_locale_string (LOCALE_S1159), rp))
815 if (strcmp (get_locale_string (LOCALE_S1159), HERE_AM_STR))
819 if (match_string (get_locale_string (LOCALE_S2359), rp))
821 if (strcmp (get_locale_string (LOCALE_S2359), HERE_PM_STR))
829 if (!match_string (HERE_AM_STR, rp))
831 if (match_string (HERE_PM_STR, rp))
841 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
850 if (*decided == not &&
851 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
858 #elif defined (G_OS_WIN32)
862 g_strconcat (get_locale_string (LOCALE_STIMEFORMAT),
865 const gchar *posix_t_p_fmt = translate_picture (t_p_fmt);
869 if (!recursive (posix_t_p_fmt))
878 if (*decided == not &&
879 strcmp (posix_t_p_fmt,
887 if (!recursive (HERE_T_FMT_AMPM))
891 if (!recursive ("%H:%M"))
896 /* The number of seconds may be very high so we cannot use
897 the `get_number' macro. Instead read the number
898 character for character and construct the result while
901 if (*rp < '0' || *rp > '9')
902 /* We need at least one digit. */
910 while (*rp >= '0' && *rp <= '9');
912 if (localtime_r (&secs, tm) == NULL)
913 /* Error in function. */
918 get_number (0, 61, 2);
925 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
934 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
940 #elif defined (G_OS_WIN32)
943 const gchar *picture;
944 const gchar *posix_t_fmt;
946 picture = get_local_string (LOCALE_STIMEFORMAT);
947 posix_t_fmt = translate_picture (picture);
949 if (!recursive (posix_t_fmt))
958 if (strcmp (posix_t_fmt, HERE_T_FMT))
967 if (!recursive (HERE_T_FMT))
971 get_number (1, 7, 1);
972 tm->tm_wday = val % 7;
976 get_number (0, 99, 2);
977 /* XXX This cannot determine any field in TM. */
980 if (*rp < '0' || *rp > '9')
982 /* XXX Ignore the number since we would need some more
983 information to compute a real date. */
986 while (*rp >= '0' && *rp <= '9');
989 get_number (0, 53, 2);
994 get_number (0, 53, 2);
999 get_number (0, 53, 2);
1000 /* XXX This cannot determine any field in TM without some
1004 /* Match number of weekday. */
1005 get_number (0, 6, 1);
1011 match_year_in_century:
1013 /* Match year within century. */
1014 get_number (0, 99, 2);
1015 /* The "Year 2000: The Millennium Rollover" paper suggests that
1016 * values in the range 69-99 refer to the twentieth century. */
1017 tm->tm_year = val >= 69 ? val : val + 100;
1018 /* Indicate that we want to use the century, if specified. */
1023 /* Match year including century number. */
1024 get_number (0, 9999, 4);
1025 tm->tm_year = val - 1900;
1030 /* XXX How to handle this? */
1037 /* Match locale's alternate date and time format. */
1038 if (*decided != raw)
1040 const gchar *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
1043 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
1045 if (!recursive (fmt))
1047 if (*decided == loc)
1054 if (strcmp (fmt, HERE_D_T_FMT))
1061 /* The C locale has no era information, so use the
1062 normal representation. */
1063 if (!recursive (HERE_D_T_FMT))
1068 if (*decided != raw)
1072 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1073 if (era != NULL && match_string (era->era_name, rp))
1082 num_eras = _NL_CURRENT_WORD (LC_TIME, _NL_TIME_ERA_NUM_ENTRIES);
1083 for (era_cnt = 0; era_cnt < (gint) num_eras;
1084 ++era_cnt, rp = rp_backup)
1086 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1087 if (era != NULL && match_string (era->era_name, rp))
1093 if (era_cnt != (gint) num_eras)
1097 if (*decided == loc)
1102 /* The C locale has no era information, so use the
1103 normal representation. */
1106 if (*decided != raw)
1108 get_number (0, 9999, 4);
1116 assert (*decided == loc);
1118 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1122 gint delta = ((tm->tm_year - era->offset)
1123 * era->absolute_direction);
1125 && delta < (((int64_t) era->stop_date[0]
1126 - (int64_t) era->start_date[0])
1127 * era->absolute_direction));
1135 num_eras = _NL_CURRENT_WORD (LC_TIME, _NL_TIME_ERA_NUM_ENTRIES);
1136 for (era_cnt = 0; era_cnt < (gint) num_eras; ++era_cnt)
1138 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1141 gint delta = ((tm->tm_year - era->offset)
1142 * era->absolute_direction);
1144 && delta < (((int64_t) era->stop_date[0]
1145 - (int64_t) era->start_date[0])
1146 * era->absolute_direction))
1153 if (era_cnt != (gint) num_eras)
1157 if (*decided == loc)
1163 goto match_year_in_century;
1165 if (*decided != raw)
1167 num_eras = _NL_CURRENT_WORD (
1169 _NL_TIME_ERA_NUM_ENTRIES);
1170 for (era_cnt = 0; era_cnt < (gint) num_eras;
1171 ++era_cnt, rp = rp_backup)
1173 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1174 if (era != NULL && recursive (era->era_format))
1177 if (era_cnt == (gint) num_eras)
1180 if (*decided == loc)
1194 get_number (0, 9999, 4);
1195 tm->tm_year = val - 1900;
1200 if (*decided != raw)
1202 const gchar *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
1205 fmt = _NL_CURRENT (LC_TIME, D_FMT);
1207 if (!recursive (fmt))
1209 if (*decided == loc)
1216 if (strcmp (fmt, HERE_D_FMT))
1222 if (!recursive (HERE_D_FMT))
1226 if (*decided != raw)
1228 const gchar *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
1231 fmt = _NL_CURRENT (LC_TIME, T_FMT);
1233 if (!recursive (fmt))
1235 if (*decided == loc)
1242 if (strcmp (fmt, HERE_T_FMT))
1248 if (!recursive (HERE_T_FMT))
1256 /* We have no information about the era format. Just use
1257 the normal format. */
1258 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1259 && *fmt != 'x' && *fmt != 'X')
1260 /* This is an illegal format. */
1270 /* Match day of month using alternate numeric symbols. */
1271 get_alt_number (1, 31, 2);
1277 /* Match hour in 24-hour clock using alternate numeric
1279 get_alt_number (0, 23, 2);
1284 /* Match hour in 12-hour clock using alternate numeric
1286 get_alt_number (1, 12, 2);
1287 tm->tm_hour = val % 12;
1291 /* Match month using alternate numeric symbols. */
1292 get_alt_number (1, 12, 2);
1293 tm->tm_mon = val - 1;
1298 /* Match minutes using alternate numeric symbols. */
1299 get_alt_number (0, 59, 2);
1303 /* Match seconds using alternate numeric symbols. */
1304 get_alt_number (0, 61, 2);
1308 get_alt_number (0, 53, 2);
1313 get_alt_number (0, 53, 2);
1318 get_alt_number (0, 53, 2);
1319 /* XXX This cannot determine any field in TM without
1320 further information. */
1323 /* Match number of weekday using alternate numeric symbols. */
1324 get_alt_number (0, 6, 1);
1329 /* Match year within century using alternate numeric symbols. */
1330 get_alt_number (0, 99, 2);
1331 tm->tm_year = val >= 69 ? val : val + 100;
1343 if (have_I && is_pm)
1349 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1351 /* Only the century, but not the year. Strange, but so be it. */
1352 tm->tm_year = (century - 19) * 100;
1358 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1362 tm->tm_year = (era->start_date[0]
1363 + ((tm->tm_year - era->offset)
1364 * era->absolute_direction));
1366 /* Era start year assumed. */
1367 tm->tm_year = era->start_date[0];
1373 /* No era found but we have seen an E modifier. Rectify some
1375 if (want_century && century == -1 && tm->tm_year < 69)
1379 if (want_xday && !have_wday)
1381 if ( !(have_mon && have_mday) && have_yday)
1383 /* We don't have tm_mon and/or tm_mday, compute them. */
1385 while (__mon_yday[__isleap (1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1388 tm->tm_mon = t_mon - 1;
1392 - __mon_yday[__isleap (1900 + tm->tm_year)][t_mon - 1] + 1);
1394 day_of_the_week (tm);
1397 if (want_xday && !have_yday)
1398 day_of_the_year (tm);
1400 if ((have_uweek || have_wweek) && have_wday)
1402 gint save_wday = tm->tm_wday;
1403 gint save_mday = tm->tm_mday;
1404 gint save_mon = tm->tm_mon;
1405 gint w_offset = have_uweek ? 0 : 1;
1409 day_of_the_week (tm);
1411 tm->tm_mday = save_mday;
1413 tm->tm_mon = save_mon;
1416 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1418 + save_wday - w_offset);
1420 if (!have_mday || !have_mon)
1423 while (__mon_yday[__isleap (1900 + tm->tm_year)][t_mon]
1427 tm->tm_mon = t_mon - 1;
1431 - __mon_yday[__isleap (1900 + tm->tm_year)][t_mon - 1] + 1);
1434 tm->tm_wday = save_wday;
1437 return (gchar *) rp;
1445 const gchar *format;
1449 enum ptime_locale_status decided;
1453 #elif defined (G_OS_WIN32)
1458 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1462 weak_alias (__strptime_l,
1465 #endif /* HAVE_STRPTIME */
1467 /* Returns whether a string is NULL, empty, or full of whitespace */
1469 string_is_empty (const gchar *value)
1472 gboolean empty = TRUE;
1477 if (!isspace (*p)) {
1487 /* Takes a number of format strings for strptime() and attempts to parse a
1490 static ETimeParseStatus
1491 parse_with_strptime (const gchar *value,
1493 const gchar **formats,
1496 const gchar *parse_end = NULL, *pos;
1499 ETimeParseStatus parse_ret;
1502 if (string_is_empty (value)) {
1503 memset (result, 0, sizeof (*result));
1504 result->tm_isdst = -1;
1505 return E_TIME_PARSE_NONE;
1508 parse_ret = E_TIME_PARSE_INVALID;
1509 locale_str = g_locale_from_utf8 (value, -1, NULL, NULL, NULL);
1510 pos = (const gchar *) locale_str;
1513 return E_TIME_PARSE_INVALID;
1515 /* Skip whitespace */
1516 while (n = (gint)((guchar) * pos), isspace (n) != 0)
1519 /* Try each of the formats in turn */
1521 for (i = 0; i < n_formats; i++) {
1522 memset (result, 0, sizeof (*result));
1523 format_str = g_locale_from_utf8 (formats[i], -1, NULL, NULL, NULL);
1524 parse_end = strptime (pos, format_str, result);
1525 g_free (format_str);
1527 /* If we parsed something, make sure we parsed the entire string. */
1528 /* Skip whitespace */
1529 while (isspace (*parse_end))
1532 if (*parse_end == '\0') {
1533 parse_ret = E_TIME_PARSE_OK;
1539 result->tm_isdst = -1;
1541 g_free (locale_str);
1548 correct_two_digit_year (struct tm *result,
1549 gboolean *two_digit_year)
1551 g_return_if_fail (result != NULL);
1554 *two_digit_year = FALSE;
1556 /* If a 2-digit year was used we use the current century. */
1557 if (result->tm_year < 0 && result->tm_year < -1800) {
1558 time_t t = time (NULL);
1559 struct tm *today_tm = localtime (&t);
1561 /* This should convert it into a value from 0 to 99. */
1562 result->tm_year += 1900;
1564 /* Now add on the century. */
1565 result->tm_year += today_tm->tm_year
1566 - (today_tm->tm_year % 100);
1569 *two_digit_year = TRUE;
1573 /* Returns TRUE if the locale has 'am' and 'pm' strings defined, in which
1574 * case the user can choose between 12 and 24-hour time formats. */
1576 locale_supports_12_hour_format (void)
1578 struct tm tmp_tm = { 0 };
1581 e_utf8_strftime (s, sizeof (s), "%p", &tmp_tm);
1582 return s[0] != '\0';
1586 has_correct_date (const struct tm *value)
1588 const gint days_in_month[12] = {
1589 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
1592 g_return_val_if_fail (value != NULL, FALSE);
1593 g_return_val_if_fail (value->tm_mon >= 0 && value->tm_mon < 12, FALSE);
1595 year = value->tm_year + 1900;
1596 days = days_in_month[value->tm_mon];
1597 if (value->tm_mon == 1 &&
1598 ((year <= 1752) ? (!(year % 4)) :
1599 ((!(year % 4) && (year % 100)) || !(year % 400))))
1602 return value->tm_mday >= 1 && value->tm_mday <= days;
1606 * e_time_parse_date_and_time_ex:
1607 * @value: The string to parse a date and time from.
1608 * @result: A #tm to store the result in.
1609 * @two_digit_year: set to TRUE, is parsing with two-digit year, else FALSE,
1610 * but only when not NULL.
1612 * Parses a string @value containing a date and a time and stores the
1613 * result in @result. The date in @value is expected to be in a format
1614 * like "Wed 3/13/00 14:20:00", though gettext() is used to support the
1615 * appropriate local formats. There is also some leniency on the
1616 * format of the string, e.g. the weekday can be skipped or 12-hour
1617 * formats with am/pm can be used.
1619 * Returns: E_TIME_PARSE_OK if the string was successfully parsed,
1620 * E_TIME_PARSE_NONE if the string was empty, or
1621 * E_TIME_PARSE_INVALID if the string could not be parsed.
1626 e_time_parse_date_and_time_ex (const gchar *value,
1628 gboolean *two_digit_year)
1630 struct tm *today_tm;
1632 const gchar *format[16];
1633 gint num_formats = 0;
1634 gboolean use_12_hour_formats = locale_supports_12_hour_format ();
1635 ETimeParseStatus status;
1637 if (string_is_empty (value)) {
1638 memset (result, 0, sizeof (*result));
1639 result->tm_isdst = -1;
1640 return E_TIME_PARSE_NONE;
1643 /* We'll parse the whole date and time in one go, otherwise we get
1644 * into i18n problems. We attempt to parse with several formats,
1645 * longest first. Note that we only use the '%p' specifier if the
1646 * locale actually has 'am' and 'pm' strings defined, otherwise we
1647 * will get incorrect results. Note also that we try to use exactly
1648 * the same strings as in e_time_format_date_and_time (), to try to
1649 * avoid i18n problems. We also use cut-down versions, so users don't
1650 * have to type in the weekday or the seconds, for example.
1651 * Note that all these formats include the full date, and the time
1652 * will be set to 00:00:00 before parsing, so we don't need to worry
1653 * about filling in any missing fields after parsing. */
1656 * Try the full times, with the weekday. Then try without seconds,
1657 * and without minutes, and finally with no time at all.
1659 if (use_12_hour_formats) {
1660 /* strptime format of a weekday, a date and a time,
1661 * in 12-hour format. */
1662 format[num_formats++] = _("%a %m/%d/%Y %I:%M:%S %p");
1665 /* strptime format of a weekday, a date and a time,
1666 * in 24-hour format. */
1667 format[num_formats++] = _("%a %m/%d/%Y %H:%M:%S");
1669 if (use_12_hour_formats) {
1670 /* strptime format of a weekday, a date and a time,
1671 * in 12-hour format, without seconds. */
1672 format[num_formats++] = _("%a %m/%d/%Y %I:%M %p");
1675 /* strptime format of a weekday, a date and a time,
1676 * in 24-hour format, without seconds. */
1677 format[num_formats++] = _("%a %m/%d/%Y %H:%M");
1679 if (use_12_hour_formats) {
1680 /* strptime format of a weekday, a date and a time,
1681 * in 12-hour format, without minutes or seconds. */
1682 format[num_formats++] = _("%a %m/%d/%Y %I %p");
1685 /* strptime format of a weekday, a date and a time,
1686 * in 24-hour format, without minutes or seconds. */
1687 format[num_formats++] = _("%a %m/%d/%Y %H");
1689 /* strptime format of a weekday and a date. */
1690 format[num_formats++] = _("%a %m/%d/%Y");
1693 * Now try all the above formats again, but without the weekday.
1695 if (use_12_hour_formats) {
1696 /* strptime format of a date and a time, in 12-hour format. */
1697 format[num_formats++] = _("%m/%d/%Y %I:%M:%S %p");
1700 /* strptime format of a date and a time, in 24-hour format. */
1701 format[num_formats++] = _("%m/%d/%Y %H:%M:%S");
1703 if (use_12_hour_formats) {
1704 /* strptime format of a date and a time, in 12-hour format,
1705 * without seconds. */
1706 format[num_formats++] = _("%m/%d/%Y %I:%M %p");
1709 /* strptime format of a date and a time, in 24-hour format,
1710 * without seconds. */
1711 format[num_formats++] = _("%m/%d/%Y %H:%M");
1713 if (use_12_hour_formats) {
1714 /* strptime format of a date and a time, in 12-hour format,
1715 * without minutes or seconds. */
1716 format[num_formats++] = _("%m/%d/%Y %I %p");
1719 /* strptime format of a date and a time, in 24-hour format,
1720 * without minutes or seconds. */
1721 format[num_formats++] = _("%m/%d/%Y %H");
1723 /* strptime format of a weekday and a date. */
1724 format[num_formats++] = _("%m/%d/%Y");
1727 *two_digit_year = FALSE;
1729 status = parse_with_strptime (value, result, format, num_formats);
1731 if (status == E_TIME_PARSE_OK && !has_correct_date (result))
1732 status = E_TIME_PARSE_INVALID;
1734 /* Note that we checked if it was empty already, so it is either OK
1735 * or INVALID here. */
1736 if (status == E_TIME_PARSE_OK) {
1737 correct_two_digit_year (result, two_digit_year);
1739 /* Now we try to just parse a time, assuming the current day.*/
1740 status = e_time_parse_time (value, result);
1741 if (status == E_TIME_PARSE_OK) {
1742 /* We fill in the current day. */
1744 today_tm = localtime (&t);
1745 result->tm_mday = today_tm->tm_mday;
1746 result->tm_mon = today_tm->tm_mon;
1747 result->tm_year = today_tm->tm_year;
1755 * e_time_parse_date_and_time:
1756 * @value: the string to parse a date and time from
1757 * @result: a #tm to store the result in
1759 * Parses a string @value containing a date and a time and stores the
1760 * result in @result. The date in @value is expected to be in a format
1761 * like "Wed 3/13/00 14:20:00", though gettext() is used to support the
1762 * appropriate local formats. There is also some leniency on the
1763 * format of the string, e.g. the weekday can be skipped or 12-hour
1764 * formats with am/pm can be used.
1766 * Returns: E_TIME_PARSE_OK if the string was successfully parsed,
1767 * E_TIME_PARSE_NONE if the string was empty, or
1768 * E_TIME_PARSE_INVALID if the string could not be parsed.
1771 e_time_parse_date_and_time (const gchar *value,
1774 return e_time_parse_date_and_time_ex (value, result, NULL);
1778 * e_time_parse_date_ex:
1779 * @value: A date string.
1780 * @result: Return value for the parsed date.
1781 * @two_digit_year: set to TRUE, is parsing with two-digit year, else FALSE,
1782 * but only when not NULL.
1784 * Takes in a date string entered by the user and tries to convert it to
1787 * Returns: An #ETimeParseStatus result code indicating whether
1788 * @value was an empty string, a valid date, or an invalid date.
1793 e_time_parse_date_ex (const gchar *value,
1795 gboolean *two_digit_year)
1797 const gchar *format[4];
1798 ETimeParseStatus status;
1800 g_return_val_if_fail (value != NULL, E_TIME_PARSE_INVALID);
1801 g_return_val_if_fail (result != NULL, E_TIME_PARSE_INVALID);
1803 /* according to the current locale */
1806 /* according to the current locale with forced 4-digit year*/
1807 format[1] = e_time_get_d_fmt_with_4digit_year ();
1809 /* strptime format of a weekday and a date. */
1810 format[2] = _("%a %m/%d/%Y");
1812 /* This is the preferred date format for the locale. */
1813 format[3] = _("%m/%d/%Y");
1815 if (two_digit_year) {
1816 /* when we need to know about two digit year, then always first try
1817 * full year, because for example nl_NL have format %d-%m-%y and it
1818 * changes from two year itself, which isn't what we want */
1819 const gchar *tmp = format[1];
1820 format[1] = format[0];
1822 *two_digit_year = FALSE;
1825 status = parse_with_strptime (value, result, format, G_N_ELEMENTS (format));
1827 if (status == E_TIME_PARSE_OK && !has_correct_date (result))
1828 status = E_TIME_PARSE_INVALID;
1830 if (status == E_TIME_PARSE_OK) {
1831 correct_two_digit_year (result, two_digit_year);
1835 g_free ((gchar *) format[0]);
1837 g_free ((gchar *) format[1]);
1843 * e_time_parse_date:
1844 * @value: A date string.
1845 * @result: Return value for the parsed date.
1847 * Takes in a date string entered by the user and tries to convert it to
1850 * Returns: An #ETimeParseStatus result code indicating whether
1851 * @value was an empty string, a valid date, or an invalid date.
1854 e_time_parse_date (const gchar *value,
1857 return e_time_parse_date_ex (value, result, NULL);
1861 * e_time_parse_time:
1862 * @value: The string to parse a time from.
1863 * @result: A #tm to store the result in.
1865 * Parses @value, a string containing a time. @value is expected to be
1866 * in a format like "14:20:00". gettext() is used to
1867 * support the appropriate local formats and slightly
1868 * different formats, such as 12-hour formats with am/pm,
1869 * are accepted as well.
1871 * Returns: An #ETimeParseStatus result code indicating whether
1872 * @value was an empty string, a valid date, or an invalid date.
1875 e_time_parse_time (const gchar *value,
1878 const gchar *format[7];
1879 gint num_formats = 0;
1880 gboolean use_12_hour_formats = locale_supports_12_hour_format ();
1882 if (use_12_hour_formats) {
1883 /* strptime format for a time of day, in 12-hour format. */
1884 format[num_formats++] = _("%I:%M:%S %p");
1887 /* strptime format for a time of day, in 24-hour format. */
1888 format[num_formats++] = _("%H:%M:%S");
1890 if (use_12_hour_formats) {
1891 /* strptime format for time of day, without seconds,
1892 * in 12-hour format. */
1893 format[num_formats++] = _("%I:%M %p");
1896 /* strptime format for time of day, without seconds 24-hour format. */
1897 format[num_formats++] = _("%H:%M");
1899 /* strptime format for time of day, without seconds 24-hour format,
1901 format[num_formats++] = _("%H%M");
1903 if (use_12_hour_formats) {
1904 /* strptime format for hour and AM/PM, 12-hour format. */
1905 format[num_formats++] = _("%I %p");
1908 /* strptime format for hour, 24-hour format. */
1909 format[num_formats++] = "%H";
1911 return parse_with_strptime (value, result, format, num_formats);
1915 * e_time_format_date_and_time:
1916 * @date_tm: The #tm to convert to a string.
1917 * @use_24_hour_format: A #gboolean.
1918 * @show_midnight: A #gboolean.
1919 * @show_zero_seconds: A #gboolean.
1920 * @buffer: A #char buffer to store the time string in.
1921 * @buffer_size: The length of @buffer.
1923 * Creates a string representation of the time value @date_tm and
1924 * stores it in @buffer. @buffer_size should be at least 64 to be
1925 * safe. If @show_midnight is %FALSE, and the time is midnight, then
1926 * only the date is stored in @buffer. If @show_zero_seconds is
1927 * %FALSE, then if the time has zero seconds only the hour and minute
1928 * of the time are stored in @buffer.
1931 e_time_format_date_and_time (struct tm *date_tm,
1932 gboolean use_24_hour_format,
1933 gboolean show_midnight,
1934 gboolean show_zero_seconds,
1940 if (!show_midnight && date_tm->tm_hour == 0
1941 && date_tm->tm_min == 0 && date_tm->tm_sec == 0) {
1942 /* strftime format of a weekday and a date. */
1943 format = _("%a %m/%d/%Y");
1944 } else if (use_24_hour_format) {
1945 if (!show_zero_seconds && date_tm->tm_sec == 0)
1946 /* strftime format of a weekday, a date and a
1947 * time, in 24-hour format, without seconds. */
1948 format = _("%a %m/%d/%Y %H:%M");
1950 /* strftime format of a weekday, a date and a
1951 * time, in 24-hour format. */
1952 format = _("%a %m/%d/%Y %H:%M:%S");
1954 if (!show_zero_seconds && date_tm->tm_sec == 0)
1955 /* strftime format of a weekday, a date and a
1956 * time, in 12-hour format, without seconds. */
1957 format = _("%a %m/%d/%Y %I:%M %p");
1959 /* strftime format of a weekday, a date and a
1960 * time, in 12-hour format. */
1961 format = _("%a %m/%d/%Y %I:%M:%S %p");
1964 /* strftime returns 0 if the string doesn't fit, and leaves the buffer
1965 * undefined, so we set it to the empty string in that case. */
1966 if (e_utf8_strftime (buffer, buffer_size, format, date_tm) == 0)
1971 * e_time_format_time:
1972 * @date_tm: The #tm to convert to a string.
1973 * @use_24_hour_format: A #gboolean.
1974 * @show_zero_seconds: A #gboolean.
1975 * @buffer: The #char buffer to store the result in.
1976 * @buffer_size: The length of @buffer.
1978 * Creates a string representation of a time value in @date_tm and
1979 * stores it in @buffer. @buffer_size should be at least 64.
1982 e_time_format_time (struct tm *date_tm,
1983 gboolean use_24_hour_format,
1984 gboolean show_zero_seconds,
1990 if (use_24_hour_format) {
1991 if (!show_zero_seconds && date_tm->tm_sec == 0)
1992 /* strftime format of a time in 24-hour format,
1993 * without seconds. */
1994 format = _("%H:%M");
1996 /* strftime format of a time in 24-hour format. */
1997 format = _("%H:%M:%S");
1999 if (!show_zero_seconds && date_tm->tm_sec == 0)
2000 /* strftime format of a time in 12-hour format,
2001 * without seconds. */
2002 format = _("%I:%M %p");
2004 /* strftime format of a time in 12-hour format. */
2005 format = _("%I:%M:%S %p");
2008 /* strftime returns 0 if the string doesn't fit, and leaves the buffer
2009 * undefined, so we set it to the empty string in that case. */
2010 if (e_utf8_strftime (buffer, buffer_size, format, date_tm) == 0)
2016 * @tm: The #tm to convert to a calendar time representation.
2018 * Like mktime(3), but assumes UTC instead of local timezone.
2020 * Returns: The calendar time representation of @tm.
2023 e_mktime_utc (struct tm *tm)
2030 #if defined (HAVE_TM_GMTOFF)
2031 tt += tm->tm_gmtoff;
2032 #elif defined (HAVE_TIMEZONE)
2033 if (tm->tm_isdst > 0) {
2034 #if defined (HAVE_ALTZONE)
2036 #else /* !defined (HAVE_ALTZONE) */
2037 tt -= (timezone - 3600);
2047 * e_localtime_with_offset:
2048 * @tt: The #time_t to convert.
2049 * @tm: The #tm to store the result in.
2050 * @offset: The #int to store the offset in.
2052 * Converts the calendar time time representation @tt to a broken-down
2053 * time representation, store in @tm, and provides the offset in
2054 * seconds from UTC time, stored in @offset.
2057 e_localtime_with_offset (time_t tt,
2061 localtime_r (&tt, tm);
2063 #if defined (HAVE_TM_GMTOFF)
2064 *offset = tm->tm_gmtoff;
2065 #elif defined (HAVE_TIMEZONE)
2066 if (tm->tm_isdst > 0) {
2067 #if defined (HAVE_ALTZONE)
2069 #else /* !defined (HAVE_ALTZONE) */
2070 *offset = -(timezone - 3600);
2073 *offset = -timezone;
2078 static gint _e_string_replace (gchar **str, const gchar *old, const gchar *new)
2080 GRegex *my_regex = g_regex_new (old, 0, 0, NULL);
2082 *str = g_regex_replace(my_regex, buf, -1, 0, new, 0, NULL);
2084 g_regex_unref (my_regex);
2090 * e_time_get_d_fmt_with_4digit_year:
2092 * Retrieves a date format string with a 4-digit year (D_FMT on systems with
2093 * nl_langinfo() available). Free the returned string with g_free().
2095 * Returns: a newly-allocated date format string
2100 e_time_get_d_fmt_with_4digit_year (void)
2104 #if defined(HAVE_NL_LANGINFO)
2105 res = g_strdup (nl_langinfo (D_FMT) );
2106 #elif defined(G_OS_WIN32)
2107 #define GET_LOCALE_INFO(str, len) \
2108 GetLocaleInfoA (LOCALE_USER_DEFAULT, LOCALE_SLONGDATE, str, len)
2109 gint format_string_length = GET_LOCALE_INFO (NULL, 0);
2110 if (format_string_length > 0) {
2111 gsize format_bytes_read;
2112 gsize format_bytes_written;
2113 gchar *format_string;
2115 format_string = g_strnfill (format_string_length + 1, '\0');
2116 GET_LOCALE_INFO (format_string, format_string_length);
2117 res = g_locale_to_utf8 (
2119 format_string_length,
2121 &format_bytes_written,
2123 g_free (format_string);
2125 /* now, convert the res to format of nl_langinfo */
2126 _e_string_replace (&res, "\\bd\\b", "%#d"); /* d -> %#d */
2127 _e_string_replace (&res, "\\bdd\\b", "%d"); /* dd -> %d */
2128 _e_string_replace (&res, "\\bddd\\b", "%a"); /* ddd -> %a */
2129 _e_string_replace (&res, "\\bdddd\\b", "%A"); /* dddd -> %A */
2130 _e_string_replace (&res, "\\bM\\b", "%#m"); /* M -> %#m */
2131 _e_string_replace (&res, "\\bMM\\b", "%m"); /* MM -> %m */
2132 _e_string_replace (&res, "\\bMMM\\b", "%b"); /* MMM -> %b */
2133 _e_string_replace (&res, "\\bMMMM\\b", "%B"); /* MMMM -> %B */
2134 _e_string_replace (&res, "\\by\\b", "%#y"); /* y -> %y */
2135 _e_string_replace (&res, "\\byy\\b", "%y"); /* yy -> %y */
2136 _e_string_replace (&res, "\\byyyy\\b", "%Y"); /* yyyy -> %Y */
2137 _e_string_replace (&res, "\\byyyyy\\b", "%Y"); /* yyyyy -> %Y */
2139 #undef GET_LOCALE_INFO
2140 /* TODO Implement this for other systems. */
2142 /* This will not work for other systems. */
2143 res = g_strdup ("%x");
2146 while (p = strchr (res, 'y'), p)