2df9e0cc7a63661d385b66d39a3b14d6ac7adf62
[oweals/busybox.git] / coreutils / date.c
1 /*
2  * Mini date implementation for busybox
3  *
4  * Copyright (C) 1999 by Erik Andersen <andersee@debian.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20 */
21
22 #include "internal.h"
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <sys/time.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <stdio.h>
29
30
31 /* This 'date' command supports only 2 time setting formats, 
32    all the GNU strftime stuff (its in libc, lets use it),
33    setting time using UTC and displaying int, as well as
34    an RFC 822 complient date output for shell scripting
35    mail commands */
36
37 const char      date_usage[] = "Usage: date [OPTION]... [+FORMAT]\n"
38 "  or:  date [OPTION] [MMDDhhmm[[CC]YY][.ss]]\n"
39 "Display the current time in the given FORMAT, or set the system date.\n"
40 "\nOptions:\n\t-R\t\toutput RFC-822 compliant date string\n"
41 "\t-s\t\tset time described by STRING\n"
42 "\t-u\t\tprint or set Coordinated Universal Time\n";
43
44
45 /* Input parsing code is always bulky - used heavy duty libc stuff as
46    much as possible, missed out a lot of bounds checking */
47
48 /* Default input handling to save suprising some people */
49
50 struct tm *
51 date_conv_time(struct tm *tm_time, const char *t_string) {
52   int nr;
53
54   nr = sscanf(t_string, "%2d%2d%2d%2d%d", 
55              &(tm_time->tm_mon),
56              &(tm_time->tm_mday),
57              &(tm_time->tm_hour),
58              &(tm_time->tm_min),
59              &(tm_time->tm_year));
60
61   if(nr < 4 || nr > 5) {
62     fprintf(stderr, "date: invalid date `%s'\n", t_string);
63     exit( FALSE);
64   }
65
66   /* correct for century  - minor Y2K problem here? */
67   if(tm_time->tm_year >= 1900)
68     tm_time->tm_year -= 1900;
69   /* adjust date */
70   tm_time->tm_mon -= 1;
71              
72   return(tm_time);
73
74 }
75
76
77 /* The new stuff for LRP */
78
79 struct tm *
80 date_conv_ftime(struct tm *tm_time, const char *t_string) {
81   struct tm itm_time, jtm_time, ktm_time, \
82     ltm_time, mtm_time, ntm_time;
83   
84   itm_time = *tm_time;
85   jtm_time = *tm_time;
86   ktm_time = *tm_time;
87   ltm_time = *tm_time;
88   mtm_time = *tm_time;
89   ntm_time = *tm_time;
90
91   /* Parse input and assign appropriately to tm_time */
92   
93   if(sscanf(t_string, "%d:%d:%d",
94             &itm_time.tm_hour, 
95             &itm_time.tm_min, 
96             &itm_time.tm_sec) == 3 ) {
97
98     *tm_time = itm_time;
99     return(tm_time);
100
101   } else if (sscanf(t_string, "%d:%d", 
102                     &jtm_time.tm_hour, 
103                     &jtm_time.tm_min) == 2) {
104
105     *tm_time = jtm_time;
106     return(tm_time);
107
108   } else if (sscanf(t_string, "%d.%d-%d:%d:%d",
109                     &ktm_time.tm_mon, 
110                     &ktm_time.tm_mday, 
111                     &ktm_time.tm_hour,
112                     &ktm_time.tm_min,
113                     &ktm_time.tm_sec) == 5) {
114
115     ktm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
116     *tm_time = ktm_time;
117     return(tm_time);
118
119   } else if (sscanf(t_string, "%d.%d-%d:%d",
120                     &ltm_time.tm_mon, 
121                     &ltm_time.tm_mday, 
122                     &ltm_time.tm_hour,
123                     &ltm_time.tm_min) == 4) {
124
125     ltm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
126     *tm_time = ltm_time;
127     return(tm_time);
128
129   } else if (sscanf(t_string, "%d.%d.%d-%d:%d:%d",
130                     &mtm_time.tm_year,
131                     &mtm_time.tm_mon, 
132                     &mtm_time.tm_mday, 
133                     &mtm_time.tm_hour,
134                     &mtm_time.tm_min,
135                     &mtm_time.tm_sec) == 6) {
136
137     mtm_time.tm_year -= 1900; /* Adjust years */
138     mtm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
139     *tm_time = mtm_time;
140     return(tm_time);
141
142   } else if (sscanf(t_string, "%d.%d.%d-%d:%d",
143                     &ntm_time.tm_year,
144                     &ntm_time.tm_mon, 
145                     &ntm_time.tm_mday, 
146                     &ntm_time.tm_hour,
147                     &ntm_time.tm_min) == 5) { 
148     ntm_time.tm_year -= 1900; /* Adjust years */
149     ntm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
150     *tm_time = ntm_time;
151     return(tm_time);
152
153   }
154
155   fprintf(stderr, "date: invalid date `%s'\n", t_string);
156
157   exit( FALSE);
158
159 }
160
161
162 void
163 date_err(void) {
164     fprintf (stderr, "%s\n", date_usage);
165     exit( FALSE);
166 }
167
168 int
169 date_main(int argc, char * * argv)
170 {
171   char *date_str = NULL;
172   char *date_fmt = NULL;
173   char *t_buff;
174   int i;
175   int set_time = 0;
176   int rfc822 = 0;
177   int utc = 0;
178   int use_arg = 0;
179   time_t tm; 
180   struct tm tm_time;
181   
182   /* Interpret command line args */
183     i = --argc;
184     argv++;
185     while (i > 0 && **argv) {
186         if (**argv == '-') {
187             while (i>0 && *++(*argv)) switch (**argv) {
188             case 'R':
189                 rfc822 = 1;
190                 break;
191             case 's':
192                 set_time = 1;
193                 if(date_str != NULL) date_err();
194                 date_str = optarg;
195                 break;
196             case 'u':
197                 utc = 1;
198                 if (putenv ("TZ=UTC0") != 0) {
199                     fprintf(stderr,"date: memory exhausted\n");
200                     exit( FALSE);
201                 }
202                 /* Look ma, no break.  Don't fix it either. */
203             case 'd':
204                 use_arg = 1;
205                 if(date_str != NULL) date_err();
206                 date_str = optarg;
207                 break;
208             case '-':
209                 date_err();
210             }
211         } else {
212             if ( (date_fmt == NULL) && (strcmp(*argv, "+")==0) )
213                 date_fmt=*argv;
214             else if (date_str == NULL) {
215                 set_time = 1;
216                 date_str=*argv;
217             } else { 
218                 date_err();
219             }
220         }
221         i--;
222         argv++;
223     }
224
225
226   /* Now we have parsed all the information except the date format
227    which depends on whether the clock is being set or read */
228
229   time(&tm);
230   memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
231   /* Zero out fields - take her back to midnight!*/
232   if(date_str != NULL) {
233     tm_time.tm_sec = 0;
234     tm_time.tm_min = 0;
235     tm_time.tm_hour = 0;
236   }
237
238   /* Process any date input to UNIX time since 1 Jan 1970 */
239   if(date_str != NULL) {
240
241     if(strchr(date_str, ':') != NULL) {
242       date_conv_ftime(&tm_time, date_str);
243     } else {
244       date_conv_time(&tm_time, date_str);
245     }
246
247     /* Correct any day of week and day of year etc fields */
248     tm = mktime(&tm_time);
249     if (tm < 0 ) {
250       fprintf(stderr, "date: invalid date `%s'\n", date_str);
251       exit( FALSE);
252     }
253
254     /* if setting time, set it */
255     if(set_time) {
256       if( stime(&tm) < 0) {
257         fprintf(stderr, "date: can't set date.\n");
258         exit( FALSE);
259       }
260     }
261   }
262   
263   /* Display output */
264
265   /* Deal with format string */
266   if(date_fmt == NULL) {
267     date_fmt = (rfc822
268                 ? (utc
269                    ? "%a, %_d %b %Y %H:%M:%S GMT"
270                    : "%a, %_d %b %Y %H:%M:%S %z")
271                 : "%a %b %e %H:%M:%S %Z %Y");
272
273   } else if ( *date_fmt == '\0' ) {
274     /* Imitate what GNU 'date' does with NO format string! */
275     printf ("\n");
276     exit( TRUE);
277   }
278
279   /* Handle special conversions */
280
281   if( strncmp( date_fmt, "%f", 2) == 0 ) {
282     date_fmt = "%Y.%m.%d-%H:%M:%S";
283   }
284
285   /* Print OUTPUT (after ALL that!) */
286   t_buff = malloc(201);
287   strftime(t_buff, 200, date_fmt, &tm_time);
288   printf("%s\n", t_buff);
289
290   exit( TRUE);
291
292 }
293