Initial import of tdl-1.6-pre1
[tdl.git] / dates.c
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
This page took 0.032308 seconds and 4 git commands to generate.