NeoMutt  2025-12-11-276-g10b23b
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(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_hour < 0) || (t->tm_hour > 23) || (t->tm_min < 0) ||
261 (t->tm_min > 59) || (t->tm_sec < 0) || (t->tm_sec > 60))
262 {
263 return TIME_T_MIN;
264 }
265 if (t->tm_year > 9999)
266 return TIME_T_MAX;
267
268 /* Compute the number of days since January 1 in the same year */
269 int yday = AccumDaysPerMonth[t->tm_mon % countof(Months)];
270
271 /* The leap years are 1972 and every 4. year until 2096,
272 * but this algorithm will fail after year 2099 */
273 yday += t->tm_mday;
274 if ((t->tm_year % 4) || (t->tm_mon < 2))
275 yday--;
276 t->tm_yday = yday;
277
278 time_t g = yday;
279
280 /* Compute the number of days since January 1, 1970 */
281 g += (t->tm_year - 70) * (time_t) 365;
282 g += (t->tm_year - 69) / 4;
283
284 /* Compute the number of hours */
285 g *= 24;
286 g += t->tm_hour;
287
288 /* Compute the number of minutes */
289 g *= 60;
290 g += t->tm_min;
291
292 /* Compute the number of seconds */
293 g *= 60;
294 g += t->tm_sec;
295
296 if (local)
297 g -= compute_tz(g, t);
298
299 return g;
300}
301
311void mutt_date_normalize_time(struct tm *tm)
312{
313 if (!tm)
314 return;
315
316 static const char DaysPerMonth[countof(Months)] = {
317 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
318 };
319 int leap;
320
321 while (tm->tm_sec < 0)
322 {
323 tm->tm_sec += 60;
324 tm->tm_min--;
325 }
326 while (tm->tm_sec >= 60)
327 {
328 tm->tm_sec -= 60;
329 tm->tm_min++;
330 }
331 while (tm->tm_min < 0)
332 {
333 tm->tm_min += 60;
334 tm->tm_hour--;
335 }
336 while (tm->tm_min >= 60)
337 {
338 tm->tm_min -= 60;
339 tm->tm_hour++;
340 }
341 while (tm->tm_hour < 0)
342 {
343 tm->tm_hour += 24;
344 tm->tm_mday--;
345 }
346 while (tm->tm_hour >= 24)
347 {
348 tm->tm_hour -= 24;
349 tm->tm_mday++;
350 }
351 /* use loops on NNNdwmy user input values? */
352 while (tm->tm_mon < 0)
353 {
354 tm->tm_mon += 12;
355 tm->tm_year--;
356 }
357 while (tm->tm_mon >= 12)
358 {
359 tm->tm_mon -= 12;
360 tm->tm_year++;
361 }
362 while (tm->tm_mday <= 0)
363 {
364 if (tm->tm_mon)
365 {
366 tm->tm_mon--;
367 }
368 else
369 {
370 tm->tm_mon = 11;
371 tm->tm_year--;
372 }
373 tm->tm_mday += DaysPerMonth[tm->tm_mon] + is_leap_year_feb(tm);
374 }
375 while (tm->tm_mday > (DaysPerMonth[tm->tm_mon] + (leap = is_leap_year_feb(tm))))
376 {
377 tm->tm_mday -= DaysPerMonth[tm->tm_mon] + leap;
378 if (tm->tm_mon < 11)
379 {
380 tm->tm_mon++;
381 }
382 else
383 {
384 tm->tm_mon = 0;
385 tm->tm_year++;
386 }
387 }
388}
389
398void mutt_date_make_date(struct Buffer *buf, bool local)
399{
400 if (!buf)
401 return;
402
403 struct tm tm = { 0 };
404 int tz = 0;
405
406 time_t t = mutt_date_now();
407 if (local)
408 {
409 tm = mutt_date_localtime(t);
410 tz = mutt_date_local_tz(t);
411 }
412 else
413 {
414 tm = mutt_date_gmtime(t);
415 }
416
417 tz /= 60;
418
419 buf_add_printf(buf, "%s, %d %s %d %02d:%02d:%02d %+03d%02d", Weekdays[tm.tm_wday],
420 tm.tm_mday, Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour,
421 tm.tm_min, tm.tm_sec, tz / 60, abs(tz) % 60);
422}
423
433int mutt_date_check_month(const char *s)
434{
435 if (!s)
436 return -1;
437
438 char buf[4] = { 0 };
439 memcpy(buf, s, 3);
440 uint32_t sv;
441 memcpy(&sv, buf, sizeof(sv));
442 for (int i = 0; i < countof(Months); i++)
443 {
444 uint32_t mv;
445 memcpy(&mv, Months[i], sizeof(mv));
446 if (sv == mv)
447 return i;
448 }
449
450 return -1; /* error */
451}
452
457time_t mutt_date_now(void)
458{
459 return mutt_date_now_ms() / 1000;
460}
461
466uint64_t mutt_date_now_ms(void)
467{
468 struct timeval tv = { 0, 0 };
469 gettimeofday(&tv, NULL);
470 /* We assume that gettimeofday doesn't modify its first argument on failure.
471 * We also kind of assume that gettimeofday does not fail. */
472 return ((uint64_t) tv.tv_sec * 1000) + (tv.tv_usec / 1000);
473}
474
481void mutt_time_now(struct timespec *tp)
482{
483#ifdef HAVE_CLOCK_GETTIME
484 if (clock_gettime(CLOCK_REALTIME, tp) != 0)
485 mutt_perror("clock_gettime");
486#else
487 struct timeval tv = { 0, 0 };
488 if (gettimeofday(&tv, NULL) != 0)
489 mutt_perror("gettimeofday");
490 tp->tv_sec = tv.tv_sec;
491 tp->tv_nsec = tv.tv_usec * 1000;
492#endif
493}
494
508static int parse_small_uint(const char *str, const char *end, int *val)
509{
510 const char *ptr = str;
511 int v = 0;
512 while ((ptr < end) && (ptr < (str + 5)) && (*ptr >= '0') && (*ptr <= '9'))
513 {
514 v = (v * 10) + (*ptr - '0');
515 ptr++;
516 }
517 *val = v;
518 return ptr - str;
519}
520
538static time_t mutt_date_parse_rfc5322_strict(const char *s, struct Tz *tz_out)
539{
540 size_t len = strlen(s);
541
542 /* Skip over the weekday, if any. */
543 if ((len >= 5) && (s[4] == ' ') &&
544 (eqi4(s, "Mon,") || eqi4(s, "Tue,") || eqi4(s, "Wed,") ||
545 eqi4(s, "Thu,") || eqi4(s, "Fri,") || eqi4(s, "Sat,") || eqi4(s, "Sun,")))
546 {
547 s += 5;
548 len -= 5;
549 }
550
551 while ((len > 0) && (*s == ' '))
552 {
553 s++;
554 len--;
555 }
556
557 if ((len == 0) || (*s < '0') || (*s > '9'))
558 return -1;
559
560 struct tm tm = { 0 };
561
562 /* Day */
563 int mday_len = parse_small_uint(s, s + len, &tm.tm_mday);
564 if ((mday_len == 0) || (mday_len > 2) || (tm.tm_mday > 31))
565 return -1;
566 s += mday_len;
567 len -= mday_len;
568
569 if ((len == 0) || (*s != ' '))
570 return -1;
571 s++;
572 len--;
573
574 /* Month */
575 if (len < 3)
576 return -1;
577 tm.tm_mon = mutt_date_check_month(s);
578 if (tm.tm_mon == -1)
579 return -1;
580 s += 3;
581 len -= 3;
582
583 if ((len == 0) || (*s != ' '))
584 return -1;
585 s++;
586 len--;
587
588 /* Year */
589 int year_len = parse_small_uint(s, s + len, &tm.tm_year);
590 if ((year_len != 2) && (year_len != 4))
591 return -1;
592 if (tm.tm_year < 50)
593 tm.tm_year += 100;
594 else if (tm.tm_year >= 1900)
595 tm.tm_year -= 1900;
596 s += year_len;
597 len -= year_len;
598
599 if ((len == 0) || (*s != ' '))
600 return -1;
601 s++;
602 len--;
603
604 /* Hour */
605 if ((len < 3) || (s[0] < '0') || (s[0] > '2') || (s[1] < '0') ||
606 (s[1] > '9') || (s[2] != ':'))
607 {
608 return -1;
609 }
610 tm.tm_hour = ((s[0] - '0') * 10) + (s[1] - '0');
611 if (tm.tm_hour > 23)
612 return -1;
613 s += 3;
614 len -= 3;
615
616 /* Minute */
617 if ((len < 2) || (s[0] < '0') || (s[0] > '5') || (s[1] < '0') || (s[1] > '9'))
618 return -1;
619 tm.tm_min = ((s[0] - '0') * 10) + (s[1] - '0');
620 if (tm.tm_min > 59)
621 return -1;
622 s += 2;
623 len -= 2;
624
625 /* Second (optional) */
626 if ((len > 0) && (s[0] == ':'))
627 {
628 s++;
629 len--;
630 if ((len < 2) || (s[0] < '0') || (s[0] > '5') || (s[1] < '0') || (s[1] > '9'))
631 return -1;
632 tm.tm_sec = ((s[0] - '0') * 10) + (s[1] - '0');
633 if (tm.tm_sec > 60)
634 return -1;
635 s += 2;
636 len -= 2;
637 }
638
639 while ((len > 0) && (*s == ' '))
640 {
641 s++;
642 len--;
643 }
644
645 /* Strip optional time zone comment and white space from the end
646 * (this is the only one that is very common) */
647 while ((len > 0) && (s[len - 1] == ' '))
648 len--;
649 if ((len >= 2) && (s[len - 1] == ')'))
650 {
651 for (int i = len - 2; i >= 0; i--)
652 {
653 if (s[i] == '(')
654 {
655 len = i;
656 break;
657 }
658 if (!mutt_isalpha(s[i]) && (s[i] != ' '))
659 return -1; /* give up more complex comment parsing */
660 }
661 }
662 while ((len > 0) && (s[len - 1] == ' '))
663 len--;
664
665 /* Time zone (optional) */
666 int zhours = 0;
667 int zminutes = 0;
668 bool zoccident = false;
669 if (len > 0)
670 {
671 if ((len == 5) && ((s[0] == '+') || (s[0] == '-')) && (s[1] >= '0') &&
672 (s[1] <= '9') && (s[2] >= '0') && (s[2] <= '9') && (s[3] >= '0') &&
673 (s[3] <= '9') && (s[4] >= '0') && (s[4] <= '9'))
674 {
675 zoccident = (s[0] == '-');
676 zhours = ((s[1] - '0') * 10) + (s[2] - '0');
677 zminutes = ((s[3] - '0') * 10) + (s[4] - '0');
678 }
679 else
680 {
681 for (int i = 0; i < len; i++)
682 {
683 if (!mutt_isalpha(s[i]))
684 return -1;
685 }
686 const struct Tz *tz = find_tz(s, len);
687 if (tz)
688 {
689 zhours = tz->zhours;
690 zminutes = tz->zminutes;
691 zoccident = tz->zoccident;
692 }
693 }
694 }
695
696 if (tz_out)
697 {
698 tz_out->zhours = zhours;
699 tz_out->zminutes = zminutes;
700 tz_out->zoccident = zoccident;
701 }
702
704}
705
717time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
718{
719 if (!s)
720 return -1;
721
722 const time_t strict_t = mutt_date_parse_rfc5322_strict(s, tz_out);
723 if (strict_t != -1)
724 return strict_t;
725
726 const regmatch_t *match = mutt_prex_capture(PREX_RFC5322_DATE_LAX, s);
727 if (!match)
728 {
729 mutt_debug(LL_DEBUG1, "Could not parse date: <%s>\n", s);
730 return -1;
731 }
732 mutt_debug(LL_DEBUG2, "Fallback regex for date: <%s>\n", s);
733
734 struct tm tm = { 0 };
735
736 // clang-format off
737 const regmatch_t *mday = &match[PREX_RFC5322_DATE_LAX_MATCH_DAY];
738 const regmatch_t *mmonth = &match[PREX_RFC5322_DATE_LAX_MATCH_MONTH];
739 const regmatch_t *myear = &match[PREX_RFC5322_DATE_LAX_MATCH_YEAR];
740 const regmatch_t *mhour = &match[PREX_RFC5322_DATE_LAX_MATCH_HOUR];
741 const regmatch_t *mminute = &match[PREX_RFC5322_DATE_LAX_MATCH_MINUTE];
742 const regmatch_t *msecond = &match[PREX_RFC5322_DATE_LAX_MATCH_SECOND];
743 const regmatch_t *mtz = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ];
744 const regmatch_t *mtzobs = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS];
745 // clang-format on
746
747 /* Day */
748 sscanf(s + mutt_regmatch_start(mday), "%d", &tm.tm_mday);
749 if (tm.tm_mday > 31)
750 return -1;
751
752 /* Month */
753 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
754
755 /* Year */
756 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
757 if (tm.tm_year < 50)
758 tm.tm_year += 100;
759 else if (tm.tm_year >= 1900)
760 tm.tm_year -= 1900;
761
762 /* Time */
763 int hour = 0, min = 0, sec = 0;
764 sscanf(s + mutt_regmatch_start(mhour), "%d", &hour);
765 sscanf(s + mutt_regmatch_start(mminute), "%d", &min);
766 if (mutt_regmatch_start(msecond) != -1)
767 sscanf(s + mutt_regmatch_start(msecond), "%d", &sec);
768 if ((hour > 23) || (min > 59) || (sec > 60))
769 return -1;
770 tm.tm_hour = hour;
771 tm.tm_min = min;
772 tm.tm_sec = sec;
773
774 /* Time zone */
775 int zhours = 0;
776 int zminutes = 0;
777 bool zoccident = false;
778 if (mutt_regmatch_start(mtz) != -1)
779 {
780 char direction = '\0';
781 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
782 zoccident = (direction == '-');
783 }
784 else if (mutt_regmatch_start(mtzobs) != -1)
785 {
786 const struct Tz *tz = find_tz(s + mutt_regmatch_start(mtzobs),
787 mutt_regmatch_len(mtzobs));
788 if (tz)
789 {
790 zhours = tz->zhours;
791 zminutes = tz->zminutes;
792 zoccident = tz->zoccident;
793 }
794 }
795
796 if (tz_out)
797 {
798 tz_out->zhours = zhours;
799 tz_out->zminutes = zminutes;
800 tz_out->zoccident = zoccident;
801 }
802
804}
805
812int mutt_date_make_imap(struct Buffer *buf, time_t timestamp)
813{
814 if (!buf)
815 return -1;
816
817 struct tm tm = mutt_date_localtime(timestamp);
819
820 tz /= 60;
821
822 return buf_printf(buf, "%02d-%s-%d %02d:%02d:%02d %+03d%02d", tm.tm_mday,
823 Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour, tm.tm_min,
824 tm.tm_sec, tz / 60, abs(tz) % 60);
825}
826
838int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
839{
840 if (!buf)
841 return -1;
842
843 struct tm tm = mutt_date_gmtime(timestamp);
844 return snprintf(buf, buflen, "%s, %d %s %d %02d:%02d:%02d UTC",
845 Weekdays[tm.tm_wday], tm.tm_mday, Months[tm.tm_mon],
846 tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
847}
848
855time_t mutt_date_parse_imap(const char *s)
856{
857 const regmatch_t *match = mutt_prex_capture(PREX_IMAP_DATE, s);
858 if (!match)
859 return 0;
860
861 const regmatch_t *mday = &match[PREX_IMAP_DATE_MATCH_DAY];
862 const regmatch_t *mmonth = &match[PREX_IMAP_DATE_MATCH_MONTH];
863 const regmatch_t *myear = &match[PREX_IMAP_DATE_MATCH_YEAR];
864 const regmatch_t *mtime = &match[PREX_IMAP_DATE_MATCH_TIME];
865 const regmatch_t *mtz = &match[PREX_IMAP_DATE_MATCH_TZ];
866
867 struct tm tm = { 0 };
868
869 sscanf(s + mutt_regmatch_start(mday), " %d", &tm.tm_mday);
870 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
871 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
872 tm.tm_year -= 1900;
873 sscanf(s + mutt_regmatch_start(mtime), "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
874
875 char direction = '\0';
876 int zhours = 0, zminutes = 0;
877 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
878 bool zoccident = (direction == '-');
879
880 return add_tz_offset(mutt_date_make_time(&tm, false), zoccident, zhours, zminutes);
881}
882
891time_t mutt_date_add_timeout(time_t now, time_t timeout)
892{
893 if (timeout < 0)
894 return now;
895
896 if ((TIME_T_MAX - now) < timeout)
897 return TIME_T_MAX;
898
899 return now + timeout;
900}
901
907struct tm mutt_date_localtime(time_t t)
908{
909 struct tm tm = { 0 };
910
911 struct tm *ret = localtime_r(&t, &tm);
912 if (!ret)
913 {
914 mutt_debug(LL_DEBUG1, "Could not convert time_t via localtime_r() to struct tm: time_t = %jd\n",
915 (intmax_t) t);
916 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
917 mktime(&default_tm); // update derived fields making tm into a valid tm.
918 tm = default_tm;
919 }
920 return tm;
921}
922
928struct tm mutt_date_gmtime(time_t t)
929{
930 struct tm tm = { 0 };
931
932 struct tm *ret = gmtime_r(&t, &tm);
933 if (!ret)
934 {
935 mutt_debug(LL_DEBUG1, "Could not convert time_t via gmtime_r() to struct tm: time_t = %jd\n",
936 (intmax_t) t);
937 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
938 mktime(&default_tm); // update derived fields making tm into a valid tm.
939 tm = default_tm;
940 }
941 return tm;
942}
943
952size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
953{
954 if (!buf || !format)
955 return 0;
956
957 struct tm tm = mutt_date_localtime(t);
958 return strftime(buf, buflen, format, &tm);
959}
960
970size_t mutt_date_localtime_format_locale(char *buf, size_t buflen,
971 const char *format, time_t t, locale_t loc)
972{
973 if (!buf || !format)
974 return 0;
975
976 struct tm tm = mutt_date_localtime(t);
977 return strftime_l(buf, buflen, format, &tm, loc);
978}
979
984void mutt_date_sleep_ms(size_t ms)
985{
986 const struct timespec sleep = {
987 .tv_sec = ms / 1000,
988 .tv_nsec = (ms % 1000) * 1000000UL,
989 };
990 nanosleep(&sleep, NULL);
991}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
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:106
#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:907
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition date.c:952
uint64_t mutt_date_now_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition date.c:466
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:398
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:508
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:984
int mutt_date_check_month(const char *s)
Is the string a valid month name.
Definition date.c:433
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:928
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:970
int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
Format date in TLS certificate verification style.
Definition date.c:838
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:812
static int is_leap_year_feb(struct tm *tm)
Is a given February in a leap year.
Definition date.c:203
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:538
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:891
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:457
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition date.c:717
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition date.c:855
void mutt_time_now(struct timespec *tp)
Set the provided time field to the current time.
Definition date.c:481
void mutt_date_normalize_time(struct tm *tm)
Fix the contents of a struct tm.
Definition date.c:311
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