1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1992-2010 AT&T Intellectual Property * 5* and is licensed under the * 6* Common Public License, Version 1.0 * 7* by AT&T Intellectual Property * 8* * 9* A copy of the License is available at * 10* http://www.opensource.org/licenses/cpl1.0.txt * 11* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 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) 2009-03-03 $\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" [+c?\bctime\b(3) style date without the trailing newline]" 93" [+C?2-digit century]" 94" [+d?day of month number]" 95" [+D?date as \amm/dd/yy\a]" 96" [+e?blank padded day of month number]" 97" [+E?unpadded 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]" 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"[s:show?Show the date without setting the system time.]" 172"[u:utc|gmt|zulu?Output dates in \acoordinated universal time\a (UTC).]" 173"[U:unelapsed?Interpret each argument as \bfmtelapsed\b(3) elapsed" 174" time and list the \bstrelapsed\b(3) 1/\ascale\a seconds.]#[scale]" 175"[z:list-zones?List the known time zone table and exit. The table columns" 176" are: country code, standard zone name, savings time zone name," 177" minutes west of \bUTC\b, and savings time minutes offset. Blank" 178" or empty entries are listed as \b-\b.]" 179 180"\n" 181"\n[ +format | date ... | file ... ]\n" 182"\n" 183 184"[+SEE ALSO?\bcrontab\b(1), \bls\b(1), \btouch\b(1), \bfmtelapsed\b(3)," 185" \bstrftime\b(3), \bstrptime\b(3), \btm\b(3)]" 186; 187 188#include <cmd.h> 189#include <ls.h> 190#include <proc.h> 191#include <tmx.h> 192#include <times.h> 193 194typedef struct Fmt 195{ 196 struct Fmt* next; 197 char* format; 198} Fmt_t; 199 200#ifndef ENOSYS 201#define ENOSYS EINVAL 202#endif 203 204/* 205 * set the system clock 206 * the standards wimped out here 207 */ 208 209static int 210settime(void* context, const char* cmd, Time_t now, int adjust, int network) 211{ 212 char* s; 213 char** argv; 214 char* args[5]; 215 char buf[1024]; 216 217 if (!adjust && !network) 218 return tmxsettime(now); 219 argv = args; 220 s = "/usr/bin/date"; 221 if (!streq(cmd, s) && (!eaccess(s, X_OK) || !eaccess(s+=4, X_OK))) 222 { 223 *argv++ = s; 224 if (streq(astconf("UNIVERSE", NiL, NiL), "att")) 225 { 226 tmxfmt(buf, sizeof(buf), "%m%d%H" "%M%Y.%S", now); 227 if (adjust) 228 *argv++ = "-a"; 229 } 230 else 231 { 232 tmxfmt(buf, sizeof(buf), "%Y%m%d%H" "%M.%S", now); 233 if (network) 234 *argv++ = "-n"; 235 if (tm_info.flags & TM_UTC) 236 *argv++ = "-u"; 237 } 238 *argv++ = buf; 239 *argv = 0; 240 if (!sh_run(context, argv - args, args)) 241 return 0; 242 } 243 return -1; 244} 245 246/* 247 * convert s to Time_t with error checking 248 */ 249 250static Time_t 251convert(register Fmt_t* f, char* s, Time_t now) 252{ 253 char* t; 254 char* u; 255 256 do 257 { 258 now = tmxscan(s, &t, f->format, &u, now, 0); 259 if (!*t && (!f->format || !*u)) 260 break; 261 } while (f = f->next); 262 if (!f || *t) 263 error(3, "%s: invalid date specification", f ? t : s); 264 return now; 265} 266 267int 268b_date(int argc, register char** argv, void* context) 269{ 270 register int n; 271 register char* s; 272 register Fmt_t* f; 273 char* t; 274 unsigned long u; 275 Time_t now; 276 Time_t ts; 277 Time_t te; 278 Time_t e; 279 char buf[1024]; 280 Fmt_t* fmts; 281 Fmt_t fmt; 282 struct stat st; 283 284 char* cmd = argv[0]; /* original command path */ 285 char* format = 0; /* tmxfmt() format */ 286 char* string = 0; /* date string */ 287 int elapsed = 0; /* args are start/stop pairs */ 288 int filetime = 0; /* use this st_ time field */ 289 int increment = 0; /* incrementally adjust time */ 290 int last = 0; /* display the last time arg */ 291 Tm_zone_t* listzones = 0; /* known time zone table */ 292 int network = 0; /* don't set network time */ 293 int show = 0; /* show date and don't set */ 294 int unelapsed = 0; /* fmtelapsed() => strelapsed */ 295 296 cmdinit(argc, argv, context, ERROR_CATALOG, 0); 297 tm_info.flags = TM_DATESTYLE; 298 fmts = &fmt; 299 fmt.format = ""; 300 fmt.next = 0; 301 for (;;) 302 { 303 switch (optget(argv, usage)) 304 { 305 case 'a': 306 case 'c': 307 case 'm': 308 filetime = opt_info.option[1]; 309 continue; 310 case 'd': 311 string = opt_info.arg; 312 show = 1; 313 continue; 314 case 'e': 315 format = "%#"; 316 continue; 317 case 'E': 318 elapsed = 1; 319 continue; 320 case 'f': 321 format = opt_info.arg; 322 continue; 323 case 'i': 324 increment = 1; 325 continue; 326 case 'l': 327 tm_info.flags |= TM_LEAP; 328 continue; 329 case 'L': 330 last = 1; 331 continue; 332 case 'n': 333 network = 1; 334 continue; 335 case 'p': 336 if (!(f = newof(0, Fmt_t, 1, 0))) 337 error(ERROR_SYSTEM|3, "out of space [format]"); 338 f->next = fmts; 339 f->format = opt_info.arg; 340 fmts = f; 341 continue; 342 case 's': 343 show = 1; 344 continue; 345 case 'u': 346 tm_info.flags |= TM_UTC; 347 continue; 348 case 'U': 349 unelapsed = (int)opt_info.num; 350 continue; 351 case 'z': 352 listzones = tm_data.zone; 353 continue; 354 case '?': 355 error(ERROR_USAGE|4, "%s", opt_info.arg); 356 continue; 357 case ':': 358 error(2, "%s", opt_info.arg); 359 continue; 360 } 361 break; 362 } 363 argv += opt_info.index; 364 if (error_info.errors) 365 error(ERROR_USAGE|4, "%s", optusage(NiL)); 366 now = tmxgettime(); 367 if (listzones) 368 { 369 s = "-"; 370 while (listzones->standard) 371 { 372 if (listzones->type) 373 s = listzones->type; 374 sfprintf(sfstdout, "%3s %4s %4s %4d %4d\n", s, *listzones->standard ? listzones->standard : "-", listzones->daylight ? listzones->daylight : "-", listzones->west, listzones->dst); 375 listzones++; 376 show = 1; 377 } 378 } 379 else if (elapsed) 380 { 381 e = 0; 382 while (s = *argv++) 383 { 384 if (!(t = *argv++)) 385 { 386 argv--; 387 t = "now"; 388 } 389 ts = convert(fmts, s, now); 390 te = convert(fmts, t, now); 391 if (te > ts) 392 e += te - ts; 393 else 394 e += ts - te; 395 } 396 sfputr(sfstdout, fmtelapsed((unsigned long)tmxsec(e), 1), '\n'); 397 show = 1; 398 } 399 else if (unelapsed) 400 { 401 while (s = *argv++) 402 { 403 u = strelapsed(s, &t, unelapsed); 404 if (*t) 405 error(3, "%s: invalid elapsed time", s); 406 sfprintf(sfstdout, "%lu\n", u); 407 } 408 show = 1; 409 } 410 else if (filetime) 411 { 412 if (!*argv) 413 error(ERROR_USAGE|4, "%s", optusage(NiL)); 414 n = argv[1] != 0; 415 while (s = *argv++) 416 { 417 if (stat(s, &st)) 418 error(2, "%s: not found", s); 419 else 420 { 421 switch (filetime) 422 { 423 case 'a': 424 now = tmxgetatime(&st); 425 break; 426 case 'c': 427 now = tmxgetctime(&st); 428 break; 429 default: 430 now = tmxgetmtime(&st); 431 break; 432 } 433 tmxfmt(buf, sizeof(buf), format, now); 434 if (n) 435 sfprintf(sfstdout, "%s: %s\n", s, buf); 436 else 437 sfprintf(sfstdout, "%s\n", buf); 438 show = 1; 439 } 440 } 441 } 442 else 443 { 444 if ((s = *argv) && !format && *s == '+') 445 { 446 format = s + 1; 447 argv++; 448 s = *argv; 449 } 450 if (s || (s = string)) 451 { 452 if (*argv && string) 453 error(ERROR_USAGE|4, "%s", optusage(NiL)); 454 now = convert(fmts, s, now); 455 if (*argv && (s = *++argv)) 456 { 457 show = 1; 458 do 459 { 460 if (!last) 461 { 462 tmxfmt(buf, sizeof(buf), format, now); 463 sfprintf(sfstdout, "%s\n", buf); 464 } 465 now = convert(fmts, s, now); 466 } while (s = *++argv); 467 } 468 } 469 else 470 show = 1; 471 if (format || show) 472 { 473 tmxfmt(buf, sizeof(buf), format, now); 474 sfprintf(sfstdout, "%s\n", buf); 475 } 476 else if (settime(context, cmd, now, increment, network)) 477 error(ERROR_SYSTEM|3, "cannot set system time"); 478 } 479 while (fmts != &fmt) 480 { 481 f = fmts; 482 fmts = fmts->next; 483 free(f); 484 } 485 tm_info.flags = 0; 486 if (show && sfsync(sfstdout)) 487 error(ERROR_system(0), "write error"); 488 return error_info.errors != 0; 489} 490