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