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.
14 /* We need this to get a prototype for strptime. */
16 #endif /* __linux__ */
23 #endif /* __linux__ */
28 #include <glib/gi18n-lib.h>
29 #include "e-time-utils.h"
30 #include "e-data-server-util.h"
33 /* The localtime_r() definition in pthreads-win32's pthread.h doesn't guard
34 * against localtime() returning NULL.
37 /* The localtime() in Microsoft's C library is MT-safe */
38 #define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0)
43 get_locale_string (int lctype)
45 int nbytes = GetLocaleInfo (GetThreadLocale (), lctype, NULL, 0);
52 tem = g_malloc (nbytes);
54 if (GetLocaleInfo (GetThreadLocale (), lctype, tem, nbytes) == 0) {
59 quark = g_quark_from_string (tem);
62 return g_quark_to_string (quark);
66 translate_picture (const char *picture)
68 GString *s = g_string_new ("");
72 const char *q = picture + 1;
75 while (*picture == *q)
82 while (*picture && *picture != '\'') {
83 g_string_append_c (s, *picture);
91 g_string_append (s, "%d");
95 g_string_append (s, "%a");
104 g_string_append (s, "%m");
108 g_string_append (s, "%b");
111 picture += count - 1;
115 case 1: /* Last digit of year. Ugh... */
117 g_string_append (s, "%y");
120 g_string_append (s, "%Y");
123 picture += count - 1;
126 /* Era. Huh. Just ignore, as the era stuff
127 * implementation below depends on glibc.
129 picture += count - 1;
132 g_string_append (s, "%I");
133 picture += count - 1;
136 g_string_append (s, "%H");
137 picture += count - 1;
140 g_string_append (s, "%M");
141 picture += count - 1;
144 g_string_append (s, "%S");
145 picture += count - 1;
148 g_string_append (s, "%p");
149 picture += count - 1;
152 g_string_append_c (s, *picture);
159 quark = g_quark_from_string (s->str);
160 g_string_free (s, TRUE);
162 return g_quark_to_string (quark);
167 #ifndef HAVE_STRPTIME
169 /* strptime() implementation lifted from glibc */
171 enum ptime_locale_status { not, loc, raw };
173 /* Copyright (C) 2002, 2004 Free Software Foundation, Inc.
174 This file is part of the GNU C Library.
176 The GNU C Library is free software; you can redistribute it and/or
177 modify it under the terms of the GNU Lesser General Public
178 License as published by the Free Software Foundation; either
179 version 2.1 of the License, or (at your option) any later version.
181 The GNU C Library is distributed in the hope that it will be useful,
182 but WITHOUT ANY WARRANTY; without even the implied warranty of
183 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
184 Lesser General Public License for more details.
186 You should have received a copy of the GNU Lesser General Public
187 License along with the GNU C Library; if not, write to the Free
188 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
202 # include "../locale/localeinfo.h"
207 # if defined __GNUC__ || (defined __STDC__ && __STDC__)
208 # define __P(args) args
210 # define __P(args) ()
212 #endif /* Not __P. */
215 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
217 # define localtime_r __localtime_r
219 /* Approximate localtime_r as best we can in its absence. */
220 # define localtime_r my_localtime_r
221 static struct tm *localtime_r __P ((const time_t *, struct tm *));
227 struct tm *l = localtime (t);
233 # endif /* ! _LIBC */
234 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
237 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
238 #if defined _LIBC && defined __GNUC__ && __GNUC__ >= 2
239 # define match_string(cs1, s2) \
240 ({ size_t len = strlen (cs1); \
241 int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0; \
242 if (result) (s2) += len; \
245 /* Oh come on. Get a reasonable compiler. */
246 # define match_string(cs1, s2) \
247 (g_ascii_strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
249 /* We intentionally do not use isdigit() for testing because this will
250 lead to problems with the wide character version. */
251 #define get_number(from, to, n) \
257 if (*rp < '0' || *rp > '9') \
261 val += *rp++ - '0'; \
262 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
263 if (val < from || val > to) \
267 # define get_alt_number(from, to, n) \
269 __label__ do_normal; \
271 if (*decided != raw) \
273 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \
274 if (val == -1 && *decided != loc) \
279 if (val < from || val > to) \
285 get_number (from, to, n); \
290 # define get_alt_number(from, to, n) \
291 /* We don't have the alternate representation. */ \
292 get_number(from, to, n)
294 #define recursive(new_fmt) \
295 (*(new_fmt) != '\0' \
296 && (rp = __strptime_internal (rp, (new_fmt), tm, \
297 decided, era_cnt LOCALE_ARG)) != NULL)
301 /* This is defined in locale/C-time.c in the GNU libc. */
302 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
304 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
305 # define ab_weekday_name \
306 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
307 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
308 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
309 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
310 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
311 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
312 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
313 # define HERE_T_FMT_AMPM \
314 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
315 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
317 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
319 static char const weekday_name[][10] =
321 "Sunday", "Monday", "Tuesday", "Wednesday",
322 "Thursday", "Friday", "Saturday"
324 static char const ab_weekday_name[][4] =
326 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
328 static char const month_name[][10] =
330 "January", "February", "March", "April", "May", "June",
331 "July", "August", "September", "October", "November", "December"
333 static char const ab_month_name[][4] =
335 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
336 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
338 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
339 # define HERE_D_FMT "%m/%d/%y"
340 # define HERE_AM_STR "AM"
341 # define HERE_PM_STR "PM"
342 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
343 # define HERE_T_FMT "%H:%M:%S"
345 static const unsigned short int __mon_yday[2][13] =
348 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
350 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
355 /* We use this code also for the extended locale handling where the
356 function gets as an additional argument the locale which has to be
357 used. To access the values we have to redefine the _NL_CURRENT
359 # define strptime __strptime_l
361 # define _NL_CURRENT(category, item) \
362 (current->values[_NL_ITEM_INDEX (item)].string)
363 # undef _NL_CURRENT_WORD
364 # define _NL_CURRENT_WORD(category, item) \
365 (current->values[_NL_ITEM_INDEX (item)].word)
366 # define LOCALE_PARAM , locale
367 # define LOCALE_ARG , locale
368 # define LOCALE_PARAM_PROTO , __locale_t locale
369 # define LOCALE_PARAM_DECL __locale_t locale;
370 # define HELPER_LOCALE_ARG , current
371 # define ISSPACE(Ch) __isspace_l (Ch, locale)
373 # define LOCALE_PARAM
375 # define LOCALE_PARAM_DECL
376 # define LOCALE_PARAM_PROTO
377 # define HELPER_LOCALE_ARG
378 # define ISSPACE(Ch) isspace (Ch)
385 /* Nonzero if YEAR is a leap year (every 4 years,
386 except every 100th isn't, and every 400th is). */
387 # define __isleap(year) \
388 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
391 /* Compute the day of the week. */
393 day_of_the_week (struct tm *tm)
395 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
396 the difference between this data in the one on TM and so determine
398 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
400 + (365 * (tm->tm_year - 70))
402 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
403 + (((corr_year / 4) / 25) / 4)
404 + __mon_yday[0][tm->tm_mon]
406 tm->tm_wday = ((wday % 7) + 7) % 7;
409 /* Compute the day of the year. */
411 day_of_the_year (struct tm *tm)
413 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
414 + (tm->tm_mday - 1));
424 __strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
428 enum ptime_locale_status *decided;
433 struct locale_data *const current = locale->__locales[LC_TIME];
436 const char *rp_backup;
440 int century, want_century;
442 int have_wday, want_xday;
444 int have_mon, have_mday;
445 int have_uweek, have_wweek;
450 struct era_entry *era;
459 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
464 /* A white space in the format string matches 0 more or white
465 space in the input string. */
468 while (ISSPACE (*rp))
474 /* Any character but `%' must be matched by the same character
475 in the iput string. */
478 match_char (*fmt++, *rp++);
484 /* We need this for handling the `E' modifier. */
488 /* Make back up of current processing pointer. */
494 /* Match the `%' character itself. */
495 match_char ('%', *rp++);
499 /* Match day of week. */
500 for (cnt = 0; cnt < 7; ++cnt)
505 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
508 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
513 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
516 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
517 ab_weekday_name[cnt]))
522 #elif defined (G_OS_WIN32)
525 if (match_string (get_locale_string (LOCALE_SDAYNAME1 + cnt), rp))
528 && strcmp (get_locale_string (LOCALE_SDAYNAME1 + cnt),
533 if (match_string (get_locale_string (LOCALE_SABBREVDAYNAME1 + cnt), rp))
536 && strcmp (get_locale_string (LOCALE_SABBREVDAYNAME1 + cnt),
537 ab_weekday_name[cnt]))
544 && (match_string (weekday_name[cnt], rp)
545 || match_string (ab_weekday_name[cnt], rp)))
552 /* Does not match a weekday name. */
560 /* Match month name. */
561 for (cnt = 0; cnt < 12; ++cnt)
566 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
569 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
574 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
577 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
583 #elif defined (G_OS_WIN32)
586 if (match_string (get_locale_string (LOCALE_SMONTHNAME1 + cnt), rp))
589 && strcmp (get_locale_string (LOCALE_SMONTHNAME1 + cnt),
594 if (match_string (get_locale_string (LOCALE_SABBREVMONTHNAME1 + cnt), rp))
597 && strcmp (get_locale_string (LOCALE_SABBREVMONTHNAME1 + cnt),
604 if (match_string (month_name[cnt], rp)
605 || match_string (ab_month_name[cnt], rp))
612 /* Does not match a month name. */
618 /* Match locale's date and time format. */
622 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
631 if (*decided == not &&
632 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
639 #elif defined (G_OS_WIN32)
643 g_strconcat (get_locale_string (LOCALE_SSHORTDATE),
645 get_locale_string (LOCALE_STIMEFORMAT),
647 const char *posix_d_t_fmt = translate_picture (d_t_fmt);
651 if (!recursive (posix_d_t_fmt))
660 if (*decided == not &&
661 strcmp (posix_d_t_fmt, HERE_D_T_FMT))
669 if (!recursive (HERE_D_T_FMT))
674 /* Match century number. */
678 get_number (0, 99, 2);
684 /* Match day of month. */
685 get_number (1, 31, 2);
691 if (!recursive ("%Y-%m-%d"))
699 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
709 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
716 #elif defined (G_OS_WIN32)
719 const char *posix_d_fmt = translate_picture (get_locale_string (LOCALE_SSHORTDATE));
720 if (!recursive (posix_d_fmt))
730 && strcmp (posix_d_fmt, HERE_D_FMT))
740 /* Match standard day format. */
741 if (!recursive (HERE_D_FMT))
747 /* Match hour in 24-hour clock. */
748 get_number (0, 23, 2);
753 /* Match hour in 12-hour clock. GNU extension. */
755 /* Match hour in 12-hour clock. */
756 get_number (1, 12, 2);
757 tm->tm_hour = val % 12;
761 /* Match day number of year. */
762 get_number (1, 366, 3);
763 tm->tm_yday = val - 1;
767 /* Match number of month. */
768 get_number (1, 12, 2);
769 tm->tm_mon = val - 1;
775 get_number (0, 59, 2);
780 /* Match any white space. */
781 while (ISSPACE (*rp))
785 /* Match locale's equivalent of AM/PM. */
789 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
791 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
795 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
797 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
804 #elif defined (G_OS_WIN32)
807 if (match_string (get_locale_string (LOCALE_S1159), rp))
809 if (strcmp (get_locale_string (LOCALE_S1159), HERE_AM_STR))
813 if (match_string (get_locale_string (LOCALE_S2359), rp))
815 if (strcmp (get_locale_string (LOCALE_S2359), HERE_PM_STR))
823 if (!match_string (HERE_AM_STR, rp))
825 if (match_string (HERE_PM_STR, rp))
835 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
844 if (*decided == not &&
845 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
852 #elif defined (G_OS_WIN32)
856 g_strconcat (get_locale_string (LOCALE_STIMEFORMAT),
859 const char *posix_t_p_fmt = translate_picture (t_p_fmt);
863 if (!recursive (posix_t_p_fmt))
872 if (*decided == not &&
873 strcmp (posix_t_p_fmt,
881 if (!recursive (HERE_T_FMT_AMPM))
885 if (!recursive ("%H:%M"))
890 /* The number of seconds may be very high so we cannot use
891 the `get_number' macro. Instead read the number
892 character for character and construct the result while
895 if (*rp < '0' || *rp > '9')
896 /* We need at least one digit. */
904 while (*rp >= '0' && *rp <= '9');
906 if (localtime_r (&secs, tm) == NULL)
907 /* Error in function. */
912 get_number (0, 61, 2);
919 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
928 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
934 #elif defined (G_OS_WIN32)
937 const char *posix_t_fmt = translate_picture (get_locale_string (LOCALE_STIMEFORMAT));
938 if (!recursive (posix_t_fmt))
947 if (strcmp (posix_t_fmt, HERE_T_FMT))
956 if (!recursive (HERE_T_FMT))
960 get_number (1, 7, 1);
961 tm->tm_wday = val % 7;
965 get_number (0, 99, 2);
966 /* XXX This cannot determine any field in TM. */
969 if (*rp < '0' || *rp > '9')
971 /* XXX Ignore the number since we would need some more
972 information to compute a real date. */
975 while (*rp >= '0' && *rp <= '9');
978 get_number (0, 53, 2);
983 get_number (0, 53, 2);
988 get_number (0, 53, 2);
989 /* XXX This cannot determine any field in TM without some
993 /* Match number of weekday. */
994 get_number (0, 6, 1);
1000 match_year_in_century:
1002 /* Match year within century. */
1003 get_number (0, 99, 2);
1004 /* The "Year 2000: The Millennium Rollover" paper suggests that
1005 values in the range 69-99 refer to the twentieth century. */
1006 tm->tm_year = val >= 69 ? val : val + 100;
1007 /* Indicate that we want to use the century, if specified. */
1012 /* Match year including century number. */
1013 get_number (0, 9999, 4);
1014 tm->tm_year = val - 1900;
1019 /* XXX How to handle this? */
1026 /* Match locale's alternate date and time format. */
1027 if (*decided != raw)
1029 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
1032 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
1034 if (!recursive (fmt))
1036 if (*decided == loc)
1043 if (strcmp (fmt, HERE_D_T_FMT))
1050 /* The C locale has no era information, so use the
1051 normal representation. */
1052 if (!recursive (HERE_D_T_FMT))
1057 if (*decided != raw)
1061 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1062 if (era != NULL && match_string (era->era_name, rp))
1071 num_eras = _NL_CURRENT_WORD (LC_TIME,
1072 _NL_TIME_ERA_NUM_ENTRIES);
1073 for (era_cnt = 0; era_cnt < (int) num_eras;
1074 ++era_cnt, rp = rp_backup)
1076 era = _nl_select_era_entry (era_cnt
1078 if (era != NULL && match_string (era->era_name, rp))
1084 if (era_cnt != (int) num_eras)
1088 if (*decided == loc)
1093 /* The C locale has no era information, so use the
1094 normal representation. */
1097 if (*decided != raw)
1099 get_number(0, 9999, 4);
1107 assert (*decided == loc);
1109 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1113 int delta = ((tm->tm_year - era->offset)
1114 * era->absolute_direction);
1116 && delta < (((int64_t) era->stop_date[0]
1117 - (int64_t) era->start_date[0])
1118 * era->absolute_direction));
1126 num_eras = _NL_CURRENT_WORD (LC_TIME,
1127 _NL_TIME_ERA_NUM_ENTRIES);
1128 for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
1130 era = _nl_select_era_entry (era_cnt
1134 int delta = ((tm->tm_year - era->offset)
1135 * era->absolute_direction);
1137 && delta < (((int64_t) era->stop_date[0]
1138 - (int64_t) era->start_date[0])
1139 * era->absolute_direction))
1146 if (era_cnt != (int) num_eras)
1150 if (*decided == loc)
1156 goto match_year_in_century;
1158 if (*decided != raw)
1160 num_eras = _NL_CURRENT_WORD (LC_TIME,
1161 _NL_TIME_ERA_NUM_ENTRIES);
1162 for (era_cnt = 0; era_cnt < (int) num_eras;
1163 ++era_cnt, rp = rp_backup)
1165 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1166 if (era != NULL && recursive (era->era_format))
1169 if (era_cnt == (int) num_eras)
1172 if (*decided == loc)
1186 get_number (0, 9999, 4);
1187 tm->tm_year = val - 1900;
1192 if (*decided != raw)
1194 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
1197 fmt = _NL_CURRENT (LC_TIME, D_FMT);
1199 if (!recursive (fmt))
1201 if (*decided == loc)
1208 if (strcmp (fmt, HERE_D_FMT))
1214 if (!recursive (HERE_D_FMT))
1218 if (*decided != raw)
1220 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
1223 fmt = _NL_CURRENT (LC_TIME, T_FMT);
1225 if (!recursive (fmt))
1227 if (*decided == loc)
1234 if (strcmp (fmt, HERE_T_FMT))
1240 if (!recursive (HERE_T_FMT))
1248 /* We have no information about the era format. Just use
1249 the normal format. */
1250 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1251 && *fmt != 'x' && *fmt != 'X')
1252 /* This is an illegal format. */
1262 /* Match day of month using alternate numeric symbols. */
1263 get_alt_number (1, 31, 2);
1269 /* Match hour in 24-hour clock using alternate numeric
1271 get_alt_number (0, 23, 2);
1276 /* Match hour in 12-hour clock using alternate numeric
1278 get_alt_number (1, 12, 2);
1279 tm->tm_hour = val % 12;
1283 /* Match month using alternate numeric symbols. */
1284 get_alt_number (1, 12, 2);
1285 tm->tm_mon = val - 1;
1290 /* Match minutes using alternate numeric symbols. */
1291 get_alt_number (0, 59, 2);
1295 /* Match seconds using alternate numeric symbols. */
1296 get_alt_number (0, 61, 2);
1300 get_alt_number (0, 53, 2);
1305 get_alt_number (0, 53, 2);
1310 get_alt_number (0, 53, 2);
1311 /* XXX This cannot determine any field in TM without
1312 further information. */
1315 /* Match number of weekday using alternate numeric symbols. */
1316 get_alt_number (0, 6, 1);
1321 /* Match year within century using alternate numeric symbols. */
1322 get_alt_number (0, 99, 2);
1323 tm->tm_year = val >= 69 ? val : val + 100;
1335 if (have_I && is_pm)
1341 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1343 /* Only the century, but not the year. Strange, but so be it. */
1344 tm->tm_year = (century - 19) * 100;
1350 era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1354 tm->tm_year = (era->start_date[0]
1355 + ((tm->tm_year - era->offset)
1356 * era->absolute_direction));
1358 /* Era start year assumed. */
1359 tm->tm_year = era->start_date[0];
1365 /* No era found but we have seen an E modifier. Rectify some
1367 if (want_century && century == -1 && tm->tm_year < 69)
1371 if (want_xday && !have_wday)
1373 if ( !(have_mon && have_mday) && have_yday)
1375 /* We don't have tm_mon and/or tm_mday, compute them. */
1377 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1380 tm->tm_mon = t_mon - 1;
1384 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1386 day_of_the_week (tm);
1389 if (want_xday && !have_yday)
1390 day_of_the_year (tm);
1392 if ((have_uweek || have_wweek) && have_wday)
1394 int save_wday = tm->tm_wday;
1395 int save_mday = tm->tm_mday;
1396 int save_mon = tm->tm_mon;
1397 int w_offset = have_uweek ? 0 : 1;
1401 day_of_the_week (tm);
1403 tm->tm_mday = save_mday;
1405 tm->tm_mon = save_mon;
1408 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1410 + save_wday - w_offset);
1412 if (!have_mday || !have_mon)
1415 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1419 tm->tm_mon = t_mon - 1;
1423 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1426 tm->tm_wday = save_wday;
1433 strptime (buf, format, tm LOCALE_PARAM)
1439 enum ptime_locale_status decided;
1443 #elif defined (G_OS_WIN32)
1448 return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1452 weak_alias (__strptime_l, strptime_l)
1454 #endif /* HAVE_STRPTIME */
1456 /* Returns whether a string is NULL, empty, or full of whitespace */
1458 string_is_empty (const char *value)
1461 gboolean empty = TRUE;
1466 if (!isspace (*p)) {
1477 /* Takes a number of format strings for strptime() and attempts to parse a
1480 static ETimeParseStatus
1481 parse_with_strptime (const char *value, struct tm *result, const char **formats, int n_formats)
1483 const char *parse_end = NULL, *pos;
1486 ETimeParseStatus parse_ret;
1487 gboolean parsed = FALSE;
1490 if (string_is_empty (value)) {
1491 memset (result, 0, sizeof (*result));
1492 result->tm_isdst = -1;
1493 return E_TIME_PARSE_NONE;
1496 locale_str = g_locale_from_utf8 (value, -1, NULL, NULL, NULL);
1497 pos = (const char *) locale_str;
1500 return E_TIME_PARSE_INVALID;
1502 /* Skip whitespace */
1503 while (n = (int)((unsigned char)*pos), isspace (n) != 0)
1506 /* Try each of the formats in turn */
1508 for (i = 0; i < n_formats; i++) {
1509 memset (result, 0, sizeof (*result));
1510 format_str = g_locale_from_utf8 (formats[i], -1, NULL, NULL, NULL);
1511 parse_end = strptime (pos, format_str, result);
1512 g_free (format_str);
1519 result->tm_isdst = -1;
1521 parse_ret = E_TIME_PARSE_INVALID;
1523 /* If we parsed something, make sure we parsed the entire string. */
1525 /* Skip whitespace */
1526 while (isspace (*parse_end))
1529 if (*parse_end == '\0')
1530 parse_ret = E_TIME_PARSE_OK;
1533 g_free (locale_str);
1540 /* Returns TRUE if the locale has 'am' and 'pm' strings defined, in which
1541 case the user can choose between 12 and 24-hour time formats. */
1543 locale_supports_12_hour_format (void)
1545 struct tm tmp_tm = { 0 };
1548 e_utf8_strftime (s, sizeof (s), "%p", &tmp_tm);
1549 return s[0] != '\0';
1554 * e_time_parse_date_and_time:
1555 * @value: The string to parse a date and time from.
1556 * @result: A #tm to store the result in.
1558 * Parses a string @value containing a date and a time and stores the
1559 * result in @result. The date in @value is expected to be in a format
1560 * like "Wed 3/13/00 14:20:00", though gettext() is used to support the
1561 * appropriate local formats. There is also some leniency on the
1562 * format of the string, e.g. the weekday can be skipped or 12-hour
1563 * formats with am/pm can be used.
1565 * Returns: E_TIME_PARSE_OK if the string was successfully parsed,
1566 * E_TIME_PARSE_NONE if the string was empty, or
1567 * E_TIME_PARSE_INVALID if the string could not be parsed.
1570 e_time_parse_date_and_time (const char *value,
1573 struct tm *today_tm;
1575 const char *format[16];
1576 int num_formats = 0;
1577 gboolean use_12_hour_formats = locale_supports_12_hour_format ();
1578 ETimeParseStatus status;
1580 if (string_is_empty (value)) {
1581 memset (result, 0, sizeof (*result));
1582 result->tm_isdst = -1;
1583 return E_TIME_PARSE_NONE;
1586 /* We'll parse the whole date and time in one go, otherwise we get
1587 into i18n problems. We attempt to parse with several formats,
1588 longest first. Note that we only use the '%p' specifier if the
1589 locale actually has 'am' and 'pm' strings defined, otherwise we
1590 will get incorrect results. Note also that we try to use exactly
1591 the same strings as in e_time_format_date_and_time(), to try to
1592 avoid i18n problems. We also use cut-down versions, so users don't
1593 have to type in the weekday or the seconds, for example.
1594 Note that all these formats include the full date, and the time
1595 will be set to 00:00:00 before parsing, so we don't need to worry
1596 about filling in any missing fields after parsing. */
1599 * Try the full times, with the weekday. Then try without seconds,
1600 * and without minutes, and finally with no time at all.
1602 if (use_12_hour_formats) {
1603 /* strptime format of a weekday, a date and a time,
1604 in 12-hour format. */
1605 format[num_formats++] = _("%a %m/%d/%Y %I:%M:%S %p");
1608 /* strptime format of a weekday, a date and a time,
1609 in 24-hour format. */
1610 format[num_formats++] = _("%a %m/%d/%Y %H:%M:%S");
1612 if (use_12_hour_formats) {
1613 /* strptime format of a weekday, a date and a time,
1614 in 12-hour format, without seconds. */
1615 format[num_formats++] = _("%a %m/%d/%Y %I:%M %p");
1618 /* strptime format of a weekday, a date and a time,
1619 in 24-hour format, without seconds. */
1620 format[num_formats++] = _("%a %m/%d/%Y %H:%M");
1622 if (use_12_hour_formats) {
1623 /* strptime format of a weekday, a date and a time,
1624 in 12-hour format, without minutes or seconds. */
1625 format[num_formats++] = _("%a %m/%d/%Y %I %p");
1628 /* strptime format of a weekday, a date and a time,
1629 in 24-hour format, without minutes or seconds. */
1630 format[num_formats++] = _("%a %m/%d/%Y %H");
1632 /* strptime format of a weekday and a date. */
1633 format[num_formats++] = _("%a %m/%d/%Y");
1637 * Now try all the above formats again, but without the weekday.
1639 if (use_12_hour_formats) {
1640 /* strptime format of a date and a time, in 12-hour format. */
1641 format[num_formats++] = _("%m/%d/%Y %I:%M:%S %p");
1644 /* strptime format of a date and a time, in 24-hour format. */
1645 format[num_formats++] = _("%m/%d/%Y %H:%M:%S");
1647 if (use_12_hour_formats) {
1648 /* strptime format of a date and a time, in 12-hour format,
1650 format[num_formats++] = _("%m/%d/%Y %I:%M %p");
1653 /* strptime format of a date and a time, in 24-hour format,
1655 format[num_formats++] = _("%m/%d/%Y %H:%M");
1657 if (use_12_hour_formats) {
1658 /* strptime format of a date and a time, in 12-hour format,
1659 without minutes or seconds. */
1660 format[num_formats++] = _("%m/%d/%Y %I %p");
1663 /* strptime format of a date and a time, in 24-hour format,
1664 without minutes or seconds. */
1665 format[num_formats++] = _("%m/%d/%Y %H");
1667 /* strptime format of a weekday and a date. */
1668 format[num_formats++] = _("%m/%d/%Y");
1671 status = parse_with_strptime (value, result, format, num_formats);
1672 /* Note that we checked if it was empty already, so it is either OK
1674 if (status == E_TIME_PARSE_OK) {
1675 /* If a 2-digit year was used we use the current century. */
1676 if (result->tm_year < 0) {
1678 today_tm = localtime (&t);
1680 /* This should convert it into a value from 0 to 99. */
1681 result->tm_year += 1900;
1683 /* Now add on the century. */
1684 result->tm_year += today_tm->tm_year
1685 - (today_tm->tm_year % 100);
1688 /* Now we try to just parse a time, assuming the current day.*/
1689 status = e_time_parse_time (value, result);
1690 if (status == E_TIME_PARSE_OK) {
1691 /* We fill in the current day. */
1693 today_tm = localtime (&t);
1694 result->tm_mday = today_tm->tm_mday;
1695 result->tm_mon = today_tm->tm_mon;
1696 result->tm_year = today_tm->tm_year;
1704 * e_time_parse_date:
1705 * @value: A date string.
1706 * @result: Return value for the parsed date.
1708 * Takes in a date string entered by the user and tries to convert it to
1711 * Returns: An #ETimeParseStatus result code indicating whether
1712 * @value was an empty string, a valid date, or an invalid date.
1715 e_time_parse_date (const char *value, struct tm *result)
1717 const char *format[3];
1718 struct tm *today_tm;
1720 ETimeParseStatus status;
1722 g_return_val_if_fail (value != NULL, E_TIME_PARSE_INVALID);
1723 g_return_val_if_fail (result != NULL, E_TIME_PARSE_INVALID);
1725 /* according to the current locale */
1726 format [0] = ("%x");
1728 /* strptime format of a weekday and a date. */
1729 format[1] = _("%a %m/%d/%Y");
1731 /* This is the preferred date format for the locale. */
1732 format[2] = _("%m/%d/%Y");
1735 status = parse_with_strptime (value, result, format, sizeof (format)/sizeof (format [0]));
1736 if (status == E_TIME_PARSE_OK) {
1737 /* If a 2-digit year was used we use the current century. */
1738 if (result->tm_year < 0) {
1740 today_tm = localtime (&t);
1742 /* This should convert it into a value from 0 to 99. */
1743 result->tm_year += 1900;
1745 /* Now add on the century. */
1746 result->tm_year += today_tm->tm_year
1747 - (today_tm->tm_year % 100);
1756 * e_time_parse_time:
1757 * @value: The string to parse a time from.
1758 * @result: A #tm to store the result in.
1760 * Parses @value, a string containing a time. @value is expected to be
1761 * in a format like "14:20:00". gettext() is used to
1762 * support the appropriate local formats and slightly
1763 * different formats, such as 12-hour formats with am/pm,
1764 * are accepted as well.
1766 * Returns: An #ETimeParseStatus result code indicating whether
1767 * @value was an empty string, a valid date, or an invalid date.
1770 e_time_parse_time (const char *value, struct tm *result)
1772 const char *format[6];
1773 int num_formats = 0;
1774 gboolean use_12_hour_formats = locale_supports_12_hour_format ();
1776 if (use_12_hour_formats) {
1777 /* strptime format for a time of day, in 12-hour format. */
1778 format[num_formats++] = _("%I:%M:%S %p");
1781 /* strptime format for a time of day, in 24-hour format. */
1782 format[num_formats++] = _("%H:%M:%S");
1784 if (use_12_hour_formats) {
1785 /* strptime format for time of day, without seconds,
1786 in 12-hour format. */
1787 format[num_formats++] = _("%I:%M %p");
1790 /* strptime format for time of day, without seconds 24-hour format. */
1791 format[num_formats++] = _("%H:%M");
1793 if (use_12_hour_formats) {
1794 /* strptime format for hour and AM/PM, 12-hour format. */
1795 format[num_formats++] = _("%I %p");
1798 /* strptime format for hour, 24-hour format. */
1799 format[num_formats++] = "%H";
1801 return parse_with_strptime (value, result, format, num_formats);
1806 * e_time_format_date_and_time:
1807 * @date_tm: The #tm to convert to a string.
1808 * @use_24_hour_format: A #gboolean.
1809 * @show_midnight: A #gboolean.
1810 * @show_zero_seconds: A #gboolean.
1811 * @buffer: A #char buffer to store the time string in.
1812 * @buffer_size: The length of @buffer.
1814 * Creates a string representation of the time value @date_tm and
1815 * stores it in @buffer. @buffer_size should be at least 64 to be
1816 * safe. If @show_midnight is #FALSE, and the time is midnight, then
1817 * only the date is stored in @buffer. If @show_zero_seconds is
1818 * #FALSE, then if the time has zero seconds only the hour and minute
1819 * of the time are stored in @buffer.
1822 e_time_format_date_and_time (struct tm *date_tm,
1823 gboolean use_24_hour_format,
1824 gboolean show_midnight,
1825 gboolean show_zero_seconds,
1831 if (!show_midnight && date_tm->tm_hour == 0
1832 && date_tm->tm_min == 0 && date_tm->tm_sec == 0) {
1833 /* strftime format of a weekday and a date. */
1834 format = _("%a %m/%d/%Y");
1835 } else if (use_24_hour_format) {
1836 if (!show_zero_seconds && date_tm->tm_sec == 0)
1837 /* strftime format of a weekday, a date and a
1838 time, in 24-hour format, without seconds. */
1839 format = _("%a %m/%d/%Y %H:%M");
1841 /* strftime format of a weekday, a date and a
1842 time, in 24-hour format. */
1843 format = _("%a %m/%d/%Y %H:%M:%S");
1845 if (!show_zero_seconds && date_tm->tm_sec == 0)
1846 /* strftime format of a weekday, a date and a
1847 time, in 12-hour format, without seconds. */
1848 format = _("%a %m/%d/%Y %I:%M %p");
1850 /* strftime format of a weekday, a date and a
1851 time, in 12-hour format. */
1852 format = _("%a %m/%d/%Y %I:%M:%S %p");
1855 /* strftime returns 0 if the string doesn't fit, and leaves the buffer
1856 undefined, so we set it to the empty string in that case. */
1857 if (e_utf8_strftime (buffer, buffer_size, format, date_tm) == 0)
1863 * e_time_format_time:
1864 * @date_tm: The #tm to convert to a string.
1865 * @use_24_hour_format: A #gboolean.
1866 * @show_zero_seconds: A #gboolean.
1867 * @buffer: The #char buffer to store the result in.
1868 * @buffer_size: The length of @buffer.
1870 * Creates a string representation of a time value in @date_tm and
1871 * stores it in @buffer. @buffer_size should be at least 64.
1874 e_time_format_time (struct tm *date_tm,
1875 gboolean use_24_hour_format,
1876 gboolean show_zero_seconds,
1882 if (use_24_hour_format) {
1883 if (!show_zero_seconds && date_tm->tm_sec == 0)
1884 /* strftime format of a time in 24-hour format,
1886 format = _("%H:%M");
1888 /* strftime format of a time in 24-hour format. */
1889 format = _("%H:%M:%S");
1891 if (!show_zero_seconds && date_tm->tm_sec == 0)
1892 /* strftime format of a time in 12-hour format,
1894 format = _("%I:%M %p");
1896 /* strftime format of a time in 12-hour format. */
1897 format = _("%I:%M:%S %p");
1900 /* strftime returns 0 if the string doesn't fit, and leaves the buffer
1901 undefined, so we set it to the empty string in that case. */
1902 if (e_utf8_strftime (buffer, buffer_size, format, date_tm) == 0)
1909 * @tm: The #tm to convert to a calendar time representation.
1911 * Like mktime(3), but assumes UTC instead of local timezone.
1913 * Returns: The calendar time representation of @tm.
1916 e_mktime_utc (struct tm *tm)
1923 #if defined (HAVE_TM_GMTOFF)
1924 tt += tm->tm_gmtoff;
1925 #elif defined (HAVE_TIMEZONE)
1926 if (tm->tm_isdst > 0) {
1927 #if defined (HAVE_ALTZONE)
1929 #else /* !defined (HAVE_ALTZONE) */
1930 tt -= (timezone - 3600);
1940 * e_localtime_with_offset:
1941 * @tt: The #time_t to convert.
1942 * @tm: The #tm to store the result in.
1943 * @offset: The #int to store the offset in.
1945 * Converts the calendar time time representation @tt to a broken-down
1946 * time representation, store in @tm, and provides the offset in
1947 * seconds from UTC time, stored in @offset.
1950 e_localtime_with_offset (time_t tt, struct tm *tm, int *offset)
1952 localtime_r (&tt, tm);
1954 #if defined (HAVE_TM_GMTOFF)
1955 *offset = tm->tm_gmtoff;
1956 #elif defined (HAVE_TIMEZONE)
1957 if (tm->tm_isdst > 0) {
1958 #if defined (HAVE_ALTZONE)
1960 #else /* !defined (HAVE_ALTZONE) */
1961 *offset = -(timezone - 3600);
1964 *offset = -timezone;