File Coverage

deps/libgit2/src/util/date.c
Criterion Covered Total %
statement 8 436 1.8
branch 2 308 0.6
condition n/a
subroutine n/a
pod n/a
total 10 744 1.3


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "git2_util.h"
9              
10             #ifndef GIT_WIN32
11             #include
12             #endif
13              
14             #include "util.h"
15             #include "posix.h"
16             #include "date.h"
17              
18             #include
19             #include
20              
21             typedef enum {
22             DATE_NORMAL = 0,
23             DATE_RELATIVE,
24             DATE_SHORT,
25             DATE_LOCAL,
26             DATE_ISO8601,
27             DATE_RFC2822,
28             DATE_RAW
29             } date_mode;
30              
31             /*
32             * This is like mktime, but without normalization of tm_wday and tm_yday.
33             */
34 0           static git_time_t tm_to_time_t(const struct tm *tm)
35             {
36             static const int mdays[] = {
37             0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
38             };
39 0           int year = tm->tm_year - 70;
40 0           int month = tm->tm_mon;
41 0           int day = tm->tm_mday;
42              
43 0 0         if (year < 0 || year > 129) /* algo only works for 1970-2099 */
    0          
44 0           return -1;
45 0 0         if (month < 0 || month > 11) /* array bounds */
    0          
46 0           return -1;
47 0 0         if (month < 2 || (year + 2) % 4)
    0          
48 0           day--;
49 0 0         if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
    0          
    0          
50 0           return -1;
51 0           return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
52 0           tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
53             }
54              
55             static const char *month_names[] = {
56             "January", "February", "March", "April", "May", "June",
57             "July", "August", "September", "October", "November", "December"
58             };
59              
60             static const char *weekday_names[] = {
61             "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
62             };
63              
64              
65              
66             /*
67             * Check these. And note how it doesn't do the summer-time conversion.
68             *
69             * In my world, it's always summer, and things are probably a bit off
70             * in other ways too.
71             */
72             static const struct {
73             const char *name;
74             int offset;
75             int dst;
76             } timezone_names[] = {
77             { "IDLW", -12, 0, }, /* International Date Line West */
78             { "NT", -11, 0, }, /* Nome */
79             { "CAT", -10, 0, }, /* Central Alaska */
80             { "HST", -10, 0, }, /* Hawaii Standard */
81             { "HDT", -10, 1, }, /* Hawaii Daylight */
82             { "YST", -9, 0, }, /* Yukon Standard */
83             { "YDT", -9, 1, }, /* Yukon Daylight */
84             { "PST", -8, 0, }, /* Pacific Standard */
85             { "PDT", -8, 1, }, /* Pacific Daylight */
86             { "MST", -7, 0, }, /* Mountain Standard */
87             { "MDT", -7, 1, }, /* Mountain Daylight */
88             { "CST", -6, 0, }, /* Central Standard */
89             { "CDT", -6, 1, }, /* Central Daylight */
90             { "EST", -5, 0, }, /* Eastern Standard */
91             { "EDT", -5, 1, }, /* Eastern Daylight */
92             { "AST", -3, 0, }, /* Atlantic Standard */
93             { "ADT", -3, 1, }, /* Atlantic Daylight */
94             { "WAT", -1, 0, }, /* West Africa */
95              
96             { "GMT", 0, 0, }, /* Greenwich Mean */
97             { "UTC", 0, 0, }, /* Universal (Coordinated) */
98             { "Z", 0, 0, }, /* Zulu, alias for UTC */
99              
100             { "WET", 0, 0, }, /* Western European */
101             { "BST", 0, 1, }, /* British Summer */
102             { "CET", +1, 0, }, /* Central European */
103             { "MET", +1, 0, }, /* Middle European */
104             { "MEWT", +1, 0, }, /* Middle European Winter */
105             { "MEST", +1, 1, }, /* Middle European Summer */
106             { "CEST", +1, 1, }, /* Central European Summer */
107             { "MESZ", +1, 1, }, /* Middle European Summer */
108             { "FWT", +1, 0, }, /* French Winter */
109             { "FST", +1, 1, }, /* French Summer */
110             { "EET", +2, 0, }, /* Eastern Europe */
111             { "EEST", +2, 1, }, /* Eastern European Daylight */
112             { "WAST", +7, 0, }, /* West Australian Standard */
113             { "WADT", +7, 1, }, /* West Australian Daylight */
114             { "CCT", +8, 0, }, /* China Coast */
115             { "JST", +9, 0, }, /* Japan Standard */
116             { "EAST", +10, 0, }, /* Eastern Australian Standard */
117             { "EADT", +10, 1, }, /* Eastern Australian Daylight */
118             { "GST", +10, 0, }, /* Guam Standard */
119             { "NZT", +12, 0, }, /* New Zealand */
120             { "NZST", +12, 0, }, /* New Zealand Standard */
121             { "NZDT", +12, 1, }, /* New Zealand Daylight */
122             { "IDLE", +12, 0, }, /* International Date Line East */
123             };
124              
125 0           static size_t match_string(const char *date, const char *str)
126             {
127 0           size_t i = 0;
128              
129 0 0         for (i = 0; *date; date++, str++, i++) {
130 0 0         if (*date == *str)
131 0           continue;
132 0 0         if (toupper(*date) == toupper(*str))
133 0           continue;
134 0 0         if (!isalnum(*date))
135 0           break;
136 0           return 0;
137             }
138 0           return i;
139             }
140              
141 0           static int skip_alpha(const char *date)
142             {
143 0           int i = 0;
144             do {
145 0           i++;
146 0 0         } while (isalpha(date[i]));
147 0           return i;
148             }
149              
150             /*
151             * Parse month, weekday, or timezone name
152             */
153 0           static size_t match_alpha(const char *date, struct tm *tm, int *offset)
154             {
155             unsigned int i;
156              
157 0 0         for (i = 0; i < 12; i++) {
158 0           size_t match = match_string(date, month_names[i]);
159 0 0         if (match >= 3) {
160 0           tm->tm_mon = i;
161 0           return match;
162             }
163             }
164              
165 0 0         for (i = 0; i < 7; i++) {
166 0           size_t match = match_string(date, weekday_names[i]);
167 0 0         if (match >= 3) {
168 0           tm->tm_wday = i;
169 0           return match;
170             }
171             }
172              
173 0 0         for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
174 0           size_t match = match_string(date, timezone_names[i].name);
175 0 0         if (match >= 3 || match == strlen(timezone_names[i].name)) {
    0          
176 0           int off = timezone_names[i].offset;
177              
178             /* This is bogus, but we like summer */
179 0           off += timezone_names[i].dst;
180              
181             /* Only use the tz name offset if we don't have anything better */
182 0 0         if (*offset == -1)
183 0           *offset = 60*off;
184              
185 0           return match;
186             }
187             }
188              
189 0 0         if (match_string(date, "PM") == 2) {
190 0           tm->tm_hour = (tm->tm_hour % 12) + 12;
191 0           return 2;
192             }
193              
194 0 0         if (match_string(date, "AM") == 2) {
195 0           tm->tm_hour = (tm->tm_hour % 12) + 0;
196 0           return 2;
197             }
198              
199             /* BAD */
200 0           return skip_alpha(date);
201             }
202              
203 0           static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm)
204             {
205 0 0         if (month > 0 && month < 13 && day > 0 && day < 32) {
    0          
    0          
    0          
206 0           struct tm check = *tm;
207 0 0         struct tm *r = (now_tm ? &check : tm);
208             git_time_t specified;
209              
210 0           r->tm_mon = month - 1;
211 0           r->tm_mday = day;
212 0 0         if (year == -1) {
213 0 0         if (!now_tm)
214 0           return 1;
215 0           r->tm_year = now_tm->tm_year;
216             }
217 0 0         else if (year >= 1970 && year < 2100)
    0          
218 0           r->tm_year = year - 1900;
219 0 0         else if (year > 70 && year < 100)
    0          
220 0           r->tm_year = year;
221 0 0         else if (year < 38)
222 0           r->tm_year = year + 100;
223             else
224 0           return 0;
225 0 0         if (!now_tm)
226 0           return 1;
227              
228 0           specified = tm_to_time_t(r);
229              
230             /* Be it commit time or author time, it does not make
231             * sense to specify timestamp way into the future. Make
232             * sure it is not later than ten days from now...
233             */
234 0 0         if (now + 10*24*3600 < specified)
235 0           return 0;
236 0           tm->tm_mon = r->tm_mon;
237 0           tm->tm_mday = r->tm_mday;
238 0 0         if (year != -1)
239 0           tm->tm_year = r->tm_year;
240 0           return 1;
241             }
242 0           return 0;
243             }
244              
245 0           static size_t match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
246             {
247             time_t now;
248             struct tm now_tm;
249             struct tm *refuse_future;
250             long num2, num3;
251              
252 0           num2 = strtol(end+1, &end, 10);
253 0           num3 = -1;
254 0 0         if (*end == c && isdigit(end[1]))
    0          
255 0           num3 = strtol(end+1, &end, 10);
256              
257             /* Time? Date? */
258 0           switch (c) {
259             case ':':
260 0 0         if (num3 < 0)
261 0           num3 = 0;
262 0 0         if (num < 25 && num2 >= 0 && num2 < 60 && num3 >= 0 && num3 <= 60) {
    0          
    0          
    0          
    0          
263 0           tm->tm_hour = num;
264 0           tm->tm_min = num2;
265 0           tm->tm_sec = num3;
266 0           break;
267             }
268 0           return 0;
269              
270             case '-':
271             case '/':
272             case '.':
273 0           now = time(NULL);
274 0           refuse_future = NULL;
275 0 0         if (p_gmtime_r(&now, &now_tm))
276 0           refuse_future = &now_tm;
277              
278 0 0         if (num > 70) {
279             /* yyyy-mm-dd? */
280 0 0         if (is_date(num, num2, num3, refuse_future, now, tm))
281 0           break;
282             /* yyyy-dd-mm? */
283 0 0         if (is_date(num, num3, num2, refuse_future, now, tm))
284 0           break;
285             }
286             /* Our eastern European friends say dd.mm.yy[yy]
287             * is the norm there, so giving precedence to
288             * mm/dd/yy[yy] form only when separator is not '.'
289             */
290 0           if (c != '.' &&
291 0           is_date(num3, num, num2, refuse_future, now, tm))
292 0           break;
293             /* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */
294 0 0         if (is_date(num3, num2, num, refuse_future, now, tm))
295 0           break;
296             /* Funny European mm.dd.yy */
297 0           if (c == '.' &&
298 0           is_date(num3, num, num2, refuse_future, now, tm))
299 0           break;
300 0           return 0;
301             }
302 0           return end - date;
303             }
304              
305             /*
306             * Have we filled in any part of the time/date yet?
307             * We just do a binary 'and' to see if the sign bit
308             * is set in all the values.
309             */
310 0           static int nodate(struct tm *tm)
311             {
312 0           return (tm->tm_year &
313 0           tm->tm_mon &
314 0           tm->tm_mday &
315 0           tm->tm_hour &
316 0           tm->tm_min &
317 0           tm->tm_sec) < 0;
318             }
319              
320             /*
321             * We've seen a digit. Time? Year? Date?
322             */
323 0           static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
324             {
325             size_t n;
326             char *end;
327             unsigned long num;
328              
329 0           num = strtoul(date, &end, 10);
330              
331             /*
332             * Seconds since 1970? We trigger on that for any numbers with
333             * more than 8 digits. This is because we don't want to rule out
334             * numbers like 20070606 as a YYYYMMDD date.
335             */
336 0 0         if (num >= 100000000 && nodate(tm)) {
    0          
337 0           time_t time = num;
338 0 0         if (p_gmtime_r(&time, tm)) {
339 0           *tm_gmt = 1;
340 0           return end - date;
341             }
342             }
343              
344             /*
345             * Check for special formats: num[-.:/]num[same]num
346             */
347 0 0         switch (*end) {
348             case ':':
349             case '.':
350             case '/':
351             case '-':
352 0 0         if (isdigit(end[1])) {
353 0           size_t match = match_multi_number(num, *end, date, end, tm);
354 0 0         if (match)
355 0           return match;
356             }
357             }
358              
359             /*
360             * None of the special formats? Try to guess what
361             * the number meant. We use the number of digits
362             * to make a more educated guess..
363             */
364 0           n = 0;
365             do {
366 0           n++;
367 0 0         } while (isdigit(date[n]));
368              
369             /* Four-digit year or a timezone? */
370 0 0         if (n == 4) {
371 0 0         if (num <= 1400 && *offset == -1) {
    0          
372 0           unsigned int minutes = num % 100;
373 0           unsigned int hours = num / 100;
374 0           *offset = hours*60 + minutes;
375 0 0         } else if (num > 1900 && num < 2100)
    0          
376 0           tm->tm_year = num - 1900;
377 0           return n;
378             }
379              
380             /*
381             * Ignore lots of numerals. We took care of 4-digit years above.
382             * Days or months must be one or two digits.
383             */
384 0 0         if (n > 2)
385 0           return n;
386              
387             /*
388             * NOTE! We will give precedence to day-of-month over month or
389             * year numbers in the 1-12 range. So 05 is always "mday 5",
390             * unless we already have a mday..
391             *
392             * IOW, 01 Apr 05 parses as "April 1st, 2005".
393             */
394 0 0         if (num > 0 && num < 32 && tm->tm_mday < 0) {
    0          
    0          
395 0           tm->tm_mday = num;
396 0           return n;
397             }
398              
399             /* Two-digit year? */
400 0 0         if (n == 2 && tm->tm_year < 0) {
    0          
401 0 0         if (num < 10 && tm->tm_mday >= 0) {
    0          
402 0           tm->tm_year = num + 100;
403 0           return n;
404             }
405 0 0         if (num >= 70) {
406 0           tm->tm_year = num;
407 0           return n;
408             }
409             }
410              
411 0 0         if (num > 0 && num < 13 && tm->tm_mon < 0)
    0          
    0          
412 0           tm->tm_mon = num-1;
413              
414 0           return n;
415             }
416              
417 0           static size_t match_tz(const char *date, int *offp)
418             {
419             char *end;
420 0           int hour = strtoul(date + 1, &end, 10);
421 0           size_t n = end - (date + 1);
422 0           int min = 0;
423              
424 0 0         if (n == 4) {
425             /* hhmm */
426 0           min = hour % 100;
427 0           hour = hour / 100;
428 0 0         } else if (n != 2) {
429 0           min = 99; /* random stuff */
430 0 0         } else if (*end == ':') {
431             /* hh:mm? */
432 0           min = strtoul(end + 1, &end, 10);
433 0 0         if (end - (date + 1) != 5)
434 0           min = 99; /* random stuff */
435             } /* otherwise we parsed "hh" */
436              
437             /*
438             * Don't accept any random stuff. Even though some places have
439             * offset larger than 12 hours (e.g. Pacific/Kiritimati is at
440             * UTC+14), there is something wrong if hour part is much
441             * larger than that. We might also want to check that the
442             * minutes are divisible by 15 or something too. (Offset of
443             * Kathmandu, Nepal is UTC+5:45)
444             */
445 0 0         if (min < 60 && hour < 24) {
    0          
446 0           int offset = hour * 60 + min;
447 0 0         if (*date == '-')
448 0           offset = -offset;
449 0           *offp = offset;
450             }
451 0           return end - date;
452             }
453              
454             /*
455             * Parse a string like "0 +0000" as ancient timestamp near epoch, but
456             * only when it appears not as part of any other string.
457             */
458 0           static int match_object_header_date(const char *date, git_time_t *timestamp, int *offset)
459             {
460             char *end;
461             unsigned long stamp;
462             int ofs;
463              
464 0 0         if (*date < '0' || '9' <= *date)
    0          
465 0           return -1;
466 0           stamp = strtoul(date, &end, 10);
467 0 0         if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
    0          
    0          
    0          
468 0           return -1;
469 0           date = end + 2;
470 0           ofs = strtol(date, &end, 10);
471 0 0         if ((*end != '\0' && (*end != '\n')) || end != date + 4)
    0          
    0          
472 0           return -1;
473 0           ofs = (ofs / 100) * 60 + (ofs % 100);
474 0 0         if (date[-1] == '-')
475 0           ofs = -ofs;
476 0           *timestamp = stamp;
477 0           *offset = ofs;
478 0           return 0;
479             }
480              
481             /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
482             (i.e. English) day/month names, and it doesn't work correctly with %z. */
483 0           static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset)
484             {
485             struct tm tm;
486             int tm_gmt;
487             git_time_t dummy_timestamp;
488             int dummy_offset;
489              
490 0 0         if (!timestamp)
491 0           timestamp = &dummy_timestamp;
492 0 0         if (!offset)
493 0           offset = &dummy_offset;
494              
495 0           memset(&tm, 0, sizeof(tm));
496 0           tm.tm_year = -1;
497 0           tm.tm_mon = -1;
498 0           tm.tm_mday = -1;
499 0           tm.tm_isdst = -1;
500 0           tm.tm_hour = -1;
501 0           tm.tm_min = -1;
502 0           tm.tm_sec = -1;
503 0           *offset = -1;
504 0           tm_gmt = 0;
505              
506 0           if (*date == '@' &&
507 0           !match_object_header_date(date + 1, timestamp, offset))
508 0           return 0; /* success */
509             for (;;) {
510 0           size_t match = 0;
511 0           unsigned char c = *date;
512              
513             /* Stop at end of string or newline */
514 0 0         if (!c || c == '\n')
    0          
515             break;
516              
517 0 0         if (isalpha(c))
518 0           match = match_alpha(date, &tm, offset);
519 0 0         else if (isdigit(c))
520 0           match = match_digit(date, &tm, offset, &tm_gmt);
521 0 0         else if ((c == '-' || c == '+') && isdigit(date[1]))
    0          
    0          
522 0           match = match_tz(date, offset);
523              
524 0 0         if (!match) {
525             /* BAD */
526 0           match = 1;
527             }
528              
529 0           date += match;
530 0           }
531              
532             /* mktime uses local timezone */
533 0           *timestamp = tm_to_time_t(&tm);
534 0 0         if (*offset == -1)
535 0           *offset = (int)((time_t)*timestamp - mktime(&tm)) / 60;
536              
537 0 0         if (*timestamp == (git_time_t)-1)
538 0           return -1;
539              
540 0 0         if (!tm_gmt)
541 0           *timestamp -= *offset * 60;
542 0           return 0; /* success */
543             }
544              
545              
546             /*
547             * Relative time update (eg "2 days ago"). If we haven't set the time
548             * yet, we need to set it from current time.
549             */
550 0           static git_time_t update_tm(struct tm *tm, struct tm *now, unsigned long sec)
551             {
552             time_t n;
553              
554 0 0         if (tm->tm_mday < 0)
555 0           tm->tm_mday = now->tm_mday;
556 0 0         if (tm->tm_mon < 0)
557 0           tm->tm_mon = now->tm_mon;
558 0 0         if (tm->tm_year < 0) {
559 0           tm->tm_year = now->tm_year;
560 0 0         if (tm->tm_mon > now->tm_mon)
561 0           tm->tm_year--;
562             }
563              
564 0           n = mktime(tm) - sec;
565 0           p_localtime_r(&n, tm);
566 0           return n;
567             }
568              
569 0           static void date_now(struct tm *tm, struct tm *now, int *num)
570             {
571 0           GIT_UNUSED(num);
572 0           update_tm(tm, now, 0);
573 0           }
574              
575 0           static void date_yesterday(struct tm *tm, struct tm *now, int *num)
576             {
577 0           GIT_UNUSED(num);
578 0           update_tm(tm, now, 24*60*60);
579 0           }
580              
581 0           static void date_time(struct tm *tm, struct tm *now, int hour)
582             {
583 0 0         if (tm->tm_hour < hour)
584 0           date_yesterday(tm, now, NULL);
585 0           tm->tm_hour = hour;
586 0           tm->tm_min = 0;
587 0           tm->tm_sec = 0;
588 0           }
589              
590 0           static void date_midnight(struct tm *tm, struct tm *now, int *num)
591             {
592 0           GIT_UNUSED(num);
593 0           date_time(tm, now, 0);
594 0           }
595              
596 0           static void date_noon(struct tm *tm, struct tm *now, int *num)
597             {
598 0           GIT_UNUSED(num);
599 0           date_time(tm, now, 12);
600 0           }
601              
602 0           static void date_tea(struct tm *tm, struct tm *now, int *num)
603             {
604 0           GIT_UNUSED(num);
605 0           date_time(tm, now, 17);
606 0           }
607              
608 0           static void date_pm(struct tm *tm, struct tm *now, int *num)
609             {
610 0           int hour, n = *num;
611 0           *num = 0;
612 0           GIT_UNUSED(now);
613              
614 0           hour = tm->tm_hour;
615 0 0         if (n) {
616 0           hour = n;
617 0           tm->tm_min = 0;
618 0           tm->tm_sec = 0;
619             }
620 0           tm->tm_hour = (hour % 12) + 12;
621 0           }
622              
623 0           static void date_am(struct tm *tm, struct tm *now, int *num)
624             {
625 0           int hour, n = *num;
626 0           *num = 0;
627 0           GIT_UNUSED(now);
628              
629 0           hour = tm->tm_hour;
630 0 0         if (n) {
631 0           hour = n;
632 0           tm->tm_min = 0;
633 0           tm->tm_sec = 0;
634             }
635 0           tm->tm_hour = (hour % 12);
636 0           }
637              
638 0           static void date_never(struct tm *tm, struct tm *now, int *num)
639             {
640 0           time_t n = 0;
641 0           GIT_UNUSED(now);
642 0           GIT_UNUSED(num);
643 0           p_localtime_r(&n, tm);
644 0           }
645              
646             static const struct special {
647             const char *name;
648             void (*fn)(struct tm *, struct tm *, int *);
649             } special[] = {
650             { "yesterday", date_yesterday },
651             { "noon", date_noon },
652             { "midnight", date_midnight },
653             { "tea", date_tea },
654             { "PM", date_pm },
655             { "AM", date_am },
656             { "never", date_never },
657             { "now", date_now },
658             { NULL }
659             };
660              
661             static const char *number_name[] = {
662             "zero", "one", "two", "three", "four",
663             "five", "six", "seven", "eight", "nine", "ten",
664             };
665              
666             static const struct typelen {
667             const char *type;
668             int length;
669             } typelen[] = {
670             { "seconds", 1 },
671             { "minutes", 60 },
672             { "hours", 60*60 },
673             { "days", 24*60*60 },
674             { "weeks", 7*24*60*60 },
675             { NULL }
676             };
677              
678 0           static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
679             {
680             const struct typelen *tl;
681             const struct special *s;
682 0           const char *end = date;
683             int i;
684              
685 0 0         while (isalpha(*++end))
686             /* scan to non-alpha */;
687              
688 0 0         for (i = 0; i < 12; i++) {
689 0           size_t match = match_string(date, month_names[i]);
690 0 0         if (match >= 3) {
691 0           tm->tm_mon = i;
692 0           *touched = 1;
693 0           return end;
694             }
695             }
696              
697 0 0         for (s = special; s->name; s++) {
698 0           size_t len = strlen(s->name);
699 0 0         if (match_string(date, s->name) == len) {
700 0           s->fn(tm, now, num);
701 0           *touched = 1;
702 0           return end;
703             }
704             }
705              
706 0 0         if (!*num) {
707 0 0         for (i = 1; i < 11; i++) {
708 0           size_t len = strlen(number_name[i]);
709 0 0         if (match_string(date, number_name[i]) == len) {
710 0           *num = i;
711 0           *touched = 1;
712 0           return end;
713             }
714             }
715 0 0         if (match_string(date, "last") == 4) {
716 0           *num = 1;
717 0           *touched = 1;
718             }
719 0           return end;
720             }
721              
722 0           tl = typelen;
723 0 0         while (tl->type) {
724 0           size_t len = strlen(tl->type);
725 0 0         if (match_string(date, tl->type) >= len-1) {
726 0           update_tm(tm, now, tl->length * (unsigned long)*num);
727 0           *num = 0;
728 0           *touched = 1;
729 0           return end;
730             }
731 0           tl++;
732             }
733              
734 0 0         for (i = 0; i < 7; i++) {
735 0           size_t match = match_string(date, weekday_names[i]);
736 0 0         if (match >= 3) {
737 0           int diff, n = *num -1;
738 0           *num = 0;
739              
740 0           diff = tm->tm_wday - i;
741 0 0         if (diff <= 0)
742 0           n++;
743 0           diff += 7*n;
744              
745 0           update_tm(tm, now, diff * 24 * 60 * 60);
746 0           *touched = 1;
747 0           return end;
748             }
749             }
750              
751 0 0         if (match_string(date, "months") >= 5) {
752             int n;
753 0           update_tm(tm, now, 0); /* fill in date fields if needed */
754 0           n = tm->tm_mon - *num;
755 0           *num = 0;
756 0 0         while (n < 0) {
757 0           n += 12;
758 0           tm->tm_year--;
759             }
760 0           tm->tm_mon = n;
761 0           *touched = 1;
762 0           return end;
763             }
764              
765 0 0         if (match_string(date, "years") >= 4) {
766 0           update_tm(tm, now, 0); /* fill in date fields if needed */
767 0           tm->tm_year -= *num;
768 0           *num = 0;
769 0           *touched = 1;
770 0           return end;
771             }
772              
773 0           return end;
774             }
775              
776 0           static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
777             {
778             char *end;
779 0           unsigned long number = strtoul(date, &end, 10);
780              
781 0 0         switch (*end) {
782             case ':':
783             case '.':
784             case '/':
785             case '-':
786 0 0         if (isdigit(end[1])) {
787 0           size_t match = match_multi_number(number, *end, date, end, tm);
788 0 0         if (match)
789 0           return date + match;
790             }
791             }
792              
793             /* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */
794 0 0         if (date[0] != '0' || end - date <= 2)
    0          
795 0           *num = number;
796 0           return end;
797             }
798              
799             /*
800             * Do we have a pending number at the end, or when
801             * we see a new one? Let's assume it's a month day,
802             * as in "Dec 6, 1992"
803             */
804 0           static void pending_number(struct tm *tm, int *num)
805             {
806 0           int number = *num;
807              
808 0 0         if (number) {
809 0           *num = 0;
810 0 0         if (tm->tm_mday < 0 && number < 32)
    0          
811 0           tm->tm_mday = number;
812 0 0         else if (tm->tm_mon < 0 && number < 13)
    0          
813 0           tm->tm_mon = number-1;
814 0 0         else if (tm->tm_year < 0) {
815 0 0         if (number > 1969 && number < 2100)
    0          
816 0           tm->tm_year = number - 1900;
817 0 0         else if (number > 69 && number < 100)
    0          
818 0           tm->tm_year = number;
819 0 0         else if (number < 38)
820 0           tm->tm_year = 100 + number;
821             /* We mess up for number = 00 ? */
822             }
823             }
824 0           }
825              
826 0           static git_time_t approxidate_str(const char *date,
827             time_t time_sec,
828             int *error_ret)
829             {
830 0           int number = 0;
831 0           int touched = 0;
832 0           struct tm tm = {0}, now;
833              
834 0           p_localtime_r(&time_sec, &tm);
835 0           now = tm;
836              
837 0           tm.tm_year = -1;
838 0           tm.tm_mon = -1;
839 0           tm.tm_mday = -1;
840              
841             for (;;) {
842 0           unsigned char c = *date;
843 0 0         if (!c)
844 0           break;
845 0           date++;
846 0 0         if (isdigit(c)) {
847 0           pending_number(&tm, &number);
848 0           date = approxidate_digit(date-1, &tm, &number);
849 0           touched = 1;
850 0           continue;
851             }
852 0 0         if (isalpha(c))
853 0           date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
854 0           }
855 0           pending_number(&tm, &number);
856 0 0         if (!touched)
857 0           *error_ret = -1;
858 0           return update_tm(&tm, &now, 0);
859             }
860              
861 0           int git_date_parse(git_time_t *out, const char *date)
862             {
863             time_t time_sec;
864             git_time_t timestamp;
865 0           int offset, error_ret=0;
866              
867 0 0         if (!parse_date_basic(date, ×tamp, &offset)) {
868 0           *out = timestamp;
869 0           return 0;
870             }
871              
872 0 0         if (time(&time_sec) == -1)
873 0           return -1;
874              
875 0           *out = approxidate_str(date, time_sec, &error_ret);
876 0           return error_ret;
877             }
878              
879 3           int git_date_rfc2822_fmt(git_str *out, git_time_t time, int offset)
880             {
881             time_t t;
882             struct tm gmt;
883              
884 3 50         GIT_ASSERT_ARG(out);
885              
886 3           t = (time_t) (time + offset * 60);
887              
888 3 50         if (p_gmtime_r(&t, &gmt) == NULL)
889 0           return -1;
890              
891 3           return git_str_printf(out, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d",
892 3           weekday_names[gmt.tm_wday],
893             gmt.tm_mday,
894 3           month_names[gmt.tm_mon],
895 3           gmt.tm_year + 1900,
896             gmt.tm_hour, gmt.tm_min, gmt.tm_sec,
897             offset / 60, offset % 60);
898             }
899