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 <gsf@research.att.com> * 18* David Korn <dgk@research.att.com> * 19* * 20***********************************************************************/ 21#pragma prototyped 22/* 23 * Glenn Fowler 24 * AT&T Research 25 * 26 * date -- set/display date 27 */ 28 29static const char usage[] = 30"[-?\n@(#)$Id: date (AT&T Research) 2011-01-27 $\n]" 31USAGE_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 203typedef 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 218static int 219settime(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 259static Time_t 260convert(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 276int 277b_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