Set the tm_isdst flag to -1 before calling mktime(). Otherwise, the current
[oweals/busybox.git] / coreutils / date.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini date implementation for busybox
4  *
5  * by Matthew Grant <grantma@anathoth.gen.nz>
6  * 
7  * iso-format handling added by Robert Griebl <griebl@gmx.de>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
23 */
24
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <sys/time.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <getopt.h>
33 #include "busybox.h"
34
35
36 /* This 'date' command supports only 2 time setting formats, 
37    all the GNU strftime stuff (its in libc, lets use it),
38    setting time using UTC and displaying int, as well as
39    an RFC 822 complient date output for shell scripting
40    mail commands */
41
42 /* Input parsing code is always bulky - used heavy duty libc stuff as
43    much as possible, missed out a lot of bounds checking */
44
45 /* Default input handling to save suprising some people */
46
47 static struct tm *date_conv_time(struct tm *tm_time, const char *t_string)
48 {
49         int nr;
50
51         nr = sscanf(t_string, "%2d%2d%2d%2d%d", &(tm_time->tm_mon),
52                                 &(tm_time->tm_mday), &(tm_time->tm_hour), &(tm_time->tm_min),
53                                 &(tm_time->tm_year));
54
55         if (nr < 4 || nr > 5) {
56                 bb_error_msg_and_die(bb_msg_invalid_date, t_string);
57         }
58
59         /* correct for century  - minor Y2K problem here? */
60         if (tm_time->tm_year >= 1900) {
61                 tm_time->tm_year -= 1900;
62         }
63         /* adjust date */
64         tm_time->tm_mon -= 1;
65
66         return (tm_time);
67
68 }
69
70
71 /* The new stuff for LRP */
72
73 static struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string)
74 {
75         struct tm t;
76
77         /* Parse input and assign appropriately to tm_time */
78
79         if (t =
80                 *tm_time, sscanf(t_string, "%d:%d:%d", &t.tm_hour, &t.tm_min,
81                                                  &t.tm_sec) == 3) {
82                 /* no adjustments needed */
83         } else if (t =
84                            *tm_time, sscanf(t_string, "%d:%d", &t.tm_hour,
85                                                                 &t.tm_min) == 2) {
86                 /* no adjustments needed */
87         } else if (t =
88                            *tm_time, sscanf(t_string, "%d.%d-%d:%d:%d", &t.tm_mon,
89                                                                 &t.tm_mday, &t.tm_hour, &t.tm_min,
90                                                                 &t.tm_sec) == 5) {
91                 /* Adjust dates from 1-12 to 0-11 */
92                 t.tm_mon -= 1;
93         } else if (t =
94                            *tm_time, sscanf(t_string, "%d.%d-%d:%d", &t.tm_mon,
95                                                                 &t.tm_mday, &t.tm_hour, &t.tm_min) == 4) {
96                 /* Adjust dates from 1-12 to 0-11 */
97                 t.tm_mon -= 1;
98         } else if (t =
99                            *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d:%d", &t.tm_year,
100                                                                 &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min,
101                                                                 &t.tm_sec) == 6) {
102                 t.tm_year -= 1900;      /* Adjust years */
103                 t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
104         } else if (t =
105                            *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d", &t.tm_year,
106                                                                 &t.tm_mon, &t.tm_mday, &t.tm_hour,
107                                                                 &t.tm_min) == 5) {
108                 t.tm_year -= 1900;      /* Adjust years */
109                 t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
110         } else {
111                 bb_error_msg_and_die(bb_msg_invalid_date, t_string);
112         }
113         *tm_time = t;
114         return (tm_time);
115 }
116
117
118 int date_main(int argc, char **argv)
119 {
120         char *date_str = NULL;
121         char *date_fmt = NULL;
122         char *t_buff;
123         int set_time;
124         int rfc822;
125         int utc;
126         int use_arg = 0;
127         time_t tm;
128         unsigned long opt;
129         struct tm tm_time;
130
131 #ifdef CONFIG_FEATURE_DATE_ISOFMT
132         int ifmt = 0;
133         char *isofmt_arg;
134
135 # define GETOPT_ISOFMT  "I::"
136 #else
137 # define GETOPT_ISOFMT
138 #endif
139         bb_opt_complementaly = "d~ds:s~ds";
140         opt = bb_getopt_ulflags(argc, argv, "Rs:ud:" GETOPT_ISOFMT,
141                                         &date_str, &date_str
142 #ifdef CONFIG_FEATURE_DATE_ISOFMT
143                                         , &isofmt_arg
144 #endif
145                                         );
146         rfc822 = opt & 1;
147         set_time = opt & 2;
148         utc = opt & 4;
149         if(utc) {
150                         if (putenv("TZ=UTC0") != 0)
151                                 bb_error_msg_and_die(bb_msg_memory_exhausted);
152         }
153         use_arg = opt & 8;
154         if(opt & 0x80000000UL)
155                                 bb_show_usage();
156 #ifdef CONFIG_FEATURE_DATE_ISOFMT
157         if(opt & 16) {
158                 if (!isofmt_arg)
159                                 ifmt = 1;
160                         else {
161                         int ifmt_len = bb_strlen(isofmt_arg);
162
163                                 if ((ifmt_len <= 4)
164                                 && (strncmp(isofmt_arg, "date", ifmt_len) == 0)) {
165                                         ifmt = 1;
166                                 } else if ((ifmt_len <= 5)
167                                            && (strncmp(isofmt_arg, "hours", ifmt_len) == 0)) {
168                                         ifmt = 2;
169                                 } else if ((ifmt_len <= 7)
170                                            && (strncmp(isofmt_arg, "minutes", ifmt_len) == 0)) {
171                                         ifmt = 3;
172                                 } else if ((ifmt_len <= 7)
173                                            && (strncmp(isofmt_arg, "seconds", ifmt_len) == 0)) {
174                                         ifmt = 4;
175                                 }
176                         }
177                 if (!ifmt) {
178                         bb_show_usage();
179                 }
180         }
181 #endif
182
183         if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+')) {
184                 date_fmt = &argv[optind][1];    /* Skip over the '+' */
185         } else if (date_str == NULL) {
186                 set_time = 1;
187                 date_str = argv[optind];
188         }
189
190         /* Now we have parsed all the information except the date format
191            which depends on whether the clock is being set or read */
192
193         time(&tm);
194         memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
195         /* Zero out fields - take her back to midnight! */
196         if (date_str != NULL) {
197                 tm_time.tm_sec = 0;
198                 tm_time.tm_min = 0;
199                 tm_time.tm_hour = 0;
200         }
201
202         /* Process any date input to UNIX time since 1 Jan 1970 */
203         if (date_str != NULL) {
204
205                 if (strchr(date_str, ':') != NULL) {
206                         date_conv_ftime(&tm_time, date_str);
207                 } else {
208                         date_conv_time(&tm_time, date_str);
209                 }
210
211                 /* Correct any day of week and day of year etc. fields */
212                 tm_time.tm_isdst = -1;  /* Be sure to recheck dst. */
213                 tm = mktime(&tm_time);
214                 if (tm < 0) {
215                         bb_error_msg_and_die(bb_msg_invalid_date, date_str);
216                 }
217                 if (utc && (putenv("TZ=UTC0") != 0)) {
218                         bb_error_msg_and_die(bb_msg_memory_exhausted);
219                 }
220
221                 /* if setting time, set it */
222                 if (set_time && (stime(&tm) < 0)) {
223                         bb_perror_msg("cannot set date");
224                 }
225         }
226
227         /* Display output */
228
229         /* Deal with format string */
230         if (date_fmt == NULL) {
231 #ifdef CONFIG_FEATURE_DATE_ISOFMT
232                 switch (ifmt) {
233                 case 4:
234                         date_fmt = utc ? "%Y-%m-%dT%H:%M:%SZ" : "%Y-%m-%dT%H:%M:%S%z";
235                         break;
236                 case 3:
237                         date_fmt = utc ? "%Y-%m-%dT%H:%MZ" : "%Y-%m-%dT%H:%M%z";
238                         break;
239                 case 2:
240                         date_fmt = utc ? "%Y-%m-%dT%HZ" : "%Y-%m-%dT%H%z";
241                         break;
242                 case 1:
243                         date_fmt = "%Y-%m-%d";
244                         break;
245                 case 0:
246                 default:
247 #endif
248                         date_fmt =
249                                 (rfc822
250                                  ? (utc ? "%a, %e %b %Y %H:%M:%S GMT" :
251                                         "%a, %e %b %Y %H:%M:%S %z") : "%a %b %e %H:%M:%S %Z %Y");
252
253 #ifdef CONFIG_FEATURE_DATE_ISOFMT
254                         break;
255                 }
256 #endif
257         } else if (*date_fmt == '\0') {
258                 /* Imitate what GNU 'date' does with NO format string! */
259                 printf("\n");
260                 return EXIT_SUCCESS;
261         }
262
263         /* Handle special conversions */
264
265         if (strncmp(date_fmt, "%f", 2) == 0) {
266                 date_fmt = "%Y.%m.%d-%H:%M:%S";
267         }
268
269         /* Print OUTPUT (after ALL that!) */
270         t_buff = xmalloc(201);
271         strftime(t_buff, 200, date_fmt, &tm_time);
272         puts(t_buff);
273
274         return EXIT_SUCCESS;
275 }