1e43936f8ebb9c7462347ab3a8b192a973b36195
[git.git] / date.c
1 /*
2  * GIT - The information manager from hell
3  *
4  * Copyright (C) Linus Torvalds, 2005
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <time.h>
12
13 static time_t my_mktime(struct tm *tm)
14 {
15         static const int mdays[] = {
16             0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
17         };
18         int year = tm->tm_year - 70;
19         int month = tm->tm_mon;
20         int day = tm->tm_mday;
21
22         if (year < 0 || year > 129) /* algo only works for 1970-2099 */
23                 return -1;
24         if (month < 0 || month > 11) /* array bounds */
25                 return -1;
26         if (month < 2 || (year + 2) % 4)
27                 day--;
28         return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
29                 tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
30 }
31
32 static const char *month_names[] = {
33         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
34         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
35 };
36
37 static const char *weekday_names[] = {
38         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
39 };
40
41
42 static char *skipfws(char *str)
43 {
44         while (isspace(*str))
45                 str++;
46         return str;
47 }
48
49         
50 /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
51    (i.e. English) day/month names, and it doesn't work correctly with %z. */
52 void parse_date(char *date, char *result, int maxlen)
53 {
54         struct tm tm;
55         char *p, *tz;
56         int i, offset;
57         time_t then;
58
59         memset(&tm, 0, sizeof(tm));
60
61         /* Skip day-name */
62         p = skipfws(date);
63         if (!isdigit(*p)) {
64                 for (i=0; i<7; i++) {
65                         if (!strncmp(p,weekday_names[i],3) && p[3] == ',') {
66                                 p = skipfws(p+4);
67                                 goto day;
68                         }
69                 }
70                 return;
71         }                                       
72
73         /* day */
74  day:
75         tm.tm_mday = strtoul(p, &p, 10);
76
77         if (tm.tm_mday < 1 || tm.tm_mday > 31)
78                 return;
79
80         if (!isspace(*p))
81                 return;
82
83         p = skipfws(p);
84
85         /* month */
86
87         for (i=0; i<12; i++) {
88                 if (!strncmp(p, month_names[i], 3) && isspace(p[3])) {
89                         tm.tm_mon = i;
90                         p = skipfws(p+strlen(month_names[i]));
91                         goto year;
92                 }
93         }
94         return; /* Error -- bad month */
95
96         /* year */
97  year:  
98         tm.tm_year = strtoul(p, &p, 10);
99
100         if (!tm.tm_year && !isspace(*p))
101                 return;
102
103         if (tm.tm_year > 1900)
104                 tm.tm_year -= 1900;
105                 
106         p=skipfws(p);
107
108         /* hour */
109         if (!isdigit(*p))
110                 return;
111         tm.tm_hour = strtoul(p, &p, 10);
112         
113         if (tm.tm_hour > 23)
114                 return;
115
116         if (*p != ':')
117                 return; /* Error -- bad time */
118         p++;
119
120         /* minute */
121         if (!isdigit(*p))
122                 return;
123         tm.tm_min = strtoul(p, &p, 10);
124         
125         if (tm.tm_min > 59)
126                 return;
127
128         if (*p != ':')
129                 goto zone;
130         p++;
131
132         /* second */
133         if (!isdigit(*p))
134                 return;
135         tm.tm_sec = strtoul(p, &p, 10);
136         
137         if (tm.tm_sec > 60)
138                 return;
139
140  zone:
141         if (!isspace(*p))
142                 return;
143
144         p = skipfws(p);
145
146         if (*p == '-')
147                 offset = -60;
148         else if (*p == '+')
149                 offset = 60;
150         else
151                return;
152
153         if (!isdigit(p[1]) || !isdigit(p[2]) || !isdigit(p[3]) || !isdigit(p[4]))
154                 return;
155
156         tz = p;
157         i = strtoul(p+1, NULL, 10);
158         offset *= ((i % 100) + ((i / 100) * 60));
159
160         p = skipfws(p + 5);
161         if (*p && *p != '(') /* trailing comment like (EDT) is ok */
162                 return;
163
164         then = my_mktime(&tm); /* mktime uses local timezone */
165         if (then == -1)
166                 return;
167
168         then -= offset;
169
170         snprintf(result, maxlen, "%lu %5.5s", then, tz);
171 }
172
173 void datestamp(char *buf, int bufsize)
174 {
175         time_t now;
176         int offset;
177
178         time(&now);
179
180         offset = my_mktime(localtime(&now)) - now;
181         offset /= 60;
182
183         snprintf(buf, bufsize, "%lu %+05d", now, offset/60*100 + offset%60);
184 }