NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
date.c
Go to the documentation of this file.
1
31
37
38#include "config.h"
39#include <locale.h>
40#include <stdbool.h>
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <sys/time.h>
46#include <time.h>
47#include "date.h"
48#include "buffer.h"
49#include "ctype2.h"
50#include "eqi.h"
51#include "logging2.h"
52#include "prex.h"
53#include "regex3.h"
54#include "string2.h"
55
56struct timespec;
57
61static const char *const Weekdays[] = {
62 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
63};
64
68static const char *const Months[] = {
69 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
70 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
71};
72
78static const struct Tz TimeZones[] = {
79 // clang-format off
80 { "aat", 1, 0, true }, /* Atlantic Africa Time */
81 { "adt", 4, 0, false }, /* Arabia DST */
82 { "ast", 3, 0, false }, /* Arabia */
83//{ "ast", 4, 0, true }, /* Atlantic */
84 { "bst", 1, 0, false }, /* British DST */
85 { "cat", 1, 0, false }, /* Central Africa */
86 { "cdt", 5, 0, true },
87 { "cest", 2, 0, false }, /* Central Europe DST */
88 { "cet", 1, 0, false }, /* Central Europe */
89 { "cst", 6, 0, true },
90//{ "cst", 8, 0, false }, /* China */
91//{ "cst", 9, 30, false }, /* Australian Central Standard Time */
92 { "eat", 3, 0, false }, /* East Africa */
93 { "edt", 4, 0, true },
94 { "eest", 3, 0, false }, /* Eastern Europe DST */
95 { "eet", 2, 0, false }, /* Eastern Europe */
96 { "egst", 0, 0, false }, /* Eastern Greenland DST */
97 { "egt", 1, 0, true }, /* Eastern Greenland */
98 { "est", 5, 0, true },
99 { "gmt", 0, 0, false },
100 { "gst", 4, 0, false }, /* Presian Gulf */
101 { "hkt", 8, 0, false }, /* Hong Kong */
102 { "ict", 7, 0, false }, /* Indochina */
103 { "idt", 3, 0, false }, /* Israel DST */
104 { "ist", 2, 0, false }, /* Israel */
105//{ "ist", 5, 30, false }, /* India */
106 { "jst", 9, 0, false }, /* Japan */
107 { "kst", 9, 0, false }, /* Korea */
108 { "mdt", 6, 0, true },
109 { "met", 1, 0, false }, /* This is now officially CET */
110 { "met dst", 2, 0, false }, /* MET in Daylight Saving Time */
111 { "msd", 4, 0, false }, /* Moscow DST */
112 { "msk", 3, 0, false }, /* Moscow */
113 { "mst", 7, 0, true },
114 { "nzdt", 13, 0, false }, /* New Zealand DST */
115 { "nzst", 12, 0, false }, /* New Zealand */
116 { "pdt", 7, 0, true },
117 { "pst", 8, 0, true },
118 { "sat", 2, 0, false }, /* South Africa */
119 { "smt", 4, 0, false }, /* Seychelles */
120 { "sst", 11, 0, true }, /* Samoa */
121//{ "sst", 8, 0, false }, /* Singapore */
122 { "utc", 0, 0, false },
123 { "wat", 0, 0, false }, /* West Africa */
124 { "west", 1, 0, false }, /* Western Europe DST */
125 { "wet", 0, 0, false }, /* Western Europe */
126 { "wgst", 2, 0, true }, /* Western Greenland DST */
127 { "wgt", 3, 0, true }, /* Western Greenland */
128 { "wst", 8, 0, false }, /* Western Australia */
129 // clang-format on
130};
131
141static int compute_tz(time_t g, struct tm *utc)
142{
143 struct tm lt = mutt_date_localtime(g);
144
145 int tz = (((lt.tm_hour - utc->tm_hour) * 60) + (lt.tm_min - utc->tm_min)) * 60;
146
147 int yday = (lt.tm_yday - utc->tm_yday);
148 if (yday != 0)
149 {
150 /* This code is optimized to negative timezones (West of Greenwich) */
151 if ((yday == -1) || /* UTC passed midnight before localtime */
152 (yday > 1)) /* UTC passed new year before localtime */
153 {
154 tz -= (24 * 60 * 60);
155 }
156 else
157 {
158 tz += (24 * 60 * 60);
159 }
160 }
161
162 return tz;
163}
164
173static time_t add_tz_offset(time_t t, bool w, time_t h, time_t m)
174{
175 if ((t != TIME_T_MAX) && (t != TIME_T_MIN))
176 return t + (w ? 1 : -1) * (((time_t) h * 3600) + ((time_t) m * 60));
177 else
178 return t;
179}
180
188static const struct Tz *find_tz(const char *s, size_t len)
189{
190 for (size_t i = 0; i < countof(TimeZones); i++)
191 {
192 if (mutt_istrn_equal(TimeZones[i].tzname, s, len))
193 return &TimeZones[i];
194 }
195 return NULL;
196}
197
203static int is_leap_year_feb(const struct tm *tm)
204{
205 if (tm->tm_mon != 1)
206 return 0;
207
208 int y = tm->tm_year + 1900;
209 return ((y & 3) == 0) && (((y % 100) != 0) || ((y % 400) == 0));
210}
211
221{
222 /* Check we haven't overflowed the time (on 32-bit arches) */
223 if ((t == TIME_T_MAX) || (t == TIME_T_MIN))
224 return 0;
225
226 if (t == 0)
227 t = mutt_date_now();
228
229 struct tm tm = mutt_date_gmtime(t);
230 return compute_tz(t, &tm);
231}
232
243time_t mutt_date_make_time(struct tm *t, bool local)
244{
245 if (!t)
246 return TIME_T_MIN;
247
248 static const int AccumDaysPerMonth[countof(Months)] = {
249 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
250 };
251
252 /* Prevent an integer overflow, with some arbitrary limits. */
253 if (t->tm_year > 10000)
254 return TIME_T_MAX;
255 if (t->tm_year < -10000)
256 return TIME_T_MIN;
257
258 if ((t->tm_mday < 1) || (t->tm_mday > 31))
259 return TIME_T_MIN;
260 if ((t->tm_mon < 0) || (t->tm_mon > 11))
261 return TIME_T_MIN;
262 if ((t->tm_hour < 0) || (t->tm_hour > 23) || (t->tm_min < 0) ||
263 (t->tm_min > 59) || (t->tm_sec < 0) || (t->tm_sec > 60))
264 {
265 return TIME_T_MIN;
266 }
267 if (t->tm_year > 9999)
268 return TIME_T_MAX;
269
270 /* Compute the number of days since January 1 in the same year */
271 int yday = AccumDaysPerMonth[t->tm_mon % countof(Months)];
272
273 /* The leap years are 1972 and every 4. year until 2096,
274 * but this algorithm will fail after year 2099 */
275 yday += t->tm_mday;
276 if ((t->tm_year % 4) || (t->tm_mon < 2))
277 yday--;
278 t->tm_yday = yday;
279
280 time_t g = yday;
281
282 /* Compute the number of days since January 1, 1970 */
283 g += (t->tm_year - 70) * (time_t) 365;
284 g += (t->tm_year - 69) / 4;
285
286 /* Compute the number of hours */
287 g *= 24;
288 g += t->tm_hour;
289
290 /* Compute the number of minutes */
291 g *= 60;
292 g += t->tm_min;
293
294 /* Compute the number of seconds */
295 g *= 60;
296 g += t->tm_sec;
297
298 if (local)
299 g -= compute_tz(g, t);
300
301 return g;
302}
303
313void mutt_date_normalize_time(struct tm *tm)
314{
315 if (!tm)
316 return;
317
318 static const char DaysPerMonth[countof(Months)] = {
319 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
320 };
321 int leap;
322
323 while (tm->tm_sec < 0)
324 {
325 tm->tm_sec += 60;
326 tm->tm_min--;
327 }
328 while (tm->tm_sec >= 60)
329 {
330 tm->tm_sec -= 60;
331 tm->tm_min++;
332 }
333 while (tm->tm_min < 0)
334 {
335 tm->tm_min += 60;
336 tm->tm_hour--;
337 }
338 while (tm->tm_min >= 60)
339 {
340 tm->tm_min -= 60;
341 tm->tm_hour++;
342 }
343 while (tm->tm_hour < 0)
344 {
345 tm->tm_hour += 24;
346 tm->tm_mday--;
347 }
348 while (tm->tm_hour >= 24)
349 {
350 tm->tm_hour -= 24;
351 tm->tm_mday++;
352 }
353 /* use loops on NNNdwmy user input values? */
354 while (tm->tm_mon < 0)
355 {
356 tm->tm_mon += 12;
357 tm->tm_year--;
358 }
359 while (tm->tm_mon >= 12)
360 {
361 tm->tm_mon -= 12;
362 tm->tm_year++;
363 }
364 while (tm->tm_mday <= 0)
365 {
366 if (tm->tm_mon)
367 {
368 tm->tm_mon--;
369 }
370 else
371 {
372 tm->tm_mon = 11;
373 tm->tm_year--;
374 }
375 tm->tm_mday += DaysPerMonth[tm->tm_mon] + is_leap_year_feb(tm);
376 }
377 while (tm->tm_mday > (DaysPerMonth[tm->tm_mon] + (leap = is_leap_year_feb(tm))))
378 {
379 tm->tm_mday -= DaysPerMonth[tm->tm_mon] + leap;
380 if (tm->tm_mon < 11)
381 {
382 tm->tm_mon++;
383 }
384 else
385 {
386 tm->tm_mon = 0;
387 tm->tm_year++;
388 }
389 }
390}
391
400void mutt_date_make_date(struct Buffer *buf, bool local)
401{
402 if (!buf)
403 return;
404
405 struct tm tm = { 0 };
406 int tz = 0;
407
408 time_t t = mutt_date_now();
409 if (local)
410 {
411 tm = mutt_date_localtime(t);
412 tz = mutt_date_local_tz(t);
413 }
414 else
415 {
416 tm = mutt_date_gmtime(t);
417 }
418
419 tz /= 60;
420
421 buf_add_printf(buf, "%s, %d %s %d %02d:%02d:%02d %+03d%02d", Weekdays[tm.tm_wday],
422 tm.tm_mday, Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour,
423 tm.tm_min, tm.tm_sec, tz / 60, abs(tz) % 60);
424}
425
435int mutt_date_check_month(const char *s)
436{
437 if (!s)
438 return -1;
439
440 char buf[4] = { 0 };
441 memcpy(buf, s, 3);
442 uint32_t sv = 0;
443 memcpy(&sv, buf, sizeof(sv));
444 for (int i = 0; i < countof(Months); i++)
445 {
446 uint32_t mv = 0;
447 memcpy(&mv, Months[i], sizeof(mv));
448 if (sv == mv)
449 return i;
450 }
451
452 return -1; /* error */
453}
454
459time_t mutt_date_now(void)
460{
461 return mutt_date_now_ms() / 1000;
462}
463
468uint64_t mutt_date_now_ms(void)
469{
470 struct timeval tv = { 0, 0 };
471 gettimeofday(&tv, NULL);
472 /* We assume that gettimeofday doesn't modify its first argument on failure.
473 * We also kind of assume that gettimeofday does not fail. */
474 return ((uint64_t) tv.tv_sec * 1000) + (tv.tv_usec / 1000);
475}
476
483void mutt_time_now(struct timespec *tp)
484{
485#ifdef HAVE_CLOCK_GETTIME
486 if (clock_gettime(CLOCK_REALTIME, tp) != 0)
487 mutt_perror("clock_gettime");
488#else
489 struct timeval tv = { 0, 0 };
490 if (gettimeofday(&tv, NULL) != 0)
491 mutt_perror("gettimeofday");
492 tp->tv_sec = tv.tv_sec;
493 tp->tv_nsec = tv.tv_usec * 1000;
494#endif
495}
496
510static int parse_small_uint(const char *str, const char *end, int *val)
511{
512 const char *ptr = str;
513 int v = 0;
514 while ((ptr < end) && (ptr < (str + 5)) && (*ptr >= '0') && (*ptr <= '9'))
515 {
516 v = (v * 10) + (*ptr - '0');
517 ptr++;
518 }
519 *val = v;
520 return ptr - str;
521}
522
540static time_t mutt_date_parse_rfc5322_strict(const char *s, struct Tz *tz_out)
541{
542 size_t len = strlen(s);
543
544 /* Skip over the weekday, if any. */
545 if ((len >= 5) && (s[4] == ' ') &&
546 (eqi4(s, "Mon,") || eqi4(s, "Tue,") || eqi4(s, "Wed,") ||
547 eqi4(s, "Thu,") || eqi4(s, "Fri,") || eqi4(s, "Sat,") || eqi4(s, "Sun,")))
548 {
549 s += 5;
550 len -= 5;
551 }
552
553 while ((len > 0) && (*s == ' '))
554 {
555 s++;
556 len--;
557 }
558
559 if ((len == 0) || (*s < '0') || (*s > '9'))
560 return -1;
561
562 struct tm tm = { 0 };
563
564 /* Day */
565 int mday_len = parse_small_uint(s, s + len, &tm.tm_mday);
566 if ((mday_len == 0) || (mday_len > 2) || (tm.tm_mday > 31))
567 return -1;
568 s += mday_len;
569 len -= mday_len;
570
571 if ((len == 0) || (*s != ' '))
572 return -1;
573 s++;
574 len--;
575
576 /* Month */
577 if (len < 3)
578 return -1;
579 tm.tm_mon = mutt_date_check_month(s);
580 if (tm.tm_mon == -1)
581 return -1;
582 s += 3;
583 len -= 3;
584
585 if ((len == 0) || (*s != ' '))
586 return -1;
587 s++;
588 len--;
589
590 /* Year */
591 int year_len = parse_small_uint(s, s + len, &tm.tm_year);
592 if ((year_len != 2) && (year_len != 4))
593 return -1;
594 if (tm.tm_year < 50)
595 tm.tm_year += 100;
596 else if (tm.tm_year >= 1900)
597 tm.tm_year -= 1900;
598 s += year_len;
599 len -= year_len;
600
601 if ((len == 0) || (*s != ' '))
602 return -1;
603 s++;
604 len--;
605
606 /* Hour */
607 if ((len < 3) || (s[0] < '0') || (s[0] > '2') || (s[1] < '0') ||
608 (s[1] > '9') || (s[2] != ':'))
609 {
610 return -1;
611 }
612 tm.tm_hour = ((s[0] - '0') * 10) + (s[1] - '0');
613 if (tm.tm_hour > 23)
614 return -1;
615 s += 3;
616 len -= 3;
617
618 /* Minute */
619 if ((len < 2) || (s[0] < '0') || (s[0] > '5') || (s[1] < '0') || (s[1] > '9'))
620 return -1;
621 tm.tm_min = ((s[0] - '0') * 10) + (s[1] - '0');
622 if (tm.tm_min > 59)
623 return -1;
624 s += 2;
625 len -= 2;
626
627 /* Second (optional) */
628 if ((len > 0) && (s[0] == ':'))
629 {
630 s++;
631 len--;
632 if ((len < 2) || (s[0] < '0') || (s[0] > '5') || (s[1] < '0') || (s[1] > '9'))
633 return -1;
634 tm.tm_sec = ((s[0] - '0') * 10) + (s[1] - '0');
635 if (tm.tm_sec > 60)
636 return -1;
637 s += 2;
638 len -= 2;
639 }
640
641 while ((len > 0) && (*s == ' '))
642 {
643 s++;
644 len--;
645 }
646
647 /* Strip optional time zone comment and white space from the end
648 * (this is the only one that is very common) */
649 while ((len > 0) && (s[len - 1] == ' '))
650 len--;
651 if ((len >= 2) && (s[len - 1] == ')'))
652 {
653 for (int i = len - 2; i >= 0; i--)
654 {
655 if (s[i] == '(')
656 {
657 len = i;
658 break;
659 }
660 if (!mutt_isalpha(s[i]) && (s[i] != ' '))
661 return -1; /* give up more complex comment parsing */
662 }
663 }
664 while ((len > 0) && (s[len - 1] == ' '))
665 len--;
666
667 /* Time zone (optional) */
668 int zhours = 0;
669 int zminutes = 0;
670 bool zoccident = false;
671 if (len > 0)
672 {
673 if ((len == 5) && ((s[0] == '+') || (s[0] == '-')) && (s[1] >= '0') &&
674 (s[1] <= '9') && (s[2] >= '0') && (s[2] <= '9') && (s[3] >= '0') &&
675 (s[3] <= '9') && (s[4] >= '0') && (s[4] <= '9'))
676 {
677 zoccident = (s[0] == '-');
678 zhours = ((s[1] - '0') * 10) + (s[2] - '0');
679 zminutes = ((s[3] - '0') * 10) + (s[4] - '0');
680 }
681 else
682 {
683 for (int i = 0; i < len; i++)
684 {
685 if (!mutt_isalpha(s[i]))
686 return -1;
687 }
688 const struct Tz *tz = find_tz(s, len);
689 if (tz)
690 {
691 zhours = tz->zhours;
692 zminutes = tz->zminutes;
693 zoccident = tz->zoccident;
694 }
695 }
696 }
697
698 if (tz_out)
699 {
700 tz_out->zhours = zhours;
701 tz_out->zminutes = zminutes;
702 tz_out->zoccident = zoccident;
703 }
704
706}
707
719time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
720{
721 if (!s)
722 return -1;
723
724 const time_t strict_t = mutt_date_parse_rfc5322_strict(s, tz_out);
725 if (strict_t != -1)
726 return strict_t;
727
728 const regmatch_t *match = mutt_prex_capture(PREX_RFC5322_DATE_LAX, s);
729 if (!match)
730 {
731 mutt_debug(LL_DEBUG1, "Could not parse date: <%s>\n", s);
732 return -1;
733 }
734 mutt_debug(LL_DEBUG2, "Fallback regex for date: <%s>\n", s);
735
736 struct tm tm = { 0 };
737
738 // clang-format off
739 const regmatch_t *mday = &match[PREX_RFC5322_DATE_LAX_MATCH_DAY];
740 const regmatch_t *mmonth = &match[PREX_RFC5322_DATE_LAX_MATCH_MONTH];
741 const regmatch_t *myear = &match[PREX_RFC5322_DATE_LAX_MATCH_YEAR];
742 const regmatch_t *mhour = &match[PREX_RFC5322_DATE_LAX_MATCH_HOUR];
743 const regmatch_t *mminute = &match[PREX_RFC5322_DATE_LAX_MATCH_MINUTE];
744 const regmatch_t *msecond = &match[PREX_RFC5322_DATE_LAX_MATCH_SECOND];
745 const regmatch_t *mtz = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ];
746 const regmatch_t *mtzobs = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS];
747 // clang-format on
748
749 /* Day */
750 sscanf(s + mutt_regmatch_start(mday), "%d", &tm.tm_mday);
751 if (tm.tm_mday > 31)
752 return -1;
753
754 /* Month */
755 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
756
757 /* Year */
758 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
759 if (tm.tm_year < 50)
760 tm.tm_year += 100;
761 else if (tm.tm_year >= 1900)
762 tm.tm_year -= 1900;
763
764 /* Time */
765 int hour = 0;
766 int min = 0;
767 int sec = 0;
768 sscanf(s + mutt_regmatch_start(mhour), "%d", &hour);
769 sscanf(s + mutt_regmatch_start(mminute), "%d", &min);
770 if (mutt_regmatch_start(msecond) != -1)
771 sscanf(s + mutt_regmatch_start(msecond), "%d", &sec);
772 if ((hour > 23) || (min > 59) || (sec > 60))
773 return -1;
774 tm.tm_hour = hour;
775 tm.tm_min = min;
776 tm.tm_sec = sec;
777
778 /* Time zone */
779 int zhours = 0;
780 int zminutes = 0;
781 bool zoccident = false;
782 if (mutt_regmatch_start(mtz) != -1)
783 {
784 char direction = '\0';
785 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
786 zoccident = (direction == '-');
787 }
788 else if (mutt_regmatch_start(mtzobs) != -1)
789 {
790 const struct Tz *tz = find_tz(s + mutt_regmatch_start(mtzobs),
791 mutt_regmatch_len(mtzobs));
792 if (tz)
793 {
794 zhours = tz->zhours;
795 zminutes = tz->zminutes;
796 zoccident = tz->zoccident;
797 }
798 }
799
800 if (tz_out)
801 {
802 tz_out->zhours = zhours;
803 tz_out->zminutes = zminutes;
804 tz_out->zoccident = zoccident;
805 }
806
808}
809
816int mutt_date_make_imap(struct Buffer *buf, time_t timestamp)
817{
818 if (!buf)
819 return -1;
820
821 struct tm tm = mutt_date_localtime(timestamp);
823
824 tz /= 60;
825
826 return buf_printf(buf, "%02d-%s-%d %02d:%02d:%02d %+03d%02d", tm.tm_mday,
827 Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour, tm.tm_min,
828 tm.tm_sec, tz / 60, abs(tz) % 60);
829}
830
842int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
843{
844 if (!buf)
845 return -1;
846
847 struct tm tm = mutt_date_gmtime(timestamp);
848 return snprintf(buf, buflen, "%s, %d %s %d %02d:%02d:%02d UTC",
849 Weekdays[tm.tm_wday], tm.tm_mday, Months[tm.tm_mon],
850 tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
851}
852
859time_t mutt_date_parse_imap(const char *s)
860{
861 const regmatch_t *match = mutt_prex_capture(PREX_IMAP_DATE, s);
862 if (!match)
863 return 0;
864
865 const regmatch_t *mday = &match[PREX_IMAP_DATE_MATCH_DAY];
866 const regmatch_t *mmonth = &match[PREX_IMAP_DATE_MATCH_MONTH];
867 const regmatch_t *myear = &match[PREX_IMAP_DATE_MATCH_YEAR];
868 const regmatch_t *mtime = &match[PREX_IMAP_DATE_MATCH_TIME];
869 const regmatch_t *mtz = &match[PREX_IMAP_DATE_MATCH_TZ];
870
871 struct tm tm = { 0 };
872
873 sscanf(s + mutt_regmatch_start(mday), " %d", &tm.tm_mday);
874 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
875 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
876 tm.tm_year -= 1900;
877 sscanf(s + mutt_regmatch_start(mtime), "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
878
879 char direction = '\0';
880 int zhours = 0;
881 int zminutes = 0;
882 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
883 bool zoccident = (direction == '-');
884
885 return add_tz_offset(mutt_date_make_time(&tm, false), zoccident, zhours, zminutes);
886}
887
896time_t mutt_date_add_timeout(time_t now, time_t timeout)
897{
898 if (timeout < 0)
899 return now;
900
901 if ((TIME_T_MAX - now) < timeout)
902 return TIME_T_MAX;
903
904 return now + timeout;
905}
906
912struct tm mutt_date_localtime(time_t t)
913{
914 struct tm tm = { 0 };
915
916 struct tm *ret = localtime_r(&t, &tm);
917 if (!ret)
918 {
919 mutt_debug(LL_DEBUG1, "Could not convert time_t via localtime_r() to struct tm: time_t = %jd\n",
920 (intmax_t) t);
921 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
922 mktime(&default_tm); // update derived fields making tm into a valid tm.
923 tm = default_tm;
924 }
925 return tm;
926}
927
933struct tm mutt_date_gmtime(time_t t)
934{
935 struct tm tm = { 0 };
936
937 struct tm *ret = gmtime_r(&t, &tm);
938 if (!ret)
939 {
940 mutt_debug(LL_DEBUG1, "Could not convert time_t via gmtime_r() to struct tm: time_t = %jd\n",
941 (intmax_t) t);
942 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
943 mktime(&default_tm); // update derived fields making tm into a valid tm.
944 tm = default_tm;
945 }
946 return tm;
947}
948
957size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
958{
959 if (!buf || !format)
960 return 0;
961
962 struct tm tm = mutt_date_localtime(t);
963 return strftime(buf, buflen, format, &tm);
964}
965
975size_t mutt_date_localtime_format_locale(char *buf, size_t buflen,
976 const char *format, time_t t, locale_t loc)
977{
978 if (!buf || !format)
979 return 0;
980
981 struct tm tm = mutt_date_localtime(t);
982 return strftime_l(buf, buflen, format, &tm, loc);
983}
984
989void mutt_date_sleep_ms(size_t ms)
990{
991 const struct timespec sleep = {
992 .tv_sec = ms / 1000,
993 .tv_nsec = (ms % 1000) * 1000000UL,
994 };
995 nanosleep(&sleep, NULL);
996}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:211
General purpose object for storing and parsing strings.
ctype(3) wrapper functions
bool mutt_isalpha(int arg)
Wrapper for isalpha(3)
Definition ctype.c:53
Time and date handling routines.
#define TIME_T_MAX
Maximum value for a time_t.
Definition date.h:40
#define TIME_T_MIN
Minimum value for a time_t.
Definition date.h:42
Case-insensitive fixed-chunk comparisons.
static bool eqi4(const char *a, const char b[4])
Compare two 4-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:107
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
Logging Dispatcher.
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define countof(x)
Definition memory.h:49
struct tm mutt_date_localtime(time_t t)
Converts calendar time to a broken-down time structure expressed in user timezone.
Definition date.c:912
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition date.c:957
uint64_t mutt_date_now_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition date.c:468
static int compute_tz(time_t g, struct tm *utc)
Calculate the number of seconds east of UTC.
Definition date.c:141
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition date.c:400
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t
Definition date.c:243
static int parse_small_uint(const char *str, const char *end, int *val)
Parse a positive integer of at most 5 digits.
Definition date.c:510
static const char *const Months[]
Months of the year (abbreviated)
Definition date.c:68
void mutt_date_sleep_ms(size_t ms)
Sleep for milliseconds.
Definition date.c:989
int mutt_date_check_month(const char *s)
Is the string a valid month name.
Definition date.c:435
static const struct Tz * find_tz(const char *s, size_t len)
Look up a timezone.
Definition date.c:188
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone.
Definition date.c:933
size_t mutt_date_localtime_format_locale(char *buf, size_t buflen, const char *format, time_t t, locale_t loc)
Format localtime using a given locale.
Definition date.c:975
int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
Format date in TLS certificate verification style.
Definition date.c:842
static int is_leap_year_feb(const struct tm *tm)
Is a given February in a leap year.
Definition date.c:203
int mutt_date_make_imap(struct Buffer *buf, time_t timestamp)
Format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition date.c:816
static const char *const Weekdays[]
Day of the week (abbreviated)
Definition date.c:61
static time_t mutt_date_parse_rfc5322_strict(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition date.c:540
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition date.c:220
time_t mutt_date_add_timeout(time_t now, time_t timeout)
Safely add a timeout to a given time_t value.
Definition date.c:896
static const struct Tz TimeZones[]
Lookup table of Time Zones.
Definition date.c:78
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:459
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition date.c:719
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition date.c:859
void mutt_time_now(struct timespec *tp)
Set the provided time field to the current time.
Definition date.c:483
void mutt_date_normalize_time(struct tm *tm)
Fix the contents of a struct tm.
Definition date.c:313
static time_t add_tz_offset(time_t t, bool w, time_t h, time_t m)
Compute and add a timezone offset to an UTC time.
Definition date.c:173
static const char * timestamp(time_t stamp)
Create a YYYY-MM-DD HH:MM:SS timestamp.
Definition logging.c:79
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition string.c:457
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
Match a precompiled regex against a string.
Definition prex.c:301
Manage precompiled / predefined regular expressions.
@ PREX_IMAP_DATE_MATCH_TIME
15-MAR-2020 [15:09:35] -0700
Definition prex.h:168
@ PREX_IMAP_DATE_MATCH_YEAR
15-MAR-[2020] 15:09:35 -0700
Definition prex.h:167
@ PREX_IMAP_DATE_MATCH_DAY
[ 4]-MAR-2020 15:09:35 -0700
Definition prex.h:163
@ PREX_IMAP_DATE_MATCH_TZ
15-MAR-2020 15:09:35 [-0700]
Definition prex.h:169
@ PREX_IMAP_DATE_MATCH_MONTH
15-[MAR]-2020 15:09:35 -0700
Definition prex.h:166
@ PREX_IMAP_DATE
[16-MAR-2020 15:09:35 -0700]
Definition prex.h:39
@ PREX_RFC5322_DATE_LAX
[Mon, (Comment) 16 Mar 2020 15:09:35 -0700]
Definition prex.h:38
@ PREX_RFC5322_DATE_LAX_MATCH_SECOND
Tue, 3 Mar 2020 14:32:[55] +0200
Definition prex.h:147
@ PREX_RFC5322_DATE_LAX_MATCH_TZ
Tue, 3 Mar 2020 14:32:55 [+0200]
Definition prex.h:150
@ PREX_RFC5322_DATE_LAX_MATCH_YEAR
Tue, 3 Mar [2020] 14:32:55 +0200
Definition prex.h:139
@ PREX_RFC5322_DATE_LAX_MATCH_HOUR
Tue, 3 Mar 2020 [14]:32:55 +0200
Definition prex.h:141
@ PREX_RFC5322_DATE_LAX_MATCH_MINUTE
Tue, 3 Mar 2020 14:[32]:55 +0200
Definition prex.h:143
@ PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS
Tue, 3 Mar 2020 14:32:55[UT]
Definition prex.h:151
@ PREX_RFC5322_DATE_LAX_MATCH_MONTH
Tue, 3 [Jan] 2020 14:32:55 +0200
Definition prex.h:137
@ PREX_RFC5322_DATE_LAX_MATCH_DAY
Tue, [3] Mar 2020 14:32:55 +0200
Definition prex.h:135
Manage regular expressions.
static size_t mutt_regmatch_len(const regmatch_t *match)
Return the length of a match.
Definition regex3.h:76
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition regex3.h:56
String manipulation functions.
String manipulation buffer.
Definition buffer.h:36
List of recognised Timezones.
Definition date.h:53
unsigned char zminutes
Minutes away from UTC.
Definition date.h:56
bool zoccident
True if west of UTC, False if East.
Definition date.h:57
char tzname[8]
Name, e.g. UTC.
Definition date.h:54
unsigned char zhours
Hours away from UTC.
Definition date.h:55