Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libcmd/date.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1992-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* *
20
***********************************************************************/
21
#pragma prototyped
22
/*
23
* Glenn Fowler
24
* AT&T Research
25
*
26
* date -- set/display date
27
*/
28
29
static const char usage[] =
30
"[-?\n@(#)$Id: date (AT&T Research) 2011-01-27 $\n]"
31
USAGE_LICENSE
32
"[+NAME?date - set/list/convert dates]"
33
"[+DESCRIPTION?\bdate\b sets the current date and time (with appropriate"
34
" privilege), lists the current date or file dates, or converts"
35
" dates.]"
36
"[+?Most common \adate\a forms are recognized, including those for"
37
" \bcrontab\b(1), \bls\b(1), \btouch\b(1), and the default"
38
" output from \bdate\b itself.]"
39
"[+?If the \adate\a operand consists of 4, 6, 8, 10 or 12 digits followed"
40
" by an optional \b.\b and two digits then it is interpreted as:"
41
" \aHHMM.SS\a, \addHHMM.SS\a, \ammddHHMM.SS\a, \ammddHHMMyy.SS\a or"
42
" \ayymmddHHMM.SS\a, or \ammddHHMMccyy.SS\a or \accyymmddHHMM.SS\a."
43
" Conflicting standards and practice allow a leading or trailing"
44
" 2 or 4 digit year for the 10 and 12 digit forms; the X/Open trailing"
45
" form is used to disambiguate (\btouch\b(1) uses the leading form.)"
46
" Avoid the 10 digit form to avoid confusion. The digit fields are:]{"
47
" [+cc?Century - 1, 19-20.]"
48
" [+yy?Year in century, 00-99.]"
49
" [+mm?Month, 01-12.]"
50
" [+dd?Day of month, 01-31.]"
51
" [+HH?Hour, 00-23.]"
52
" [+MM?Minute, 00-59.]"
53
" [+SS?Seconds, 00-60.]"
54
"}"
55
"[+?If more than one \adate\a operand is specified then:]{"
56
" [+1.?Each operand sets the reference date for the next"
57
" operand.]"
58
" [+2.?The date is listed for each operand.]"
59
" [+3.?The system date is not set.]"
60
"}"
61
62
"[a:access-time|atime?List file argument access times.]"
63
"[c:change-time|ctime?List file argument change times.]"
64
"[d:date?Use \adate\a as the current date and do not set the system"
65
" clock.]:[date]"
66
"[e:epoch?Output the date in seconds since the epoch."
67
" Equivalent to \b--format=%s\b.]"
68
"[E:elapsed?Interpret pairs of arguments as start and stop dates, sum the"
69
" differences between all pairs, and list the result as a"
70
" \bfmtelapsed\b(3) elapsed time on the standard output. If there are"
71
" an odd number of arguments then the last time argument is differenced"
72
" with the current time.]"
73
"[f:format?Output the date according to the \bstrftime\b(3) \aformat\a."
74
" For backwards compatibility, a first argument of the form"
75
" \b+\b\aformat\a is equivalent to \b-f\b format."
76
" \aformat\a is in \bprintf\b(3) style, where %\afield\a names"
77
" a fixed size field, zero padded if necessary,"
78
" and \\\ac\a and \\\annn\a sequences are as in C. Invalid"
79
" %\afield\a specifications and all other characters are copied"
80
" without change. \afield\a may be preceded by \b%-\b to turn off"
81
" padding or \b%_\b to pad with space, otherwise numeric fields"
82
" are padded with \b0\b and string fields are padded with space."
83
" \afield\a may also be preceded by \bE\b for alternate era"
84
" representation or \bO\b for alternate digit representation (if"
85
" supported by the current locale.) Finally, an integral \awidth\a"
86
" preceding \afield\a truncates the field to \awidth\a characters."
87
" The fields are:]:[format]{"
88
" [+%?% character]"
89
" [+a?abbreviated weekday name]"
90
" [+A?full weekday name]"
91
" [+b?abbreviated month name]"
92
" [+B?full month name]"
93
" [+c?\bctime\b(3) style date without the trailing newline]"
94
" [+C?2-digit century]"
95
" [+d?day of month number]"
96
" [+D?date as \amm/dd/yy\a]"
97
" [+e?blank padded day of month number]"
98
" [+f?locale default override date format]"
99
" [+F?%ISO 8601:2000 standard date format; equivalent to Y-%m-%d]"
100
" [+g?\bls\b(1) \b-l\b recent date with \ahh:mm\a]"
101
" [+G?\bls\b(1) \b-l\b distant date with \ayyyy\a]"
102
" [+h?abbreviated month name]"
103
" [+H?24-hour clock hour]"
104
" [+i?international \bdate\b(1) date with time zone type name]"
105
" [+I?12-hour clock hour]"
106
" [+j?1-offset Julian date]"
107
" [+J?0-offset Julian date]"
108
" [+k?\bdate\b(1) style date]"
109
" [+K?all numeric date; equivalent to \b%Y-%m-%d+%H:%M:%S\b; \b%_[EO]]K\b for space separator, %OK adds \b.%N\b, \b%EK\b adds \b%.N%z\b, \b%_EK\b adds \b.%N %z\b]"
110
" [+l?\bls\b(1) \b-l\b date; equivalent to \b%Q/%g/%G/\b]"
111
" [+L?locale default date format]"
112
" [+m?month number]"
113
" [+M?minutes]"
114
" [+n?newline character]"
115
" [+N?nanoseconds 000000000-999999999]"
116
" [+p?meridian (e.g., \bAM\b or \bPM\b)]"
117
" [+q?time zone type name (nation code)]"
118
" [+Q?\a<del>recent<del>distant<del>\a: \a<del>\a is a unique"
119
" delimter character; \arecent\a format for recent"
120
" dates, \adistant\a format otherwise]"
121
" [+r?12-hour time as \ahh:mm:ss meridian\a]"
122
" [+R?24-hour time as \ahh:mm\a]"
123
" [+s?number of seconds since the epoch; \a.prec\a preceding"
124
" \bs\b appends \aprec\a nanosecond digits, \b9\b if"
125
" \aprec\a is omitted]"
126
" [+S?seconds 00-60]"
127
" [+t?tab character]"
128
" [+T?24-hour time as \ahh:mm:ss\a]"
129
" [+u?weekday number 1(Monday)-7]"
130
" [+U?week number with Sunday as the first day]"
131
" [+V?ISO week number (i18n is \afun\a)]"
132
" [+w?weekday number 0(Sunday)-6]"
133
" [+W?week number with Monday as the first day]"
134
" [+x?locale date style that includes month, day and year]"
135
" [+X?locale time style that includes hours and minutes]"
136
" [+y?2-digit year (you'll be sorry)]"
137
" [+Y?4-digit year]"
138
" [+z?time zone \aSHHMM\a west of GMT offset where S is"
139
" \b+\b or \b-\b, use pad _ for \aSHH:MM\a]"
140
" [+Z?time zone name]"
141
" [+=[=]][-+]]flag?set (default or +) or clear (-) \aflag\a"
142
" for the remainder of \aformat\a, or for the remainder"
143
" of the process if \b==\b is specified. \aflag\a may be:]{"
144
" [+l?enable leap second adjustments]"
145
" [+n?convert \b%S\b as \b%S.%N\b]"
146
" [+u?UTC time zone]"
147
" }"
148
" [+#?equivalent to %s]"
149
" [+??alternate?use \aalternate\a format if a default format"
150
" override has not been specified, e.g., \bls\b(1) uses"
151
" \"%?%l\"; export TM_OPTIONS=\"format='\aoverride\a'\""
152
" to override the default]"
153
"}"
154
"[i:incremental|adjust?Set the system time in incrementatl adjustments to"
155
" avoid complete time shift shock. Negative adjustments still maintain"
156
" monotonic increasing time. Not available on all systems.]"
157
"[L:last?List only the last time for multiple \adate\a operands.]"
158
"[l:leap-seconds?Include leap seconds in time calculations. Leap seconds"
159
" after the ast library release date are not accounted for.]"
160
"[m:modify-time|mtime?List file argument modify times.]"
161
"[n!:network?Set network time.]"
162
"[p:parse?Add \aformat\a to the list of \bstrptime\b(3) parse conversion"
163
" formats. \aformat\a follows the same conventions as the"
164
" \b--format\b option, with the addition of these format"
165
" fields:]:[format]{"
166
" [+|?If the format failed before this point then restart"
167
" the parse with the remaining format.]"
168
" [+&?Call the \btmdate\b(3) heuristic parser. This is"
169
" is the default when \b--parse\b is omitted.]"
170
"}"
171
"[R:rfc-2822?List date and time in RFC 2822 format "
172
"(%a, %-e %h %Y %H:%M:%S %z).]"
173
"[T:rfc-3339?List date and time in RFC 3339 format according to "
174
"\atype\a:]:[type]"
175
"{"
176
"[d:date?(%Y-%m-%d)]"
177
"[s:seconds?(%Y-%m-%d %H:%M:%S%_z)]"
178
"[n:ns|nanoseconds?(%Y-%m-%d %H:%M:%S.%N%_z)]"
179
"}"
180
"[s:show?Show the date without setting the system time.]"
181
"[u:utc|gmt|zulu|universal?Output dates in \acoordinated universal time\a (UTC).]"
182
"[U:unelapsed?Interpret each argument as \bfmtelapsed\b(3) elapsed"
183
" time and list the \bstrelapsed\b(3) 1/\ascale\a seconds.]#[scale]"
184
"[z:list-zones?List the known time zone table and exit. The table columns"
185
" are: country code, standard zone name, savings time zone name,"
186
" minutes west of \bUTC\b, and savings time minutes offset. Blank"
187
" or empty entries are listed as \b-\b.]"
188
189
"\n"
190
"\n[ +format | date ... | file ... ]\n"
191
"\n"
192
193
"[+SEE ALSO?\bcrontab\b(1), \bls\b(1), \btouch\b(1), \bfmtelapsed\b(3),"
194
" \bstrftime\b(3), \bstrptime\b(3), \btm\b(3)]"
195
;
196
197
#include <cmd.h>
198
#include <ls.h>
199
#include <proc.h>
200
#include <tmx.h>
201
#include <times.h>
202
203
typedef struct Fmt
204
{
205
struct Fmt* next;
206
char* format;
207
} Fmt_t;
208
209
#ifndef ENOSYS
210
#define ENOSYS EINVAL
211
#endif
212
213
/*
214
* set the system clock
215
* the standards wimped out here
216
*/
217
218
static int
219
settime(Shbltin_t* context, const char* cmd, Time_t now, int adjust, int network)
220
{
221
char* s;
222
char** argv;
223
char* args[5];
224
char buf[1024];
225
226
if (!adjust && !network)
227
return tmxsettime(now);
228
argv = args;
229
s = "/usr/bin/date";
230
if (!streq(cmd, s) && (!eaccess(s, X_OK) || !eaccess(s+=4, X_OK)))
231
{
232
*argv++ = s;
233
if (streq(astconf("UNIVERSE", NiL, NiL), "att"))
234
{
235
tmxfmt(buf, sizeof(buf), "%m%d%H" "%M%Y.%S", now);
236
if (adjust)
237
*argv++ = "-a";
238
}
239
else
240
{
241
tmxfmt(buf, sizeof(buf), "%Y%m%d%H" "%M.%S", now);
242
if (network)
243
*argv++ = "-n";
244
if (tm_info.flags & TM_UTC)
245
*argv++ = "-u";
246
}
247
*argv++ = buf;
248
*argv = 0;
249
if (!sh_run(context, argv - args, args))
250
return 0;
251
}
252
return -1;
253
}
254
255
/*
256
* convert s to Time_t with error checking
257
*/
258
259
static Time_t
260
convert(register Fmt_t* f, char* s, Time_t now)
261
{
262
char* t;
263
char* u;
264
265
do
266
{
267
now = tmxscan(s, &t, f->format, &u, now, 0);
268
if (!*t && (!f->format || !*u))
269
break;
270
} while (f = f->next);
271
if (!f || *t)
272
error(3, "%s: invalid date specification", f ? t : s);
273
return now;
274
}
275
276
int
277
b_date(int argc, register char** argv, Shbltin_t* context)
278
{
279
register int n;
280
register char* s;
281
register Fmt_t* f;
282
char* t;
283
unsigned long u;
284
Time_t now;
285
Time_t ts;
286
Time_t te;
287
Time_t e;
288
char buf[1024];
289
Fmt_t* fmts;
290
Fmt_t fmt;
291
struct stat st;
292
293
char* cmd = argv[0]; /* original command path */
294
char* format = 0; /* tmxfmt() format */
295
char* string = 0; /* date string */
296
int elapsed = 0; /* args are start/stop pairs */
297
int filetime = 0; /* use this st_ time field */
298
int increment = 0; /* incrementally adjust time */
299
int last = 0; /* display the last time arg */
300
Tm_zone_t* listzones = 0; /* known time zone table */
301
int network = 0; /* don't set network time */
302
int show = 0; /* show date and don't set */
303
int unelapsed = 0; /* fmtelapsed() => strelapsed */
304
305
cmdinit(argc, argv, context, ERROR_CATALOG, 0);
306
tm_info.flags = TM_DATESTYLE;
307
fmts = &fmt;
308
fmt.format = "";
309
fmt.next = 0;
310
for (;;)
311
{
312
switch (optget(argv, usage))
313
{
314
case 'a':
315
case 'c':
316
case 'm':
317
filetime = opt_info.option[1];
318
continue;
319
case 'd':
320
string = opt_info.arg;
321
show = 1;
322
continue;
323
case 'e':
324
format = "%s";
325
continue;
326
case 'E':
327
elapsed = 1;
328
continue;
329
case 'f':
330
format = opt_info.arg;
331
continue;
332
case 'i':
333
increment = 1;
334
continue;
335
case 'l':
336
tm_info.flags |= TM_LEAP;
337
continue;
338
case 'L':
339
last = 1;
340
continue;
341
case 'n':
342
network = 1;
343
continue;
344
case 'p':
345
if (!(f = newof(0, Fmt_t, 1, 0)))
346
error(ERROR_SYSTEM|3, "out of space [format]");
347
f->next = fmts;
348
f->format = opt_info.arg;
349
fmts = f;
350
continue;
351
case 'R':
352
format = "%a, %-e %h %Y %H:%M:%S %z";
353
continue;
354
case 's':
355
show = 1;
356
continue;
357
case 'T':
358
switch (opt_info.num)
359
{
360
case 'd':
361
format = "%Y-%m-%d";
362
continue;
363
case 'n':
364
format = "%Y-%m-%d %H:%M:%S.%N%_z";
365
continue;
366
case 's':
367
format = "%Y-%m-%d %H:%M:%S%_z";
368
continue;
369
}
370
continue;
371
case 'u':
372
tm_info.flags |= TM_UTC;
373
continue;
374
case 'U':
375
unelapsed = (int)opt_info.num;
376
continue;
377
case 'z':
378
listzones = tm_data.zone;
379
continue;
380
case '?':
381
error(ERROR_USAGE|4, "%s", opt_info.arg);
382
continue;
383
case ':':
384
error(2, "%s", opt_info.arg);
385
continue;
386
}
387
break;
388
}
389
argv += opt_info.index;
390
if (error_info.errors)
391
error(ERROR_USAGE|4, "%s", optusage(NiL));
392
now = tmxgettime();
393
if (listzones)
394
{
395
s = "-";
396
while (listzones->standard)
397
{
398
if (listzones->type)
399
s = listzones->type;
400
sfprintf(sfstdout, "%3s %4s %4s %4d %4d\n", s, *listzones->standard ? listzones->standard : "-", listzones->daylight ? listzones->daylight : "-", listzones->west, listzones->dst);
401
listzones++;
402
show = 1;
403
}
404
}
405
else if (elapsed)
406
{
407
e = 0;
408
while (s = *argv++)
409
{
410
if (!(t = *argv++))
411
{
412
argv--;
413
t = "now";
414
}
415
ts = convert(fmts, s, now);
416
te = convert(fmts, t, now);
417
if (te > ts)
418
e += te - ts;
419
else
420
e += ts - te;
421
}
422
sfputr(sfstdout, fmtelapsed((unsigned long)tmxsec(e), 1), '\n');
423
show = 1;
424
}
425
else if (unelapsed)
426
{
427
while (s = *argv++)
428
{
429
u = strelapsed(s, &t, unelapsed);
430
if (*t)
431
error(3, "%s: invalid elapsed time", s);
432
sfprintf(sfstdout, "%lu\n", u);
433
}
434
show = 1;
435
}
436
else if (filetime)
437
{
438
if (!*argv)
439
error(ERROR_USAGE|4, "%s", optusage(NiL));
440
n = argv[1] != 0;
441
while (s = *argv++)
442
{
443
if (stat(s, &st))
444
error(2, "%s: not found", s);
445
else
446
{
447
switch (filetime)
448
{
449
case 'a':
450
now = tmxgetatime(&st);
451
break;
452
case 'c':
453
now = tmxgetctime(&st);
454
break;
455
default:
456
now = tmxgetmtime(&st);
457
break;
458
}
459
tmxfmt(buf, sizeof(buf), format, now);
460
if (n)
461
sfprintf(sfstdout, "%s: %s\n", s, buf);
462
else
463
sfprintf(sfstdout, "%s\n", buf);
464
show = 1;
465
}
466
}
467
}
468
else
469
{
470
if ((s = *argv) && !format && *s == '+')
471
{
472
format = s + 1;
473
argv++;
474
s = *argv;
475
}
476
if (s || (s = string))
477
{
478
if (*argv && string)
479
error(ERROR_USAGE|4, "%s", optusage(NiL));
480
now = convert(fmts, s, now);
481
if (*argv && (s = *++argv))
482
{
483
show = 1;
484
do
485
{
486
if (!last)
487
{
488
tmxfmt(buf, sizeof(buf), format, now);
489
sfprintf(sfstdout, "%s\n", buf);
490
}
491
now = convert(fmts, s, now);
492
} while (s = *++argv);
493
}
494
}
495
else
496
show = 1;
497
if (format || show)
498
{
499
tmxfmt(buf, sizeof(buf), format, now);
500
sfprintf(sfstdout, "%s\n", buf);
501
}
502
else if (settime(context, cmd, now, increment, network))
503
error(ERROR_SYSTEM|3, "cannot set system time");
504
}
505
while (fmts != &fmt)
506
{
507
f = fmts;
508
fmts = fmts->next;
509
free(f);
510
}
511
tm_info.flags = 0;
512
if (show && sfsync(sfstdout))
513
error(ERROR_system(0), "write error");
514
return error_info.errors != 0;
515
}
516
517