]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | $Header: /cvs/src/tdl/dates.c,v 1.5.2.1 2004/01/07 00:09:05 richard Exp $ | |
3 | ||
4 | tdl - A console program for managing to-do lists | |
5 | Copyright (C) 2001-2004 Richard P. Curnow | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | |
20 | */ | |
21 | ||
22 | /* This file deals with parsing dates and relative time expressions. */ | |
23 | ||
24 | #include "tdl.h" | |
25 | #include <string.h> | |
26 | #include <ctype.h> | |
27 | ||
28 | static time_t tm_to_time_t (struct tm *stm)/*{{{*/ | |
29 | { | |
30 | /* Convert from (struct tm) to time_t. This has to be an inverse of | |
31 | * localtime(). It's not obvious that mktime() does this right, especially | |
32 | * with daylight saving time, on all systems (as I found out writing the RTC | |
33 | * support for chrony!). This function will apply any compensation that is | |
34 | * needed. */ | |
35 | ||
36 | struct tm temp1, temp2; | |
37 | long diff; | |
38 | time_t t1, t2; | |
39 | temp1 = *stm; | |
40 | temp1.tm_isdst = 0; | |
41 | t1 = mktime(&temp1); | |
42 | temp2 = *localtime(&t1); | |
43 | temp2.tm_isdst = 0; | |
44 | t2 = mktime(&temp2); | |
45 | diff = t2 - t1; | |
46 | return t1 - diff; | |
47 | } | |
48 | /*}}}*/ | |
49 | static int parse_time(char *d, int *hour, int *min, int *sec)/*{{{*/ | |
50 | { | |
51 | int n; | |
52 | *hour = *min = *sec = 0; | |
53 | switch (strlen(d)) { | |
54 | case 2: | |
55 | n = sscanf(d, "%2d", hour); | |
56 | if (n != 1) { | |
57 | fprintf(stderr, "Can't parse hour from %s\n", d); | |
58 | return -1; | |
59 | } | |
60 | break; | |
61 | case 4: | |
62 | n = sscanf(d, "%2d%2d", hour, min); | |
63 | if (n != 2) { | |
64 | fprintf(stderr, "Can't parse hour and minute from %s\n", d); | |
65 | return -1; | |
66 | } | |
67 | break; | |
68 | case 6: | |
69 | n = sscanf(d, "%2d%2d%2d", hour, min, sec); | |
70 | if (n != 3) { | |
71 | fprintf(stderr, "Can't parse hour, minute and second from %s\n", d); | |
72 | return -1; | |
73 | } | |
74 | break; | |
75 | default: | |
76 | fprintf(stderr, "Cannot parse time\n"); | |
77 | return -1; | |
78 | break; | |
79 | } | |
80 | ||
81 | return 0; | |
82 | } | |
83 | /*}}}*/ | |
84 | time_t parse_date(char *d, time_t ref, int default_positive, int *error)/*{{{*/ | |
85 | { | |
86 | int len; | |
87 | char *hyphenpos; | |
88 | char *d0; | |
89 | time_t result; | |
90 | ||
91 | *error = 0; /* default : success */ | |
92 | ||
93 | d0 = (*d == '-') ? d+1 : d; | |
94 | hyphenpos = strchr(d0, '-'); | |
95 | ||
96 | if (hyphenpos) *hyphenpos = 0; | |
97 | ||
98 | len = strlen(d); | |
99 | ||
100 | if (!strcmp(d, ".")) { | |
101 | result = ref; | |
102 | ||
103 | } else if (isalpha(d[0]) || | |
104 | (((d[0] == '+')|| (d[0] == '-')) && isalpha(d[1]))) { | |
105 | ||
106 | /* Look for dayname, +dayname, or -dayname */ | |
107 | int dow = -1; | |
108 | int posneg, diff; | |
109 | char *dd = d; | |
110 | struct tm stm; | |
111 | ||
112 | if (*dd == '+') posneg = 1, dd++; | |
113 | else if (*dd == '-') posneg = 0, dd++; | |
114 | else posneg = default_positive; | |
115 | ||
116 | /* This really needs to be internationalized properly. Somebody who | |
117 | * understands how to make locales work properly needs to fix that. */ | |
118 | ||
119 | if (!strncasecmp(dd, "sun", 3)) dow = 0; | |
120 | else if (!strncasecmp(dd, "mon", 3)) dow = 1; | |
121 | else if (!strncasecmp(dd, "tue", 3)) dow = 2; | |
122 | else if (!strncasecmp(dd, "wed", 3)) dow = 3; | |
123 | else if (!strncasecmp(dd, "thu", 3)) dow = 4; | |
124 | else if (!strncasecmp(dd, "fri", 3)) dow = 5; | |
125 | else if (!strncasecmp(dd, "sat", 3)) dow = 6; | |
126 | ||
127 | if (dow < 0) { | |
128 | fprintf(stderr, "Cannot understand day of week\n"); | |
129 | } | |
130 | ||
131 | stm = *localtime(&ref); | |
132 | diff = dow - stm.tm_wday; | |
133 | ||
134 | if (diff == 0) { | |
135 | /* If the day specified is the same day of the week as today, step a | |
136 | * whole week in the required direction. */ | |
137 | if (posneg) result = ref + (7*86400); | |
138 | else result = ref - (7*86400); | |
139 | ||
140 | } else if (diff > 0) { | |
141 | if (posneg) result = ref + (diff * 86400); | |
142 | else result = ref - ((7-diff) * 86400); | |
143 | } else { /* diff < 0 */ | |
144 | if (posneg) result = ref + ((7+diff) * 86400); | |
145 | else result = ref + (diff * 86400); | |
146 | } | |
147 | ||
148 | stm = *localtime(&result); | |
149 | ||
150 | stm.tm_hour = 12; | |
151 | stm.tm_min = 0; | |
152 | stm.tm_sec = 0; | |
153 | result = tm_to_time_t(&stm); | |
154 | ||
155 | } else if ((len > 1) && isalpha(d[len-1])) { | |
156 | /* Relative time */ | |
157 | long interval; | |
158 | int nc; | |
159 | int posneg; | |
160 | ||
161 | if (*d == '+') posneg = 1, d++; | |
162 | else if (*d == '-') posneg = 0, d++; | |
163 | else posneg = default_positive; | |
164 | ||
165 | if (sscanf(d, "%ld%n", &interval, &nc) != 1) { | |
166 | fprintf(stderr, "Cannot recognize interval '%s'\n", d); | |
167 | *error = -1; | |
168 | return (time_t) 0; /* arbitrary */ | |
169 | } | |
170 | ||
171 | d += nc; | |
172 | switch (d[0]) { | |
173 | case 'h': interval *= 3600; break; | |
174 | case 'd': interval *= 86400; break; | |
175 | case 'w': interval *= 7 * 86400; break; | |
176 | case 'm': interval *= 30 * 86400; break; | |
177 | case 'y': interval *= 365 * 86400; break; | |
178 | case 's': break; /* use seconds */ | |
179 | default: | |
180 | fprintf(stderr, "Can't understand interval multiplier '%s'\n", d); | |
181 | *error = -1; | |
182 | return (time_t) 0; /* arbitrary */ | |
183 | } | |
184 | if (!posneg) interval = -interval; | |
185 | ||
186 | result = ref + interval; | |
187 | ||
188 | } else { | |
189 | int year, month, day, n; | |
190 | struct tm stm; | |
191 | ||
192 | stm = *localtime(&ref); | |
193 | ||
194 | /* Try to parse absolute date. | |
195 | * Formats allowed : dd, mmdd, yymmdd, yyyymmdd. | |
196 | * Time is assumed to be noon on the specified day */ | |
197 | switch (len) { | |
198 | case 0: | |
199 | day = stm.tm_mday; | |
200 | month = stm.tm_mon + 1; | |
201 | year = stm.tm_year; | |
202 | break; | |
203 | case 2: | |
204 | n = sscanf(d, "%2d", &day); | |
205 | if (n != 1) { | |
206 | fprintf(stderr, "Can't parse day from %s\n", d); | |
207 | *error = -1; | |
208 | return (time_t) 0; /* arbitrary */ | |
209 | } | |
210 | month = stm.tm_mon + 1; | |
211 | year = stm.tm_year; | |
212 | break; | |
213 | case 4: | |
214 | n = sscanf(d, "%2d%2d", &month, &day); | |
215 | if (n != 2) { | |
216 | fprintf(stderr, "Can't parse month and day from %s\n", d); | |
217 | *error = -1; | |
218 | return (time_t) 0; /* arbitrary */ | |
219 | } | |
220 | year = stm.tm_year; | |
221 | break; | |
222 | case 6: | |
223 | n = sscanf(d, "%2d%2d%2d", &year, &month, &day); | |
224 | if (n != 3) { | |
225 | fprintf(stderr, "Can't parse year, month and day from %s\n", d); | |
226 | *error = -1; | |
227 | return (time_t) 0; /* arbitrary */ | |
228 | } | |
229 | if (year < 70) year += 100; | |
230 | break; | |
231 | case 8: | |
232 | n = sscanf(d, "%4d%2d%2d", &year, &month, &day); | |
233 | if (n != 3) { | |
234 | fprintf(stderr, "Can't parse year, month and day from %s\n", d); | |
235 | *error = -1; | |
236 | return (time_t) 0; /* arbitrary */ | |
237 | } | |
238 | year -= 1900; | |
239 | break; | |
240 | default: | |
241 | fprintf(stderr, "Can't parse date from %s\n", d); | |
242 | *error = -2; | |
243 | return (time_t) 0; /* arbitrary */ | |
244 | break; | |
245 | } | |
246 | stm.tm_year = year; | |
247 | stm.tm_mon = month - 1; | |
248 | stm.tm_mday = day; | |
249 | stm.tm_hour = 12; | |
250 | stm.tm_min = 0; | |
251 | stm.tm_sec = 0; | |
252 | result = tm_to_time_t(&stm); | |
253 | } | |
254 | ||
255 | if (hyphenpos) { | |
256 | int hour, min, sec; | |
257 | struct tm stm; | |
258 | ||
259 | parse_time(hyphenpos+1, &hour, &min, &sec); | |
260 | stm = *localtime(&result); | |
261 | stm.tm_hour = hour; | |
262 | stm.tm_min = min; | |
263 | stm.tm_sec = sec; | |
264 | result = tm_to_time_t(&stm); | |
265 | } | |
266 | ||
267 | return result; | |
268 | ||
269 | }/*}}}*/ | |
270 | ||
271 | /*{{{ Test code*/ | |
272 | #ifdef TEST | |
273 | ||
274 | #include <locale.h> | |
275 | ||
276 | int main (int argc, char **argv) | |
277 | { | |
278 | time_t ref, newtime; | |
279 | char buffer[64]; | |
280 | ||
281 | setlocale(LC_ALL, ""); | |
282 | ||
283 | ref = time(NULL); | |
284 | ||
285 | if (argc < 2) { | |
286 | fprintf(stderr, "Require an argument\n"); | |
287 | return 1; | |
288 | } | |
289 | ||
290 | newtime = parse_date(argv[1], ref, (argc > 2)); | |
291 | ||
292 | strftime(buffer, sizeof(buffer), "%a %A %d %b %B %Y %H:%M:%S", localtime(&newtime)); | |
293 | printf("%s\n", buffer); | |
294 | ||
295 | return 0; | |
296 | ||
297 | } | |
298 | #endif | |
299 | ||
300 | /*}}}*/ | |
301 |