1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2008 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 29 static const char usage[] = 30 "[-?\n@(#)$Id: date (AT&T Research) 2007-05-21 $\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 " [+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]" 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 194 typedef 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 209 static int 210 settime(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[128]; 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 250 static Time_t 251 convert(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 267 int 268 b_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[128]; 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 setlocale(LC_ALL, ""); 298 tm_info.flags = TM_DATESTYLE; 299 fmts = &fmt; 300 fmt.format = ""; 301 fmt.next = 0; 302 for (;;) 303 { 304 switch (optget(argv, usage)) 305 { 306 case 'a': 307 case 'c': 308 case 'm': 309 filetime = opt_info.option[1]; 310 continue; 311 case 'd': 312 string = opt_info.arg; 313 show = 1; 314 continue; 315 case 'e': 316 format = "%#"; 317 continue; 318 case 'E': 319 elapsed = 1; 320 continue; 321 case 'f': 322 format = opt_info.arg; 323 continue; 324 case 'i': 325 increment = 1; 326 continue; 327 case 'l': 328 tm_info.flags |= TM_LEAP; 329 continue; 330 case 'L': 331 last = 1; 332 continue; 333 case 'n': 334 network = 1; 335 continue; 336 case 'p': 337 if (!(f = newof(0, Fmt_t, 1, 0))) 338 error(ERROR_SYSTEM|3, "out of space [format]"); 339 f->next = fmts; 340 f->format = opt_info.arg; 341 fmts = f; 342 continue; 343 case 's': 344 show = 1; 345 continue; 346 case 'u': 347 tm_info.flags |= TM_UTC; 348 continue; 349 case 'U': 350 unelapsed = (int)opt_info.num; 351 continue; 352 case 'z': 353 listzones = tm_data.zone; 354 continue; 355 case '?': 356 error(ERROR_USAGE|4, "%s", opt_info.arg); 357 continue; 358 case ':': 359 error(2, "%s", opt_info.arg); 360 continue; 361 } 362 break; 363 } 364 argv += opt_info.index; 365 if (error_info.errors) 366 error(ERROR_USAGE|4, "%s", optusage(NiL)); 367 now = tmxgettime(); 368 if (listzones) 369 { 370 s = "-"; 371 while (listzones->standard) 372 { 373 if (listzones->type) 374 s = listzones->type; 375 sfprintf(sfstdout, "%3s %4s %4s %4d %4d\n", s, *listzones->standard ? listzones->standard : "-", listzones->daylight ? listzones->daylight : "-", listzones->west, listzones->dst); 376 listzones++; 377 show = 1; 378 } 379 } 380 else if (elapsed) 381 { 382 e = 0; 383 while (s = *argv++) 384 { 385 if (!(t = *argv++)) 386 { 387 argv--; 388 t = "now"; 389 } 390 ts = convert(fmts, s, now); 391 te = convert(fmts, t, now); 392 if (te > ts) 393 e += te - ts; 394 else 395 e += ts - te; 396 } 397 sfputr(sfstdout, fmtelapsed((unsigned long)tmxsec(e), 1), '\n'); 398 show = 1; 399 } 400 else if (unelapsed) 401 { 402 while (s = *argv++) 403 { 404 u = strelapsed(s, &t, unelapsed); 405 if (*t) 406 error(3, "%s: invalid elapsed time", s); 407 sfprintf(sfstdout, "%lu\n", u); 408 } 409 show = 1; 410 } 411 else if (filetime) 412 { 413 if (!*argv) 414 error(ERROR_USAGE|4, "%s", optusage(NiL)); 415 n = argv[1] != 0; 416 while (s = *argv++) 417 { 418 if (stat(s, &st)) 419 error(2, "%s: not found", s); 420 else 421 { 422 switch (filetime) 423 { 424 case 'a': 425 now = tmxgetatime(&st); 426 break; 427 case 'c': 428 now = tmxgetctime(&st); 429 break; 430 default: 431 now = tmxgetmtime(&st); 432 break; 433 } 434 tmxfmt(buf, sizeof(buf), format, now); 435 if (n) 436 sfprintf(sfstdout, "%s: %s\n", s, buf); 437 else 438 sfprintf(sfstdout, "%s\n", buf); 439 show = 1; 440 } 441 } 442 } 443 else 444 { 445 if ((s = *argv) && !format && *s == '+') 446 { 447 format = s + 1; 448 argv++; 449 s = *argv; 450 } 451 if (s || (s = string)) 452 { 453 if (*argv && string) 454 error(ERROR_USAGE|4, "%s", optusage(NiL)); 455 now = convert(fmts, s, now); 456 if (*argv && (s = *++argv)) 457 { 458 show = 1; 459 do 460 { 461 if (!last) 462 { 463 tmxfmt(buf, sizeof(buf), format, now); 464 sfprintf(sfstdout, "%s\n", buf); 465 } 466 now = convert(fmts, s, now); 467 } while (s = *++argv); 468 } 469 } 470 else 471 show = 1; 472 if (format || show) 473 { 474 tmxfmt(buf, sizeof(buf), format, now); 475 sfprintf(sfstdout, "%s\n", buf); 476 } 477 else if (settime(context, cmd, now, increment, network)) 478 error(ERROR_SYSTEM|3, "cannot set system time"); 479 } 480 while (fmts != &fmt) 481 { 482 f = fmts; 483 fmts = fmts->next; 484 free(f); 485 } 486 tm_info.flags = 0; 487 if (show && sfsync(sfstdout)) 488 error(ERROR_system(0), "write error"); 489 return error_info.errors != 0; 490 } 491