1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 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-03-28 $\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?locale default date format]" 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 " [+m?month number]" 112 " [+M?minutes]" 113 " [+n?newline character]" 114 " [+N?nanoseconds 000000000-999999999]" 115 " [+p?meridian (e.g., \bAM\b or \bPM\b)]" 116 " [+q?time zone type name (nation code)]" 117 " [+Q?\a<del>recent<del>distant<del>\a: \a<del>\a is a unique" 118 " delimter character; \arecent\a format for recent" 119 " dates, \adistant\a format otherwise]" 120 " [+r?12-hour time as \ahh:mm:ss meridian\a]" 121 " [+R?24-hour time as \ahh:mm\a]" 122 " [+s?number of seconds since the epoch; \a.prec\a preceding" 123 " \bs\b appends \aprec\a nanosecond digits, \b9\b if" 124 " \aprec\a is omitted]" 125 " [+S?seconds 00-60]" 126 " [+t?tab character]" 127 " [+T?24-hour time as \ahh:mm:ss\a]" 128 " [+u?weekday number 1(Monday)-7]" 129 " [+U?week number with Sunday as the first day]" 130 " [+V?ISO week number (i18n is \afun\a)]" 131 " [+w?weekday number 0(Sunday)-6]" 132 " [+W?week number with Monday as the first day]" 133 " [+x?locale date style that includes month, day and year]" 134 " [+X?locale time style that includes hours and minutes]" 135 " [+y?2-digit year (you'll be sorry)]" 136 " [+Y?4-digit year]" 137 " [+z?time zone \aSHHMM\a west of GMT offset where S is" 138 " \b+\b or \b-\b]" 139 " [+Z?time zone name]" 140 " [+=[=]][-+]]flag?set (default or +) or clear (-) \aflag\a" 141 " for the remainder of \aformat\a, or for the remainder" 142 " of the process if \b==\b is specified. \aflag\a may be:]{" 143 " [+l?enable leap second adjustments]" 144 " [+n?convert \b%S\b as \b%S.%N\b]" 145 " [+u?UTC time zone]" 146 " }" 147 " [+#?equivalent to %s]" 148 " [+??alternate?use \aalternate\a format if a default format" 149 " override has not been specified, e.g., \bls\b(1) uses" 150 " \"%?%l\"; export TM_OPTIONS=\"format='\aoverride\a'\"" 151 " to override the default]" 152 "}" 153 "[i:incremental|adjust?Set the system time in incrementatl adjustments to" 154 " avoid complete time shift shock. Negative adjustments still maintain" 155 " monotonic increasing time. Not available on all systems.]" 156 "[L:last?List only the last time for multiple \adate\a operands.]" 157 "[l:leap-seconds?Include leap seconds in time calculations. Leap seconds" 158 " after the ast library release date are not accounted for.]" 159 "[m:modify-time|mtime?List file argument modify times.]" 160 "[n!:network?Set network time.]" 161 "[p:parse?Add \aformat\a to the list of \bstrptime\b(3) parse conversion" 162 " formats. \aformat\a follows the same conventions as the" 163 " \b--format\b option, with the addition of these format" 164 " fields:]:[format]{" 165 " [+|?If the format failed before this point then restart" 166 " the parse with the remaining format.]" 167 " [+&?Call the \btmdate\b(3) heuristic parser. This is" 168 " is the default when \b--parse\b is omitted.]" 169 "}" 170 "[s:show?Show the date without setting the system time.]" 171 "[u:utc|gmt|zulu?Output dates in \acoordinated universal time\a (UTC).]" 172 "[U:unelapsed?Interpret each argument as \bfmtelapsed\b(3) elapsed" 173 " time and list the \bstrelapsed\b(3) 1/\ascale\a seconds.]#[scale]" 174 "[z:list-zones?List the known time zone table and exit. The table columns" 175 " are: country code, standard zone name, savings time zone name," 176 " minutes west of \bUTC\b, and savings time minutes offset. Blank" 177 " or empty entries are listed as \b-\b.]" 178 179 "\n" 180 "\n[ +format | date ... | file ... ]\n" 181 "\n" 182 183 "[+SEE ALSO?\bcrontab\b(1), \bls\b(1), \btouch\b(1), \bfmtelapsed\b(3)," 184 " \bstrftime\b(3), \bstrptime\b(3), \btm\b(3)]" 185 ; 186 187 #include <cmd.h> 188 #include <ls.h> 189 #include <proc.h> 190 #include <tmx.h> 191 #include <times.h> 192 193 typedef struct Fmt 194 { 195 struct Fmt* next; 196 char* format; 197 } Fmt_t; 198 199 #ifndef ENOSYS 200 #define ENOSYS EINVAL 201 #endif 202 203 /* 204 * set the system clock 205 * the standards wimped out here 206 */ 207 208 static int 209 settime(const char* cmd, Time_t now, int adjust, int network) 210 { 211 char* s; 212 char** argv; 213 char* args[5]; 214 char buf[128]; 215 216 if (!adjust && !network) 217 return tmxsettime(now); 218 argv = args; 219 s = "/usr/bin/date"; 220 if (!streq(cmd, s) && (!eaccess(s, X_OK) || !eaccess(s+=4, X_OK))) 221 { 222 *argv++ = s; 223 if (streq(astconf("UNIVERSE", NiL, NiL), "att")) 224 { 225 tmxfmt(buf, sizeof(buf), "%m%d%H" "%M" "%Y.%S", now); 226 if (adjust) 227 *argv++ = "-a"; 228 } 229 else 230 { 231 tmxfmt(buf, sizeof(buf), "%Y" "%m%d%H" "%M.%S", now); 232 if (network) 233 *argv++ = "-n"; 234 if (tm_info.flags & TM_UTC) 235 *argv++ = "-u"; 236 } 237 *argv++ = buf; 238 *argv = 0; 239 if (!procrun(s, args)) 240 return 0; 241 } 242 return -1; 243 } 244 245 /* 246 * convert s to Time_t with error checking 247 */ 248 249 static Time_t 250 convert(register Fmt_t* f, char* s, Time_t now) 251 { 252 char* t; 253 char* u; 254 255 do 256 { 257 now = tmxscan(s, &t, f->format, &u, now, 0); 258 if (!*t && (!f->format || !*u)) 259 break; 260 } while (f = f->next); 261 if (!f || *t) 262 error(3, "%s: invalid date specification", f ? t : s); 263 return now; 264 } 265 266 int 267 b_date(int argc, register char** argv, void* context) 268 { 269 register int n; 270 register char* s; 271 register Fmt_t* f; 272 char* t; 273 unsigned long u; 274 Time_t now; 275 Time_t ts; 276 Time_t te; 277 Time_t e; 278 char buf[128]; 279 Fmt_t* fmts; 280 Fmt_t fmt; 281 struct stat st; 282 283 char* cmd = argv[0]; /* original command path */ 284 char* format = 0; /* tmxfmt() format */ 285 char* string = 0; /* date string */ 286 int elapsed = 0; /* args are start/stop pairs */ 287 int filetime = 0; /* use this st_ time field */ 288 int increment = 0; /* incrementally adjust time */ 289 int last = 0; /* display the last time arg */ 290 Tm_zone_t* listzones = 0; /* known time zone table */ 291 int network = 0; /* don't set network time */ 292 int show = 0; /* show date and don't set */ 293 int unelapsed = 0; /* fmtelapsed() => strelapsed */ 294 295 cmdinit(argc, argv, context, ERROR_CATALOG, 0); 296 setlocale(LC_ALL, ""); 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(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