Commit 58fcd34a 58fcd34aa0a28b883040d659e769af9e7dda7663 by Sergey Poznyakoff

New version of mu_tm2time function.

The previous version of mu_tm2time incorrectly handled dates within
the DST period.
1 parent db83b070
1 /* GNU mailutils - a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Library Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
17
18 #include <mailutils/mutil.h>
19
20 #define SECS_PER_DAY 86400
21 #define ADJUSTMENT -719162L
22
23 static time_t
24 jan1st (int year)
25 {
26 year--; /* Do not consider the current year */
27 return year*365L
28 + year/4L /* Years divisible by 4 are leap years */
29 + year/400L /* Years divisible by 400 are always leap years */
30 - year/100L; /* Years divisible by 100 but not 400 aren't */
31 }
32
33 static int month_start[]=
34 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
35 /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
36 31 28 31 30 31 30 31 31 30 31 30 31
37 */
38
39 #define leap_year(y) ((y) % 4 == 0 && (y) % 100 != 0 || (y) % 400 == 0)
40
41 static int
42 dayofyear (time_t *pday, int year, int month, int day)
43 {
44 int leap, month_days;
45
46 if (year < 0 || month < 0 || month > 11)
47 return -1;
48
49 leap = leap_year (year);
50
51 month_days = month_start[month + 1] - month_start[month]
52 + ((month == 2) ? leap : 0);
53
54 if (day < 0 || day > month_days)
55 return -1; /* Illegal Date */
56
57 if (month <= 2)
58 leap = 0;
59
60 *pday = month_start[month] + day + leap;
61 return 0;
62 }
63
64
65 /* Convert struct tm into time_t, taking into account timezone offset. */
66 /* FIXME: It does not take DST into account */
67 time_t
68 mu_tm2time (struct tm *tm, mu_timezone *tz)
69 {
70 time_t t;
71
72 if (dayofyear (&t, tm->tm_year, tm->tm_mon, tm->tm_mday - 1))
73 return -1;
74 t = (t + ADJUSTMENT + jan1st (1900 + tm->tm_year)) * SECS_PER_DAY
75 + (tm->tm_hour * 60 + tm->tm_min) * 60 + tm->tm_sec
76 - tz->utc_offset;
77 return t;
78 }
79
80 /* Convert time 0 at UTC to our localtime, that tells us the offset
81 of our current timezone from UTC. */
82 time_t
83 mu_utc_offset (void)
84 {
85 time_t t = 0;
86 struct tm *tm = gmtime (&t);
87
88 return - mktime (tm);
89 }
90
91 static const char *months[] =
92 {
93 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
94 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
95 };
96
97 static const char *wdays[] =
98 {
99 "Sun", "Mon", "Teu", "Wed", "Thr", "Fri", "Sat", NULL
100 };
101
102 int
103 mu_parse_imap_date_time (const char **p, struct tm *tm, mu_timezone *tz)
104 {
105 int year, mon, day, hour, min, sec;
106 char zone[6] = "+0000"; /* ( "+" / "-" ) hhmm */
107 char month[5] = "";
108 int hh = 0;
109 int mm = 0;
110 int sign = 1;
111 int scanned = 0, scanned3;
112 int i;
113 int tzoffset;
114
115 day = mon = year = hour = min = sec = 0;
116
117 memset (tm, 0, sizeof (*tm));
118
119 switch (sscanf (*p,
120 "%2d-%3s-%4d%n %2d:%2d:%2d %5s%n",
121 &day, month, &year, &scanned3, &hour, &min, &sec, zone,
122 &scanned))
123 {
124 case 3:
125 scanned = scanned3;
126 break;
127 case 7:
128 break;
129 default:
130 return -1;
131 }
132
133 tm->tm_sec = sec;
134 tm->tm_min = min;
135 tm->tm_hour = hour;
136 tm->tm_mday = day;
137
138 for (i = 0; i < 12; i++)
139 {
140 if (strncasecmp (month, months[i], 3) == 0)
141 {
142 mon = i;
143 break;
144 }
145 }
146 tm->tm_mon = mon;
147 tm->tm_year = (year > 1900) ? year - 1900 : year;
148 tm->tm_yday = 0; /* unknown. */
149 tm->tm_wday = 0; /* unknown. */
150 #if HAVE_STRUCT_TM_TM_ISDST
151 tm->tm_isdst = -1; /* unknown. */
152 #endif
153
154 hh = (zone[1] - '0') * 10 + (zone[2] - '0');
155 mm = (zone[3] - '0') * 10 + (zone[4] - '0');
156 sign = (zone[0] == '-') ? -1 : +1;
157 tzoffset = sign * (hh * 60 * 60 + mm * 60);
158
159 #if HAVE_STRUCT_TM_TM_GMTOFF
160 tm->tm_gmtoff = tzoffset;
161 #endif
162
163 if (tz)
164 {
165 tz->utc_offset = tzoffset;
166 tz->tz_name = NULL;
167 }
168
169 *p += scanned;
170
171 return 0;
172 }
173
174 /* "ctime" format is: Thu Jul 01 15:58:27 1999, with no trailing \n. */
175 int
176 mu_parse_ctime_date_time (const char **p, struct tm *tm, mu_timezone * tz)
177 {
178 int wday = 0;
179 int year = 0;
180 int mon = 0;
181 int day = 0;
182 int hour = 0;
183 int min = 0;
184 int sec = 0;
185 int n = 0;
186 int i;
187 char weekday[5] = "";
188 char month[5] = "";
189
190 if (sscanf (*p, "%3s %3s %2d %2d:%2d:%2d %d%n\n",
191 weekday, month, &day, &hour, &min, &sec, &year, &n) != 7)
192 return -1;
193
194 *p += n;
195
196 for (i = 0; i < 7; i++)
197 {
198 if (strncasecmp (weekday, wdays[i], 3) == 0)
199 {
200 wday = i;
201 break;
202 }
203 }
204
205 for (i = 0; i < 12; i++)
206 {
207 if (strncasecmp (month, months[i], 3) == 0)
208 {
209 mon = i;
210 break;
211 }
212 }
213
214 if (tm)
215 {
216 memset (tm, 0, sizeof (struct tm));
217
218 tm->tm_sec = sec;
219 tm->tm_min = min;
220 tm->tm_hour = hour;
221 tm->tm_mday = day;
222 tm->tm_wday = wday;
223 tm->tm_mon = mon;
224 tm->tm_year = (year > 1900) ? year - 1900 : year;
225 #ifdef HAVE_STRUCT_TM_TM_ISDST
226 tm->tm_isdst = -1; /* unknown. */
227 #endif
228 }
229
230 /* ctime has no timezone information, set tz to UTC if they ask. */
231 if (tz)
232 memset (tz, 0, sizeof (struct mu_timezone));
233
234 return 0;
235 }