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