NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
date.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <ctype.h>
31#include <stdbool.h>
32#include <stdint.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <sys/time.h>
37#include <time.h>
38#include "date.h"
39#include "buffer.h"
40#include "eqi.h"
41#include "logging2.h"
42#include "memory.h"
43#include "prex.h"
44#include "regex3.h"
45#include "string2.h"
46
50static const char *const Weekdays[] = {
51 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
52};
53
57static const char *const Months[] = {
58 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
59 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
60};
61
67static const struct Tz TimeZones[] = {
68 // clang-format off
69 { "aat", 1, 0, true }, /* Atlantic Africa Time */
70 { "adt", 4, 0, false }, /* Arabia DST */
71 { "ast", 3, 0, false }, /* Arabia */
72//{ "ast", 4, 0, true }, /* Atlantic */
73 { "bst", 1, 0, false }, /* British DST */
74 { "cat", 1, 0, false }, /* Central Africa */
75 { "cdt", 5, 0, true },
76 { "cest", 2, 0, false }, /* Central Europe DST */
77 { "cet", 1, 0, false }, /* Central Europe */
78 { "cst", 6, 0, true },
79//{ "cst", 8, 0, false }, /* China */
80//{ "cst", 9, 30, false }, /* Australian Central Standard Time */
81 { "eat", 3, 0, false }, /* East Africa */
82 { "edt", 4, 0, true },
83 { "eest", 3, 0, false }, /* Eastern Europe DST */
84 { "eet", 2, 0, false }, /* Eastern Europe */
85 { "egst", 0, 0, false }, /* Eastern Greenland DST */
86 { "egt", 1, 0, true }, /* Eastern Greenland */
87 { "est", 5, 0, true },
88 { "gmt", 0, 0, false },
89 { "gst", 4, 0, false }, /* Presian Gulf */
90 { "hkt", 8, 0, false }, /* Hong Kong */
91 { "ict", 7, 0, false }, /* Indochina */
92 { "idt", 3, 0, false }, /* Israel DST */
93 { "ist", 2, 0, false }, /* Israel */
94//{ "ist", 5, 30, false }, /* India */
95 { "jst", 9, 0, false }, /* Japan */
96 { "kst", 9, 0, false }, /* Korea */
97 { "mdt", 6, 0, true },
98 { "met", 1, 0, false }, /* This is now officially CET */
99 { "met dst", 2, 0, false }, /* MET in Daylight Saving Time */
100 { "msd", 4, 0, false }, /* Moscow DST */
101 { "msk", 3, 0, false }, /* Moscow */
102 { "mst", 7, 0, true },
103 { "nzdt", 13, 0, false }, /* New Zealand DST */
104 { "nzst", 12, 0, false }, /* New Zealand */
105 { "pdt", 7, 0, true },
106 { "pst", 8, 0, true },
107 { "sat", 2, 0, false }, /* South Africa */
108 { "smt", 4, 0, false }, /* Seychelles */
109 { "sst", 11, 0, true }, /* Samoa */
110//{ "sst", 8, 0, false }, /* Singapore */
111 { "utc", 0, 0, false },
112 { "wat", 0, 0, false }, /* West Africa */
113 { "west", 1, 0, false }, /* Western Europe DST */
114 { "wet", 0, 0, false }, /* Western Europe */
115 { "wgst", 2, 0, true }, /* Western Greenland DST */
116 { "wgt", 3, 0, true }, /* Western Greenland */
117 { "wst", 8, 0, false }, /* Western Australia */
118 // clang-format on
119};
120
130static int compute_tz(time_t g, struct tm *utc)
131{
132 struct tm lt = mutt_date_localtime(g);
133
134 int tz = (((lt.tm_hour - utc->tm_hour) * 60) + (lt.tm_min - utc->tm_min)) * 60;
135
136 int yday = (lt.tm_yday - utc->tm_yday);
137 if (yday != 0)
138 {
139 /* This code is optimized to negative timezones (West of Greenwich) */
140 if ((yday == -1) || /* UTC passed midnight before localtime */
141 (yday > 1)) /* UTC passed new year before localtime */
142 {
143 tz -= (24 * 60 * 60);
144 }
145 else
146 {
147 tz += (24 * 60 * 60);
148 }
149 }
150
151 return tz;
152}
153
162static time_t add_tz_offset(time_t t, bool w, time_t h, time_t m)
163{
164 if ((t != TIME_T_MAX) && (t != TIME_T_MIN))
165 return t + (w ? 1 : -1) * (((time_t) h * 3600) + ((time_t) m * 60));
166 else
167 return t;
168}
169
177static const struct Tz *find_tz(const char *s, size_t len)
178{
179 for (size_t i = 0; i < mutt_array_size(TimeZones); i++)
180 {
181 if (mutt_istrn_equal(TimeZones[i].tzname, s, len))
182 return &TimeZones[i];
183 }
184 return NULL;
185}
186
192static int is_leap_year_feb(struct tm *tm)
193{
194 if (tm->tm_mon != 1)
195 return 0;
196
197 int y = tm->tm_year + 1900;
198 return ((y & 3) == 0) && (((y % 100) != 0) || ((y % 400) == 0));
199}
200
210{
211 /* Check we haven't overflowed the time (on 32-bit arches) */
212 if ((t == TIME_T_MAX) || (t == TIME_T_MIN))
213 return 0;
214
215 if (t == 0)
216 t = mutt_date_now();
217
218 struct tm tm = mutt_date_gmtime(t);
219 return compute_tz(t, &tm);
220}
221
232time_t mutt_date_make_time(struct tm *t, bool local)
233{
234 if (!t)
235 return TIME_T_MIN;
236
237 static const int AccumDaysPerMonth[mutt_array_size(Months)] = {
238 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
239 };
240
241 /* Prevent an integer overflow, with some arbitrary limits. */
242 if (t->tm_year > 10000)
243 return TIME_T_MAX;
244 if (t->tm_year < -10000)
245 return TIME_T_MIN;
246
247 if ((t->tm_mday < 1) || (t->tm_mday > 31))
248 return TIME_T_MIN;
249 if ((t->tm_hour < 0) || (t->tm_hour > 23) || (t->tm_min < 0) ||
250 (t->tm_min > 59) || (t->tm_sec < 0) || (t->tm_sec > 60))
251 {
252 return TIME_T_MIN;
253 }
254 if (t->tm_year > 9999)
255 return TIME_T_MAX;
256
257 /* Compute the number of days since January 1 in the same year */
258 int yday = AccumDaysPerMonth[t->tm_mon % mutt_array_size(Months)];
259
260 /* The leap years are 1972 and every 4. year until 2096,
261 * but this algorithm will fail after year 2099 */
262 yday += t->tm_mday;
263 if ((t->tm_year % 4) || (t->tm_mon < 2))
264 yday--;
265 t->tm_yday = yday;
266
267 time_t g = yday;
268
269 /* Compute the number of days since January 1, 1970 */
270 g += (t->tm_year - 70) * (time_t) 365;
271 g += (t->tm_year - 69) / 4;
272
273 /* Compute the number of hours */
274 g *= 24;
275 g += t->tm_hour;
276
277 /* Compute the number of minutes */
278 g *= 60;
279 g += t->tm_min;
280
281 /* Compute the number of seconds */
282 g *= 60;
283 g += t->tm_sec;
284
285 if (local)
286 g -= compute_tz(g, t);
287
288 return g;
289}
290
300void mutt_date_normalize_time(struct tm *tm)
301{
302 if (!tm)
303 return;
304
305 static const char DaysPerMonth[mutt_array_size(Months)] = {
306 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
307 };
308 int leap;
309
310 while (tm->tm_sec < 0)
311 {
312 tm->tm_sec += 60;
313 tm->tm_min--;
314 }
315 while (tm->tm_sec >= 60)
316 {
317 tm->tm_sec -= 60;
318 tm->tm_min++;
319 }
320 while (tm->tm_min < 0)
321 {
322 tm->tm_min += 60;
323 tm->tm_hour--;
324 }
325 while (tm->tm_min >= 60)
326 {
327 tm->tm_min -= 60;
328 tm->tm_hour++;
329 }
330 while (tm->tm_hour < 0)
331 {
332 tm->tm_hour += 24;
333 tm->tm_mday--;
334 }
335 while (tm->tm_hour >= 24)
336 {
337 tm->tm_hour -= 24;
338 tm->tm_mday++;
339 }
340 /* use loops on NNNdwmy user input values? */
341 while (tm->tm_mon < 0)
342 {
343 tm->tm_mon += 12;
344 tm->tm_year--;
345 }
346 while (tm->tm_mon >= 12)
347 {
348 tm->tm_mon -= 12;
349 tm->tm_year++;
350 }
351 while (tm->tm_mday <= 0)
352 {
353 if (tm->tm_mon)
354 {
355 tm->tm_mon--;
356 }
357 else
358 {
359 tm->tm_mon = 11;
360 tm->tm_year--;
361 }
362 tm->tm_mday += DaysPerMonth[tm->tm_mon] + is_leap_year_feb(tm);
363 }
364 while (tm->tm_mday > (DaysPerMonth[tm->tm_mon] + (leap = is_leap_year_feb(tm))))
365 {
366 tm->tm_mday -= DaysPerMonth[tm->tm_mon] + leap;
367 if (tm->tm_mon < 11)
368 {
369 tm->tm_mon++;
370 }
371 else
372 {
373 tm->tm_mon = 0;
374 tm->tm_year++;
375 }
376 }
377}
378
387void mutt_date_make_date(struct Buffer *buf, bool local)
388{
389 if (!buf)
390 return;
391
392 struct tm tm = { 0 };
393 int tz = 0;
394
395 time_t t = mutt_date_now();
396 if (local)
397 {
398 tm = mutt_date_localtime(t);
399 tz = mutt_date_local_tz(t);
400 }
401 else
402 {
403 tm = mutt_date_gmtime(t);
404 }
405
406 tz /= 60;
407
408 buf_add_printf(buf, "%s, %d %s %d %02d:%02d:%02d %+03d%02d", Weekdays[tm.tm_wday],
409 tm.tm_mday, Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour,
410 tm.tm_min, tm.tm_sec, tz / 60, abs(tz) % 60);
411}
412
422int mutt_date_check_month(const char *s)
423{
424 if (!s)
425 return -1;
426
427 char buf[4] = { 0 };
428 memcpy(buf, s, 3);
429 uint32_t sv;
430 memcpy(&sv, buf, sizeof(sv));
431 for (int i = 0; i < mutt_array_size(Months); i++)
432 {
433 uint32_t mv;
434 memcpy(&mv, Months[i], sizeof(mv));
435 if (sv == mv)
436 return i;
437 }
438
439 return -1; /* error */
440}
441
446time_t mutt_date_now(void)
447{
448 return mutt_date_now_ms() / 1000;
449}
450
455uint64_t mutt_date_now_ms(void)
456{
457 struct timeval tv = { 0, 0 };
458 gettimeofday(&tv, NULL);
459 /* We assume that gettimeofday doesn't modify its first argument on failure.
460 * We also kind of assume that gettimeofday does not fail. */
461 return (uint64_t) (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
462}
463
477static int parse_small_uint(const char *str, const char *end, int *val)
478{
479 const char *ptr = str;
480 int v = 0;
481 while ((ptr < end) && (ptr < (str + 5)) && (*ptr >= '0') && (*ptr <= '9'))
482 {
483 v = (v * 10) + (*ptr - '0');
484 ++ptr;
485 }
486 *val = v;
487 return ptr - str;
488}
489
507static time_t mutt_date_parse_rfc5322_strict(const char *s, struct Tz *tz_out)
508{
509 size_t len = strlen(s);
510
511 /* Skip over the weekday, if any. */
512 if ((len >= 5) && (s[4] == ' ') &&
513 (eqi4(s, "Mon,") || eqi4(s, "Tue,") || eqi4(s, "Wed,") ||
514 eqi4(s, "Thu,") || eqi4(s, "Fri,") || eqi4(s, "Sat,") || eqi4(s, "Sun,")))
515 {
516 s += 5;
517 len -= 5;
518 }
519
520 while ((len > 0) && (*s == ' '))
521 {
522 ++s;
523 --len;
524 }
525
526 if ((len == 0) || (*s < '0') || (*s > '9'))
527 return -1;
528
529 struct tm tm = { 0 };
530
531 /* Day */
532 int mday_len = parse_small_uint(s, s + len, &tm.tm_mday);
533 if ((mday_len == 0) || (mday_len > 2) || (tm.tm_mday > 31))
534 return -1;
535 s += mday_len;
536 len -= mday_len;
537
538 if ((len == 0) || (*s != ' '))
539 return -1;
540 ++s;
541 --len;
542
543 /* Month */
544 if (len < 3)
545 return -1;
546 tm.tm_mon = mutt_date_check_month(s);
547 if (tm.tm_mon == -1)
548 return -1;
549 s += 3;
550 len -= 3;
551
552 if ((len == 0) || (*s != ' '))
553 return -1;
554 ++s;
555 --len;
556
557 /* Year */
558 int year_len = parse_small_uint(s, s + len, &tm.tm_year);
559 if ((year_len != 2) && (year_len != 4))
560 return -1;
561 if (tm.tm_year < 50)
562 tm.tm_year += 100;
563 else if (tm.tm_year >= 1900)
564 tm.tm_year -= 1900;
565 s += year_len;
566 len -= year_len;
567
568 if ((len == 0) || (*s != ' '))
569 return -1;
570 ++s;
571 --len;
572
573 /* Hour */
574 if ((len < 3) || (s[0] < '0') || (s[0] > '2') || (s[1] < '0') ||
575 (s[1] > '9') || (s[2] != ':'))
576 {
577 return -1;
578 }
579 tm.tm_hour = ((s[0] - '0') * 10) + (s[1] - '0');
580 if (tm.tm_hour > 23)
581 return -1;
582 s += 3;
583 len -= 3;
584
585 /* Minute */
586 if ((len < 2) || (s[0] < '0') || (s[0] > '5') || (s[1] < '0') || (s[1] > '9'))
587 return -1;
588 tm.tm_min = ((s[0] - '0') * 10) + (s[1] - '0');
589 if (tm.tm_min > 59)
590 return -1;
591 s += 2;
592 len -= 2;
593
594 /* Second (optional) */
595 if ((len > 0) && (s[0] == ':'))
596 {
597 ++s;
598 --len;
599 if ((len < 2) || (s[0] < '0') || (s[0] > '5') || (s[1] < '0') || (s[1] > '9'))
600 return -1;
601 tm.tm_sec = ((s[0] - '0') * 10) + (s[1] - '0');
602 if (tm.tm_sec > 60)
603 return -1;
604 s += 2;
605 len -= 2;
606 }
607
608 while ((len > 0) && (*s == ' '))
609 {
610 ++s;
611 --len;
612 }
613
614 /* Strip optional time zone comment and white space from the end
615 * (this is the only one that is very common) */
616 while ((len > 0) && (s[len - 1] == ' '))
617 --len;
618 if ((len >= 2) && (s[len - 1] == ')'))
619 {
620 for (int i = len - 1; i-- > 0;)
621 {
622 if (s[i] == '(')
623 {
624 len = i;
625 break;
626 }
627 if (!isalpha(s[i]) && (s[i] != ' '))
628 return -1; /* give up more complex comment parsing */
629 }
630 }
631 while ((len > 0) && (s[len - 1] == ' '))
632 --len;
633
634 /* Time zone (optional) */
635 int zhours = 0;
636 int zminutes = 0;
637 bool zoccident = false;
638 if (len > 0)
639 {
640 if ((len == 5) && ((s[0] == '+') || (s[0] == '-')) && (s[1] >= '0') &&
641 (s[1] <= '9') && (s[2] >= '0') && (s[2] <= '9') && (s[3] >= '0') &&
642 (s[3] <= '9') && (s[4] >= '0') && (s[4] <= '9'))
643 {
644 zoccident = (s[0] == '-');
645 zhours = ((s[1] - '0') * 10) + (s[2] - '0');
646 zminutes = ((s[3] - '0') * 10) + (s[4] - '0');
647 }
648 else
649 {
650 for (int i = 0; i < len; ++i)
651 {
652 if (!isalpha(s[i]))
653 return -1;
654 }
655 const struct Tz *tz = find_tz(s, len);
656 if (tz)
657 {
658 zhours = tz->zhours;
659 zminutes = tz->zminutes;
660 zoccident = tz->zoccident;
661 }
662 }
663 }
664
665 if (tz_out)
666 {
667 tz_out->zhours = zhours;
668 tz_out->zminutes = zminutes;
669 tz_out->zoccident = zoccident;
670 }
671
673}
674
686time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
687{
688 if (!s)
689 return -1;
690
691 const time_t strict_t = mutt_date_parse_rfc5322_strict(s, tz_out);
692 if (strict_t != -1)
693 return strict_t;
694
695 const regmatch_t *match = mutt_prex_capture(PREX_RFC5322_DATE_LAX, s);
696 if (!match)
697 {
698 mutt_debug(LL_DEBUG1, "Could not parse date: <%s>\n", s);
699 return -1;
700 }
701 mutt_debug(LL_DEBUG2, "Fallback regex for date: <%s>\n", s);
702
703 struct tm tm = { 0 };
704
705 // clang-format off
706 const regmatch_t *mday = &match[PREX_RFC5322_DATE_LAX_MATCH_DAY];
707 const regmatch_t *mmonth = &match[PREX_RFC5322_DATE_LAX_MATCH_MONTH];
708 const regmatch_t *myear = &match[PREX_RFC5322_DATE_LAX_MATCH_YEAR];
709 const regmatch_t *mhour = &match[PREX_RFC5322_DATE_LAX_MATCH_HOUR];
710 const regmatch_t *mminute = &match[PREX_RFC5322_DATE_LAX_MATCH_MINUTE];
711 const regmatch_t *msecond = &match[PREX_RFC5322_DATE_LAX_MATCH_SECOND];
712 const regmatch_t *mtz = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ];
713 const regmatch_t *mtzobs = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS];
714 // clang-format on
715
716 /* Day */
717 sscanf(s + mutt_regmatch_start(mday), "%d", &tm.tm_mday);
718 if (tm.tm_mday > 31)
719 return -1;
720
721 /* Month */
722 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
723
724 /* Year */
725 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
726 if (tm.tm_year < 50)
727 tm.tm_year += 100;
728 else if (tm.tm_year >= 1900)
729 tm.tm_year -= 1900;
730
731 /* Time */
732 int hour, min, sec = 0;
733 sscanf(s + mutt_regmatch_start(mhour), "%d", &hour);
734 sscanf(s + mutt_regmatch_start(mminute), "%d", &min);
735 if (mutt_regmatch_start(msecond) != -1)
736 sscanf(s + mutt_regmatch_start(msecond), "%d", &sec);
737 if ((hour > 23) || (min > 59) || (sec > 60))
738 return -1;
739 tm.tm_hour = hour;
740 tm.tm_min = min;
741 tm.tm_sec = sec;
742
743 /* Time zone */
744 int zhours = 0;
745 int zminutes = 0;
746 bool zoccident = false;
747 if (mutt_regmatch_start(mtz) != -1)
748 {
749 char direction;
750 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
751 zoccident = (direction == '-');
752 }
753 else if (mutt_regmatch_start(mtzobs) != -1)
754 {
755 const struct Tz *tz = find_tz(s + mutt_regmatch_start(mtzobs),
756 mutt_regmatch_len(mtzobs));
757 if (tz)
758 {
759 zhours = tz->zhours;
760 zminutes = tz->zminutes;
761 zoccident = tz->zoccident;
762 }
763 }
764
765 if (tz_out)
766 {
767 tz_out->zhours = zhours;
768 tz_out->zminutes = zminutes;
769 tz_out->zoccident = zoccident;
770 }
771
773}
774
784int mutt_date_make_imap(char *buf, size_t buflen, time_t timestamp)
785{
786 if (!buf)
787 return -1;
788
789 struct tm tm = mutt_date_localtime(timestamp);
791
792 tz /= 60;
793
794 return snprintf(buf, buflen, "%02d-%s-%d %02d:%02d:%02d %+03d%02d",
795 tm.tm_mday, Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour,
796 tm.tm_min, tm.tm_sec, tz / 60, abs(tz) % 60);
797}
798
810int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
811{
812 if (!buf)
813 return -1;
814
815 struct tm tm = mutt_date_gmtime(timestamp);
816 return snprintf(buf, buflen, "%s, %d %s %d %02d:%02d:%02d UTC",
817 Weekdays[tm.tm_wday], tm.tm_mday, Months[tm.tm_mon],
818 tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
819}
820
827time_t mutt_date_parse_imap(const char *s)
828{
829 const regmatch_t *match = mutt_prex_capture(PREX_IMAP_DATE, s);
830 if (!match)
831 return 0;
832
833 const regmatch_t *mday = &match[PREX_IMAP_DATE_MATCH_DAY];
834 const regmatch_t *mmonth = &match[PREX_IMAP_DATE_MATCH_MONTH];
835 const regmatch_t *myear = &match[PREX_IMAP_DATE_MATCH_YEAR];
836 const regmatch_t *mtime = &match[PREX_IMAP_DATE_MATCH_TIME];
837 const regmatch_t *mtz = &match[PREX_IMAP_DATE_MATCH_TZ];
838
839 struct tm tm = { 0 };
840
841 sscanf(s + mutt_regmatch_start(mday), " %d", &tm.tm_mday);
842 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
843 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
844 tm.tm_year -= 1900;
845 sscanf(s + mutt_regmatch_start(mtime), "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
846
847 char direction;
848 int zhours, zminutes;
849 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
850 bool zoccident = (direction == '-');
851
852 return add_tz_offset(mutt_date_make_time(&tm, false), zoccident, zhours, zminutes);
853}
854
863time_t mutt_date_add_timeout(time_t now, time_t timeout)
864{
865 if (timeout < 0)
866 return now;
867
868 if ((TIME_T_MAX - now) < timeout)
869 return TIME_T_MAX;
870
871 return now + timeout;
872}
873
879struct tm mutt_date_localtime(time_t t)
880{
881 struct tm tm = { 0 };
882
883 struct tm *ret = localtime_r(&t, &tm);
884 if (!ret)
885 {
886 mutt_debug(LL_DEBUG1, "Could not convert time_t via localtime_r() to struct tm: time_t = %jd\n",
887 (intmax_t) t);
888 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
889 mktime(&default_tm); // update derived fields making tm into a valid tm.
890 tm = default_tm;
891 }
892 return tm;
893}
894
900struct tm mutt_date_gmtime(time_t t)
901{
902 struct tm tm = { 0 };
903
904 struct tm *ret = gmtime_r(&t, &tm);
905 if (!ret)
906 {
907 mutt_debug(LL_DEBUG1, "Could not convert time_t via gmtime_r() to struct tm: time_t = %jd\n",
908 (intmax_t) t);
909 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
910 mktime(&default_tm); // update derived fields making tm into a valid tm.
911 tm = default_tm;
912 }
913 return tm;
914}
915
924size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
925{
926 if (!buf || !format)
927 return 0;
928
929 struct tm tm = mutt_date_localtime(t);
930 return strftime(buf, buflen, format, &tm);
931}
932
942size_t mutt_date_localtime_format_locale(char *buf, size_t buflen,
943 const char *format, time_t t, locale_t loc)
944{
945 if (!buf || !format)
946 return 0;
947
948 struct tm tm = mutt_date_localtime(t);
949 return strftime_l(buf, buflen, format, &tm, loc);
950}
951
956void mutt_date_sleep_ms(size_t ms)
957{
958 const struct timespec sleep = {
959 .tv_sec = ms / 1000,
960 .tv_nsec = (ms % 1000) * 1000000UL,
961 };
962 nanosleep(&sleep, NULL);
963}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:216
General purpose object for storing and parsing strings.
Time and date handling routines.
#define TIME_T_MAX
Definition: date.h:37
#define TIME_T_MIN
Definition: date.h:38
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
Logging Dispatcher.
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
static const char * timestamp(time_t stamp)
Create a YYYY-MM-DD HH:MM:SS timestamp.
Definition: logging.c:77
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:879
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition: date.c:924
uint64_t mutt_date_now_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition: date.c:455
static int compute_tz(time_t g, struct tm *utc)
Calculate the number of seconds east of UTC.
Definition: date.c:130
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition: date.c:387
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t
Definition: date.c:232
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:477
static const char *const Months[]
Months of the year (abbreviated)
Definition: date.c:57
void mutt_date_sleep_ms(size_t ms)
Sleep for milliseconds.
Definition: date.c:956
int mutt_date_check_month(const char *s)
Is the string a valid month name.
Definition: date.c:422
static const struct Tz * find_tz(const char *s, size_t len)
Look up a timezone.
Definition: date.c:177
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone.
Definition: date.c:900
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:942
int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
Format date in TLS certificate verification style.
Definition: date.c:810
static int is_leap_year_feb(struct tm *tm)
Is a given February in a leap year.
Definition: date.c:192
static const char *const Weekdays[]
Day of the week (abbreviated)
Definition: date.c:50
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:507
int mutt_date_make_imap(char *buf, size_t buflen, time_t timestamp)
Format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:784
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition: date.c:209
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:863
static const struct Tz TimeZones[]
Lookup table of Time Zones.
Definition: date.c:67
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:446
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:686
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:827
void mutt_date_normalize_time(struct tm *tm)
Fix the contents of a struct tm.
Definition: date.c:300
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:162
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:525
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
Match a precompiled regex against a string.
Definition: prex.c:289
Manage precompiled / predefined regular expressions.
@ PREX_IMAP_DATE_MATCH_TIME
15-MAR-2020 [15:09:35] -0700
Definition: prex.h:166
@ PREX_IMAP_DATE_MATCH_YEAR
15-MAR-[2020] 15:09:35 -0700
Definition: prex.h:165
@ PREX_IMAP_DATE_MATCH_DAY
[ 4]-MAR-2020 15:09:35 -0700
Definition: prex.h:161
@ PREX_IMAP_DATE_MATCH_TZ
15-MAR-2020 15:09:35 [-0700]
Definition: prex.h:167
@ PREX_IMAP_DATE_MATCH_MONTH
15-[MAR]-2020 15:09:35 -0700
Definition: prex.h:164
@ PREX_IMAP_DATE
[16-MAR-2020 15:09:35 -0700]
Definition: prex.h:38
@ PREX_RFC5322_DATE_LAX
[Mon, (Comment) 16 Mar 2020 15:09:35 -0700]
Definition: prex.h:37
@ PREX_RFC5322_DATE_LAX_MATCH_SECOND
Tue, 3 Mar 2020 14:32:[55] +0200
Definition: prex.h:145
@ PREX_RFC5322_DATE_LAX_MATCH_TZ
Tue, 3 Mar 2020 14:32:55 [+0200]
Definition: prex.h:148
@ PREX_RFC5322_DATE_LAX_MATCH_YEAR
Tue, 3 Mar [2020] 14:32:55 +0200
Definition: prex.h:137
@ PREX_RFC5322_DATE_LAX_MATCH_HOUR
Tue, 3 Mar 2020 [14]:32:55 +0200
Definition: prex.h:139
@ PREX_RFC5322_DATE_LAX_MATCH_MINUTE
Tue, 3 Mar 2020 14:[32]:55 +0200
Definition: prex.h:141
@ PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS
Tue, 3 Mar 2020 14:32:55[UT]
Definition: prex.h:149
@ PREX_RFC5322_DATE_LAX_MATCH_MONTH
Tue, 3 [Jan] 2020 14:32:55 +0200
Definition: prex.h:135
@ PREX_RFC5322_DATE_LAX_MATCH_DAY
Tue, [3] Mar 2020 14:32:55 +0200
Definition: prex.h:133
Manage regular expressions.
static size_t mutt_regmatch_len(const regmatch_t *match)
Return the length of a match.
Definition: regex3.h:80
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition: regex3.h:60
String manipulation functions.
String manipulation buffer.
Definition: buffer.h:34
List of recognised Timezones.
Definition: date.h:47
unsigned char zminutes
Minutes away from UTC.
Definition: date.h:50
bool zoccident
True if west of UTC, False if East.
Definition: date.h:51
char tzname[8]
Name, e.g. UTC.
Definition: date.h:48
unsigned char zhours
Hours away from UTC.
Definition: date.h:49
Time value with nanosecond precision.
Definition: file.h:50
time_t tv_sec
Number of seconds since the epoch.
Definition: file.h:51