chiark / gitweb /
de1c4d3c56f1cd867e07fc1d0ef72322d73f1ace
[disorder] / lib / xgetdate.c
1 /* Derived from getdate.c in glibc 2.3.6.  This is pretty much
2  * standard getdate() except that you supply the template in an
3  * argument, rather than messing around with environment variables and
4  * files.  */
5
6 /* Convert a string representation of time to a time value.
7    Copyright (C) 1997,1998,1999,2000,2001,2003 Free Software Foundation, Inc.
8    This file is part of the GNU C Library.
9    Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
10
11    The GNU C Library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 2.1 of the License, or (at your option) any later version.
15
16    The GNU C Library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with the GNU C Library; if not, write to the Free
23    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24    02111-1307 USA.  */
25
26 #include <limits.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31
32 #include "dateparse.h"
33
34 #define TM_YEAR_BASE 1900
35
36
37 /* Prototypes for local functions.  */
38 static int first_wday (int year, int mon, int wday);
39 static int check_mday (int year, int mon, int mday);
40
41 # define isleap(year)   \
42   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
43
44 /* Set to one of the following values to indicate an error.
45      1  the DATEMSK environment variable is null or undefined,
46      2  the template file cannot be opened for reading,
47      3  failed to get file status information,
48      4  the template file is not a regular file,
49      5  an error is encountered while reading the template file,
50      6  memory allication failed (not enough memory available),
51      7  there is no line in the template that matches the input,
52      8  invalid input specification Example: February 31 or a time is
53         specified that can not be represented in a time_t (representing
54         the time in seconds since 00:00:00 UTC, January 1, 1970) */
55 int xgetdate_err;
56
57
58 /* Returns the first weekday WDAY of month MON in the year YEAR.  */
59 static int
60 first_wday (int year, int mon, int wday)
61 {
62   struct tm tm;
63
64   if (wday == INT_MIN)
65     return 1;
66
67   memset (&tm, 0, sizeof (struct tm));
68   tm.tm_year = year;
69   tm.tm_mon = mon;
70   tm.tm_mday = 1;
71   mktime (&tm);
72
73   return (1 + (wday - tm.tm_wday + 7) % 7);
74 }
75
76
77 /* Returns 1 if MDAY is a valid day of the month in month MON of year
78    YEAR, and 0 if it is not.  */
79 static int
80 check_mday (int year, int mon, int mday)
81 {
82   switch (mon)
83     {
84     case 0:
85     case 2:
86     case 4:
87     case 6:
88     case 7:
89     case 9:
90     case 11:
91       if (mday >= 1 && mday <= 31)
92         return 1;
93       break;
94     case 3:
95     case 5:
96     case 8:
97     case 10:
98       if (mday >= 1 && mday <= 30)
99         return 1;
100       break;
101     case 1:
102       if (mday >= 1 && mday <= (isleap (year) ? 29 : 28))
103         return 1;
104       break;
105     }
106
107   return 0;
108 }
109
110
111 int
112 xgetdate_r (const char *string, struct tm *tp,
113             const char *const *template)
114 {
115   const char *line;
116   size_t len;
117   char *result = NULL;
118   time_t timer;
119   struct tm tm;
120   int mday_ok = 0;
121
122   line = NULL;
123   len = 0;
124   while((line = *template++))
125     {
126       /* Do the conversion.  */
127       tp->tm_year = tp->tm_mon = tp->tm_mday = tp->tm_wday = INT_MIN;
128       tp->tm_hour = tp->tm_sec = tp->tm_min = INT_MIN;
129       tp->tm_isdst = -1;
130       tp->tm_gmtoff = 0;
131       tp->tm_zone = NULL;
132       result = strptime (string, line, tp);
133       if (result && *result == '\0')
134         break;
135     }
136
137   if (result == NULL || *result != '\0')
138     return 7;
139
140   /* Get current time.  */
141   time (&timer);
142   localtime_r (&timer, &tm);
143
144   /* If only the weekday is given, today is assumed if the given day
145      is equal to the current day and next week if it is less.  */
146   if (tp->tm_wday >= 0 && tp->tm_wday <= 6 && tp->tm_year == INT_MIN
147       && tp->tm_mon == INT_MIN && tp->tm_mday == INT_MIN)
148     {
149       tp->tm_year = tm.tm_year;
150       tp->tm_mon = tm.tm_mon;
151       tp->tm_mday = tm.tm_mday + (tp->tm_wday - tm.tm_wday + 7) % 7;
152       mday_ok = 1;
153     }
154
155   /* If only the month is given, the current month is assumed if the
156      given month is equal to the current month and next year if it is
157      less and no year is given (the first day of month is assumed if
158      no day is given.  */
159   if (tp->tm_mon >= 0 && tp->tm_mon <= 11 && tp->tm_mday == INT_MIN)
160     {
161       if (tp->tm_year == INT_MIN)
162         tp->tm_year = tm.tm_year + (((tp->tm_mon - tm.tm_mon) < 0) ? 1 : 0);
163       tp->tm_mday = first_wday (tp->tm_year, tp->tm_mon, tp->tm_wday);
164       mday_ok = 1;
165     }
166
167   /* If no hour, minute and second are given the current hour, minute
168      and second are assumed.  */
169   if (tp->tm_hour == INT_MIN && tp->tm_min == INT_MIN && tp->tm_sec == INT_MIN)
170     {
171       tp->tm_hour = tm.tm_hour;
172       tp->tm_min = tm.tm_min;
173       tp->tm_sec = tm.tm_sec;
174     }
175
176   /* If no date is given, today is assumed if the given hour is
177      greater than the current hour and tomorrow is assumed if
178      it is less.  */
179   if (tp->tm_hour >= 0 && tp->tm_hour <= 23
180       && tp->tm_year == INT_MIN && tp->tm_mon == INT_MIN
181       && tp->tm_mday == INT_MIN && tp->tm_wday == INT_MIN)
182     {
183       tp->tm_year = tm.tm_year;
184       tp->tm_mon = tm.tm_mon;
185       tp->tm_mday = tm.tm_mday + ((tp->tm_hour - tm.tm_hour) < 0 ? 1 : 0);
186       mday_ok = 1;
187     }
188
189   /* Fill in the gaps.  */
190   if (tp->tm_year == INT_MIN)
191     tp->tm_year = tm.tm_year;
192   if (tp->tm_hour == INT_MIN)
193     tp->tm_hour = 0;
194   if (tp->tm_min == INT_MIN)
195     tp->tm_min = 0;
196   if (tp->tm_sec == INT_MIN)
197     tp->tm_sec = 0;
198
199   /* Check if the day of month is within range, and if the time can be
200      represented in a time_t.  We make use of the fact that the mktime
201      call normalizes the struct tm.  */
202   if ((!mday_ok && !check_mday (TM_YEAR_BASE + tp->tm_year, tp->tm_mon,
203                                 tp->tm_mday))
204       || mktime (tp) == (time_t) -1)
205     return 8;
206
207   return 0;
208 }
209
210
211
212 struct tm *
213   xgetdate (const char *string, const char *const *template)
214 {
215   /* Buffer returned by getdate.  */
216   static struct tm tmbuf;
217   int errval = xgetdate_r (string, &tmbuf, template);
218
219   if (errval != 0)
220     {
221       getdate_err = errval;
222       return NULL;
223     }
224
225   return &tmbuf;
226 }