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