14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*10898Sroland.mainz@nrubsig.org * Copyright (c) 1992-2009 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 78462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * * 204887Schin ***********************************************************************/ 214887Schin #pragma prototyped 224887Schin /* 234887Schin * Glenn Fowler 244887Schin * AT&T Research 254887Schin * 264887Schin * date -- set/display date 274887Schin */ 284887Schin 294887Schin static const char usage[] = 30*10898Sroland.mainz@nrubsig.org "[-?\n@(#)$Id: date (AT&T Research) 2009-03-03 $\n]" 314887Schin USAGE_LICENSE 324887Schin "[+NAME?date - set/list/convert dates]" 334887Schin "[+DESCRIPTION?\bdate\b sets the current date and time (with appropriate" 344887Schin " privilege), lists the current date or file dates, or converts" 354887Schin " dates.]" 364887Schin "[+?Most common \adate\a forms are recognized, including those for" 374887Schin " \bcrontab\b(1), \bls\b(1), \btouch\b(1), and the default" 384887Schin " output from \bdate\b itself.]" 394887Schin "[+?If the \adate\a operand consists of 4, 6, 8, 10 or 12 digits followed" 404887Schin " by an optional \b.\b and two digits then it is interpreted as:" 414887Schin " \aHHMM.SS\a, \addHHMM.SS\a, \ammddHHMM.SS\a, \ammddHHMMyy.SS\a or" 424887Schin " \ayymmddHHMM.SS\a, or \ammddHHMMccyy.SS\a or \accyymmddHHMM.SS\a." 434887Schin " Conflicting standards and practice allow a leading or trailing" 444887Schin " 2 or 4 digit year for the 10 and 12 digit forms; the X/Open trailing" 454887Schin " form is used to disambiguate (\btouch\b(1) uses the leading form.)" 464887Schin " Avoid the 10 digit form to avoid confusion. The digit fields are:]{" 474887Schin " [+cc?Century - 1, 19-20.]" 484887Schin " [+yy?Year in century, 00-99.]" 494887Schin " [+mm?Month, 01-12.]" 504887Schin " [+dd?Day of month, 01-31.]" 514887Schin " [+HH?Hour, 00-23.]" 524887Schin " [+MM?Minute, 00-59.]" 534887Schin " [+SS?Seconds, 00-60.]" 544887Schin "}" 554887Schin "[+?If more than one \adate\a operand is specified then:]{" 564887Schin " [+1.?Each operand sets the reference date for the next" 574887Schin " operand.]" 584887Schin " [+2.?The date is listed for each operand.]" 594887Schin " [+3.?The system date is not set.]" 604887Schin "}" 614887Schin 624887Schin "[a:access-time|atime?List file argument access times.]" 634887Schin "[c:change-time|ctime?List file argument change times.]" 644887Schin "[d:date?Use \adate\a as the current date and do not set the system" 654887Schin " clock.]:[date]" 664887Schin "[e:epoch?Output the date in seconds since the epoch." 674887Schin " Equivalent to \b--format=%s\b.]" 684887Schin "[E:elapsed?Interpret pairs of arguments as start and stop dates, sum the" 694887Schin " differences between all pairs, and list the result as a" 704887Schin " \bfmtelapsed\b(3) elapsed time on the standard output. If there are" 714887Schin " an odd number of arguments then the last time argument is differenced" 724887Schin " with the current time.]" 734887Schin "[f:format?Output the date according to the \bstrftime\b(3) \aformat\a." 744887Schin " For backwards compatibility, a first argument of the form" 754887Schin " \b+\b\aformat\a is equivalent to \b-f\b format." 764887Schin " \aformat\a is in \bprintf\b(3) style, where %\afield\a names" 774887Schin " a fixed size field, zero padded if necessary," 784887Schin " and \\\ac\a and \\\annn\a sequences are as in C. Invalid" 794887Schin " %\afield\a specifications and all other characters are copied" 804887Schin " without change. \afield\a may be preceded by \b%-\b to turn off" 814887Schin " padding or \b%_\b to pad with space, otherwise numeric fields" 824887Schin " are padded with \b0\b and string fields are padded with space." 834887Schin " \afield\a may also be preceded by \bE\b for alternate era" 844887Schin " representation or \bO\b for alternate digit representation (if" 854887Schin " supported by the current locale.) Finally, an integral \awidth\a" 864887Schin " preceding \afield\a truncates the field to \awidth\a characters." 874887Schin " The fields are:]:[format]{" 884887Schin " [+%?% character]" 894887Schin " [+a?abbreviated weekday name]" 904887Schin " [+A?full weekday name]" 914887Schin " [+b?abbreviated month name]" 924887Schin " [+c?\bctime\b(3) style date without the trailing newline]" 934887Schin " [+C?2-digit century]" 944887Schin " [+d?day of month number]" 954887Schin " [+D?date as \amm/dd/yy\a]" 964887Schin " [+e?blank padded day of month number]" 974887Schin " [+E?unpadded day of month number]" 984887Schin " [+f?locale default override date format]" 998462SApril.Chin@Sun.COM " [+F?%ISO 8601:2000 standard date format; equivalent to Y-%m-%d]" 1004887Schin " [+g?\bls\b(1) \b-l\b recent date with \ahh:mm\a]" 1014887Schin " [+G?\bls\b(1) \b-l\b distant date with \ayyyy\a]" 1024887Schin " [+h?abbreviated month name]" 1034887Schin " [+H?24-hour clock hour]" 1044887Schin " [+i?international \bdate\b(1) date with time zone type name]" 1054887Schin " [+I?12-hour clock hour]" 1064887Schin " [+j?1-offset Julian date]" 1074887Schin " [+J?0-offset Julian date]" 1084887Schin " [+k?\bdate\b(1) style date]" 109*10898Sroland.mainz@nrubsig.org " [+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]" 1104887Schin " [+l?\bls\b(1) \b-l\b date; equivalent to \b%Q/%g/%G/\b]" 1118462SApril.Chin@Sun.COM " [+L?locale default date format]" 1124887Schin " [+m?month number]" 1134887Schin " [+M?minutes]" 1144887Schin " [+n?newline character]" 1154887Schin " [+N?nanoseconds 000000000-999999999]" 1164887Schin " [+p?meridian (e.g., \bAM\b or \bPM\b)]" 1174887Schin " [+q?time zone type name (nation code)]" 1184887Schin " [+Q?\a<del>recent<del>distant<del>\a: \a<del>\a is a unique" 1194887Schin " delimter character; \arecent\a format for recent" 1204887Schin " dates, \adistant\a format otherwise]" 1214887Schin " [+r?12-hour time as \ahh:mm:ss meridian\a]" 1224887Schin " [+R?24-hour time as \ahh:mm\a]" 1234887Schin " [+s?number of seconds since the epoch; \a.prec\a preceding" 1244887Schin " \bs\b appends \aprec\a nanosecond digits, \b9\b if" 1254887Schin " \aprec\a is omitted]" 1264887Schin " [+S?seconds 00-60]" 1274887Schin " [+t?tab character]" 1284887Schin " [+T?24-hour time as \ahh:mm:ss\a]" 1294887Schin " [+u?weekday number 1(Monday)-7]" 1304887Schin " [+U?week number with Sunday as the first day]" 1314887Schin " [+V?ISO week number (i18n is \afun\a)]" 1324887Schin " [+w?weekday number 0(Sunday)-6]" 1334887Schin " [+W?week number with Monday as the first day]" 1344887Schin " [+x?locale date style that includes month, day and year]" 1354887Schin " [+X?locale time style that includes hours and minutes]" 1364887Schin " [+y?2-digit year (you'll be sorry)]" 1374887Schin " [+Y?4-digit year]" 1384887Schin " [+z?time zone \aSHHMM\a west of GMT offset where S is" 1394887Schin " \b+\b or \b-\b]" 1404887Schin " [+Z?time zone name]" 1414887Schin " [+=[=]][-+]]flag?set (default or +) or clear (-) \aflag\a" 1424887Schin " for the remainder of \aformat\a, or for the remainder" 1434887Schin " of the process if \b==\b is specified. \aflag\a may be:]{" 1444887Schin " [+l?enable leap second adjustments]" 1454887Schin " [+n?convert \b%S\b as \b%S.%N\b]" 1464887Schin " [+u?UTC time zone]" 1474887Schin " }" 1484887Schin " [+#?equivalent to %s]" 1494887Schin " [+??alternate?use \aalternate\a format if a default format" 1504887Schin " override has not been specified, e.g., \bls\b(1) uses" 1514887Schin " \"%?%l\"; export TM_OPTIONS=\"format='\aoverride\a'\"" 1524887Schin " to override the default]" 1534887Schin "}" 1544887Schin "[i:incremental|adjust?Set the system time in incrementatl adjustments to" 1554887Schin " avoid complete time shift shock. Negative adjustments still maintain" 1564887Schin " monotonic increasing time. Not available on all systems.]" 1574887Schin "[L:last?List only the last time for multiple \adate\a operands.]" 1584887Schin "[l:leap-seconds?Include leap seconds in time calculations. Leap seconds" 1594887Schin " after the ast library release date are not accounted for.]" 1604887Schin "[m:modify-time|mtime?List file argument modify times.]" 1614887Schin "[n!:network?Set network time.]" 1624887Schin "[p:parse?Add \aformat\a to the list of \bstrptime\b(3) parse conversion" 1634887Schin " formats. \aformat\a follows the same conventions as the" 1644887Schin " \b--format\b option, with the addition of these format" 1654887Schin " fields:]:[format]{" 1664887Schin " [+|?If the format failed before this point then restart" 1674887Schin " the parse with the remaining format.]" 1684887Schin " [+&?Call the \btmdate\b(3) heuristic parser. This is" 1694887Schin " is the default when \b--parse\b is omitted.]" 1704887Schin "}" 1714887Schin "[s:show?Show the date without setting the system time.]" 1724887Schin "[u:utc|gmt|zulu?Output dates in \acoordinated universal time\a (UTC).]" 1734887Schin "[U:unelapsed?Interpret each argument as \bfmtelapsed\b(3) elapsed" 1744887Schin " time and list the \bstrelapsed\b(3) 1/\ascale\a seconds.]#[scale]" 1754887Schin "[z:list-zones?List the known time zone table and exit. The table columns" 1764887Schin " are: country code, standard zone name, savings time zone name," 1774887Schin " minutes west of \bUTC\b, and savings time minutes offset. Blank" 1784887Schin " or empty entries are listed as \b-\b.]" 1794887Schin 1804887Schin "\n" 1814887Schin "\n[ +format | date ... | file ... ]\n" 1824887Schin "\n" 1834887Schin 1844887Schin "[+SEE ALSO?\bcrontab\b(1), \bls\b(1), \btouch\b(1), \bfmtelapsed\b(3)," 1854887Schin " \bstrftime\b(3), \bstrptime\b(3), \btm\b(3)]" 1864887Schin ; 1874887Schin 1884887Schin #include <cmd.h> 1894887Schin #include <ls.h> 1904887Schin #include <proc.h> 1914887Schin #include <tmx.h> 1924887Schin #include <times.h> 1934887Schin 1944887Schin typedef struct Fmt 1954887Schin { 1964887Schin struct Fmt* next; 1974887Schin char* format; 1984887Schin } Fmt_t; 1994887Schin 2004887Schin #ifndef ENOSYS 2014887Schin #define ENOSYS EINVAL 2024887Schin #endif 2034887Schin 2044887Schin /* 2054887Schin * set the system clock 2064887Schin * the standards wimped out here 2074887Schin */ 2084887Schin 2094887Schin static int 2108462SApril.Chin@Sun.COM settime(void* context, const char* cmd, Time_t now, int adjust, int network) 2114887Schin { 2124887Schin char* s; 2134887Schin char** argv; 2144887Schin char* args[5]; 215*10898Sroland.mainz@nrubsig.org char buf[1024]; 2164887Schin 2174887Schin if (!adjust && !network) 2184887Schin return tmxsettime(now); 2194887Schin argv = args; 2204887Schin s = "/usr/bin/date"; 2214887Schin if (!streq(cmd, s) && (!eaccess(s, X_OK) || !eaccess(s+=4, X_OK))) 2224887Schin { 2234887Schin *argv++ = s; 2244887Schin if (streq(astconf("UNIVERSE", NiL, NiL), "att")) 2254887Schin { 2268462SApril.Chin@Sun.COM tmxfmt(buf, sizeof(buf), "%m%d%H" "%M%Y.%S", now); 2274887Schin if (adjust) 2284887Schin *argv++ = "-a"; 2294887Schin } 2304887Schin else 2314887Schin { 2328462SApril.Chin@Sun.COM tmxfmt(buf, sizeof(buf), "%Y%m%d%H" "%M.%S", now); 2334887Schin if (network) 2344887Schin *argv++ = "-n"; 2354887Schin if (tm_info.flags & TM_UTC) 2364887Schin *argv++ = "-u"; 2374887Schin } 2384887Schin *argv++ = buf; 2394887Schin *argv = 0; 2408462SApril.Chin@Sun.COM if (!sh_run(context, argv - args, args)) 2414887Schin return 0; 2424887Schin } 2434887Schin return -1; 2444887Schin } 2454887Schin 2464887Schin /* 2474887Schin * convert s to Time_t with error checking 2484887Schin */ 2494887Schin 2504887Schin static Time_t 2514887Schin convert(register Fmt_t* f, char* s, Time_t now) 2524887Schin { 2534887Schin char* t; 2544887Schin char* u; 2554887Schin 2564887Schin do 2574887Schin { 2584887Schin now = tmxscan(s, &t, f->format, &u, now, 0); 2594887Schin if (!*t && (!f->format || !*u)) 2604887Schin break; 2614887Schin } while (f = f->next); 2624887Schin if (!f || *t) 2634887Schin error(3, "%s: invalid date specification", f ? t : s); 2644887Schin return now; 2654887Schin } 2664887Schin 2674887Schin int 2684887Schin b_date(int argc, register char** argv, void* context) 2694887Schin { 2704887Schin register int n; 2714887Schin register char* s; 2724887Schin register Fmt_t* f; 2734887Schin char* t; 2744887Schin unsigned long u; 2754887Schin Time_t now; 2764887Schin Time_t ts; 2774887Schin Time_t te; 2784887Schin Time_t e; 279*10898Sroland.mainz@nrubsig.org char buf[1024]; 2804887Schin Fmt_t* fmts; 2814887Schin Fmt_t fmt; 2824887Schin struct stat st; 2834887Schin 2844887Schin char* cmd = argv[0]; /* original command path */ 2854887Schin char* format = 0; /* tmxfmt() format */ 2864887Schin char* string = 0; /* date string */ 2874887Schin int elapsed = 0; /* args are start/stop pairs */ 2884887Schin int filetime = 0; /* use this st_ time field */ 2894887Schin int increment = 0; /* incrementally adjust time */ 2904887Schin int last = 0; /* display the last time arg */ 2914887Schin Tm_zone_t* listzones = 0; /* known time zone table */ 2924887Schin int network = 0; /* don't set network time */ 2934887Schin int show = 0; /* show date and don't set */ 2944887Schin int unelapsed = 0; /* fmtelapsed() => strelapsed */ 2954887Schin 2964887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0); 2974887Schin setlocale(LC_ALL, ""); 2984887Schin tm_info.flags = TM_DATESTYLE; 2994887Schin fmts = &fmt; 3004887Schin fmt.format = ""; 3014887Schin fmt.next = 0; 3024887Schin for (;;) 3034887Schin { 3044887Schin switch (optget(argv, usage)) 3054887Schin { 3064887Schin case 'a': 3074887Schin case 'c': 3084887Schin case 'm': 3094887Schin filetime = opt_info.option[1]; 3104887Schin continue; 3114887Schin case 'd': 3124887Schin string = opt_info.arg; 3134887Schin show = 1; 3144887Schin continue; 3154887Schin case 'e': 3164887Schin format = "%#"; 3174887Schin continue; 3184887Schin case 'E': 3194887Schin elapsed = 1; 3204887Schin continue; 3214887Schin case 'f': 3224887Schin format = opt_info.arg; 3234887Schin continue; 3244887Schin case 'i': 3254887Schin increment = 1; 3264887Schin continue; 3274887Schin case 'l': 3284887Schin tm_info.flags |= TM_LEAP; 3294887Schin continue; 3304887Schin case 'L': 3314887Schin last = 1; 3324887Schin continue; 3334887Schin case 'n': 3344887Schin network = 1; 3354887Schin continue; 3364887Schin case 'p': 3374887Schin if (!(f = newof(0, Fmt_t, 1, 0))) 3384887Schin error(ERROR_SYSTEM|3, "out of space [format]"); 3394887Schin f->next = fmts; 3404887Schin f->format = opt_info.arg; 3414887Schin fmts = f; 3424887Schin continue; 3434887Schin case 's': 3444887Schin show = 1; 3454887Schin continue; 3464887Schin case 'u': 3474887Schin tm_info.flags |= TM_UTC; 3484887Schin continue; 3494887Schin case 'U': 3504887Schin unelapsed = (int)opt_info.num; 3514887Schin continue; 3524887Schin case 'z': 3534887Schin listzones = tm_data.zone; 3544887Schin continue; 3554887Schin case '?': 3564887Schin error(ERROR_USAGE|4, "%s", opt_info.arg); 3574887Schin continue; 3584887Schin case ':': 3594887Schin error(2, "%s", opt_info.arg); 3604887Schin continue; 3614887Schin } 3624887Schin break; 3634887Schin } 3644887Schin argv += opt_info.index; 3654887Schin if (error_info.errors) 3664887Schin error(ERROR_USAGE|4, "%s", optusage(NiL)); 3674887Schin now = tmxgettime(); 3684887Schin if (listzones) 3694887Schin { 3704887Schin s = "-"; 3714887Schin while (listzones->standard) 3724887Schin { 3734887Schin if (listzones->type) 3744887Schin s = listzones->type; 3754887Schin sfprintf(sfstdout, "%3s %4s %4s %4d %4d\n", s, *listzones->standard ? listzones->standard : "-", listzones->daylight ? listzones->daylight : "-", listzones->west, listzones->dst); 3764887Schin listzones++; 3774887Schin show = 1; 3784887Schin } 3794887Schin } 3804887Schin else if (elapsed) 3814887Schin { 3824887Schin e = 0; 3834887Schin while (s = *argv++) 3844887Schin { 3854887Schin if (!(t = *argv++)) 3864887Schin { 3874887Schin argv--; 3884887Schin t = "now"; 3894887Schin } 3904887Schin ts = convert(fmts, s, now); 3914887Schin te = convert(fmts, t, now); 3924887Schin if (te > ts) 3934887Schin e += te - ts; 3944887Schin else 3954887Schin e += ts - te; 3964887Schin } 3974887Schin sfputr(sfstdout, fmtelapsed((unsigned long)tmxsec(e), 1), '\n'); 3984887Schin show = 1; 3994887Schin } 4004887Schin else if (unelapsed) 4014887Schin { 4024887Schin while (s = *argv++) 4034887Schin { 4044887Schin u = strelapsed(s, &t, unelapsed); 4054887Schin if (*t) 4064887Schin error(3, "%s: invalid elapsed time", s); 4074887Schin sfprintf(sfstdout, "%lu\n", u); 4084887Schin } 4094887Schin show = 1; 4104887Schin } 4114887Schin else if (filetime) 4124887Schin { 4134887Schin if (!*argv) 4144887Schin error(ERROR_USAGE|4, "%s", optusage(NiL)); 4154887Schin n = argv[1] != 0; 4164887Schin while (s = *argv++) 4174887Schin { 4184887Schin if (stat(s, &st)) 4194887Schin error(2, "%s: not found", s); 4204887Schin else 4214887Schin { 4224887Schin switch (filetime) 4234887Schin { 4244887Schin case 'a': 4254887Schin now = tmxgetatime(&st); 4264887Schin break; 4274887Schin case 'c': 4284887Schin now = tmxgetctime(&st); 4294887Schin break; 4304887Schin default: 4314887Schin now = tmxgetmtime(&st); 4324887Schin break; 4334887Schin } 4344887Schin tmxfmt(buf, sizeof(buf), format, now); 4354887Schin if (n) 4364887Schin sfprintf(sfstdout, "%s: %s\n", s, buf); 4374887Schin else 4384887Schin sfprintf(sfstdout, "%s\n", buf); 4394887Schin show = 1; 4404887Schin } 4414887Schin } 4424887Schin } 4434887Schin else 4444887Schin { 4454887Schin if ((s = *argv) && !format && *s == '+') 4464887Schin { 4474887Schin format = s + 1; 4484887Schin argv++; 4494887Schin s = *argv; 4504887Schin } 4514887Schin if (s || (s = string)) 4524887Schin { 4534887Schin if (*argv && string) 4544887Schin error(ERROR_USAGE|4, "%s", optusage(NiL)); 4554887Schin now = convert(fmts, s, now); 4564887Schin if (*argv && (s = *++argv)) 4574887Schin { 4584887Schin show = 1; 4594887Schin do 4604887Schin { 4614887Schin if (!last) 4624887Schin { 4634887Schin tmxfmt(buf, sizeof(buf), format, now); 4644887Schin sfprintf(sfstdout, "%s\n", buf); 4654887Schin } 4664887Schin now = convert(fmts, s, now); 4674887Schin } while (s = *++argv); 4684887Schin } 4694887Schin } 4704887Schin else 4714887Schin show = 1; 4724887Schin if (format || show) 4734887Schin { 4744887Schin tmxfmt(buf, sizeof(buf), format, now); 4754887Schin sfprintf(sfstdout, "%s\n", buf); 4764887Schin } 4778462SApril.Chin@Sun.COM else if (settime(context, cmd, now, increment, network)) 4784887Schin error(ERROR_SYSTEM|3, "cannot set system time"); 4794887Schin } 4804887Schin while (fmts != &fmt) 4814887Schin { 4824887Schin f = fmts; 4834887Schin fmts = fmts->next; 4844887Schin free(f); 4854887Schin } 4864887Schin tm_info.flags = 0; 4874887Schin if (show && sfsync(sfstdout)) 4884887Schin error(ERROR_system(0), "write error"); 4894887Schin return error_info.errors != 0; 4904887Schin } 491