date.c revision 1
1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1992-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Glenn Fowler
1N/A * AT&T Research
1N/A *
1N/A * date -- set/display date
1N/A */
1N/A
1N/Astatic const char usage[] =
1N/A"[-?\n@(#)$Id: date (AT&T Research) 2011-01-27 $\n]"
1N/AUSAGE_LICENSE
1N/A"[+NAME?date - set/list/convert dates]"
1N/A"[+DESCRIPTION?\bdate\b sets the current date and time (with appropriate"
1N/A" privilege), lists the current date or file dates, or converts"
1N/A" dates.]"
1N/A"[+?Most common \adate\a forms are recognized, including those for"
1N/A" \bcrontab\b(1), \bls\b(1), \btouch\b(1), and the default"
1N/A" output from \bdate\b itself.]"
1N/A"[+?If the \adate\a operand consists of 4, 6, 8, 10 or 12 digits followed"
1N/A" by an optional \b.\b and two digits then it is interpreted as:"
1N/A" \aHHMM.SS\a, \addHHMM.SS\a, \ammddHHMM.SS\a, \ammddHHMMyy.SS\a or"
1N/A" \ayymmddHHMM.SS\a, or \ammddHHMMccyy.SS\a or \accyymmddHHMM.SS\a."
1N/A" Conflicting standards and practice allow a leading or trailing"
1N/A" 2 or 4 digit year for the 10 and 12 digit forms; the X/Open trailing"
1N/A" form is used to disambiguate (\btouch\b(1) uses the leading form.)"
1N/A" Avoid the 10 digit form to avoid confusion. The digit fields are:]{"
1N/A" [+cc?Century - 1, 19-20.]"
1N/A" [+yy?Year in century, 00-99.]"
1N/A" [+mm?Month, 01-12.]"
1N/A" [+dd?Day of month, 01-31.]"
1N/A" [+HH?Hour, 00-23.]"
1N/A" [+MM?Minute, 00-59.]"
1N/A" [+SS?Seconds, 00-60.]"
1N/A"}"
1N/A"[+?If more than one \adate\a operand is specified then:]{"
1N/A" [+1.?Each operand sets the reference date for the next"
1N/A" operand.]"
1N/A" [+2.?The date is listed for each operand.]"
1N/A" [+3.?The system date is not set.]"
1N/A"}"
1N/A
1N/A"[a:access-time|atime?List file argument access times.]"
1N/A"[c:change-time|ctime?List file argument change times.]"
1N/A"[d:date?Use \adate\a as the current date and do not set the system"
1N/A" clock.]:[date]"
1N/A"[e:epoch?Output the date in seconds since the epoch."
1N/A" Equivalent to \b--format=%s\b.]"
1N/A"[E:elapsed?Interpret pairs of arguments as start and stop dates, sum the"
1N/A" differences between all pairs, and list the result as a"
1N/A" \bfmtelapsed\b(3) elapsed time on the standard output. If there are"
1N/A" an odd number of arguments then the last time argument is differenced"
1N/A" with the current time.]"
1N/A"[f:format?Output the date according to the \bstrftime\b(3) \aformat\a."
1N/A" For backwards compatibility, a first argument of the form"
1N/A" \b+\b\aformat\a is equivalent to \b-f\b format."
1N/A" \aformat\a is in \bprintf\b(3) style, where %\afield\a names"
1N/A" a fixed size field, zero padded if necessary,"
1N/A" and \\\ac\a and \\\annn\a sequences are as in C. Invalid"
1N/A" %\afield\a specifications and all other characters are copied"
1N/A" without change. \afield\a may be preceded by \b%-\b to turn off"
1N/A" padding or \b%_\b to pad with space, otherwise numeric fields"
1N/A" are padded with \b0\b and string fields are padded with space."
1N/A" \afield\a may also be preceded by \bE\b for alternate era"
1N/A" representation or \bO\b for alternate digit representation (if"
1N/A" supported by the current locale.) Finally, an integral \awidth\a"
1N/A" preceding \afield\a truncates the field to \awidth\a characters."
1N/A" The fields are:]:[format]{"
1N/A" [+%?% character]"
1N/A" [+a?abbreviated weekday name]"
1N/A" [+A?full weekday name]"
1N/A" [+b?abbreviated month name]"
1N/A" [+B?full month name]"
1N/A" [+c?\bctime\b(3) style date without the trailing newline]"
1N/A" [+C?2-digit century]"
1N/A" [+d?day of month number]"
1N/A" [+D?date as \amm/dd/yy\a]"
1N/A" [+e?blank padded day of month number]"
1N/A" [+f?locale default override date format]"
1N/A" [+F?%ISO 8601:2000 standard date format; equivalent to Y-%m-%d]"
1N/A" [+g?\bls\b(1) \b-l\b recent date with \ahh:mm\a]"
1N/A" [+G?\bls\b(1) \b-l\b distant date with \ayyyy\a]"
1N/A" [+h?abbreviated month name]"
1N/A" [+H?24-hour clock hour]"
1N/A" [+i?international \bdate\b(1) date with time zone type name]"
1N/A" [+I?12-hour clock hour]"
1N/A" [+j?1-offset Julian date]"
1N/A" [+J?0-offset Julian date]"
1N/A" [+k?\bdate\b(1) style date]"
1N/A" [+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]"
1N/A" [+l?\bls\b(1) \b-l\b date; equivalent to \b%Q/%g/%G/\b]"
1N/A" [+L?locale default date format]"
1N/A" [+m?month number]"
1N/A" [+M?minutes]"
1N/A" [+n?newline character]"
1N/A" [+N?nanoseconds 000000000-999999999]"
1N/A" [+p?meridian (e.g., \bAM\b or \bPM\b)]"
1N/A" [+q?time zone type name (nation code)]"
1N/A" [+Q?\a<del>recent<del>distant<del>\a: \a<del>\a is a unique"
1N/A" delimter character; \arecent\a format for recent"
1N/A" dates, \adistant\a format otherwise]"
1N/A" [+r?12-hour time as \ahh:mm:ss meridian\a]"
1N/A" [+R?24-hour time as \ahh:mm\a]"
1N/A" [+s?number of seconds since the epoch; \a.prec\a preceding"
1N/A" \bs\b appends \aprec\a nanosecond digits, \b9\b if"
1N/A" \aprec\a is omitted]"
1N/A" [+S?seconds 00-60]"
1N/A" [+t?tab character]"
1N/A" [+T?24-hour time as \ahh:mm:ss\a]"
1N/A" [+u?weekday number 1(Monday)-7]"
1N/A" [+U?week number with Sunday as the first day]"
1N/A" [+V?ISO week number (i18n is \afun\a)]"
1N/A" [+w?weekday number 0(Sunday)-6]"
1N/A" [+W?week number with Monday as the first day]"
1N/A" [+x?locale date style that includes month, day and year]"
1N/A" [+X?locale time style that includes hours and minutes]"
1N/A" [+y?2-digit year (you'll be sorry)]"
1N/A" [+Y?4-digit year]"
1N/A" [+z?time zone \aSHHMM\a west of GMT offset where S is"
1N/A" \b+\b or \b-\b, use pad _ for \aSHH:MM\a]"
1N/A" [+Z?time zone name]"
1N/A" [+=[=]][-+]]flag?set (default or +) or clear (-) \aflag\a"
1N/A" for the remainder of \aformat\a, or for the remainder"
1N/A" of the process if \b==\b is specified. \aflag\a may be:]{"
1N/A" [+l?enable leap second adjustments]"
1N/A" [+n?convert \b%S\b as \b%S.%N\b]"
1N/A" [+u?UTC time zone]"
1N/A" }"
1N/A" [+#?equivalent to %s]"
1N/A" [+??alternate?use \aalternate\a format if a default format"
1N/A" override has not been specified, e.g., \bls\b(1) uses"
1N/A" \"%?%l\"; export TM_OPTIONS=\"format='\aoverride\a'\""
1N/A" to override the default]"
1N/A"}"
1N/A"[i:incremental|adjust?Set the system time in incrementatl adjustments to"
1N/A" avoid complete time shift shock. Negative adjustments still maintain"
1N/A" monotonic increasing time. Not available on all systems.]"
1N/A"[L:last?List only the last time for multiple \adate\a operands.]"
1N/A"[l:leap-seconds?Include leap seconds in time calculations. Leap seconds"
1N/A" after the ast library release date are not accounted for.]"
1N/A"[m:modify-time|mtime?List file argument modify times.]"
1N/A"[n!:network?Set network time.]"
1N/A"[p:parse?Add \aformat\a to the list of \bstrptime\b(3) parse conversion"
1N/A" formats. \aformat\a follows the same conventions as the"
1N/A" \b--format\b option, with the addition of these format"
1N/A" fields:]:[format]{"
1N/A" [+|?If the format failed before this point then restart"
1N/A" the parse with the remaining format.]"
1N/A" [+&?Call the \btmdate\b(3) heuristic parser. This is"
1N/A" is the default when \b--parse\b is omitted.]"
1N/A"}"
1N/A"[R:rfc-2822?List date and time in RFC 2822 format "
1N/A "(%a, %-e %h %Y %H:%M:%S %z).]"
1N/A"[T:rfc-3339?List date and time in RFC 3339 format according to "
1N/A "\atype\a:]:[type]"
1N/A "{"
1N/A "[d:date?(%Y-%m-%d)]"
1N/A "[s:seconds?(%Y-%m-%d %H:%M:%S%_z)]"
1N/A "[n:ns|nanoseconds?(%Y-%m-%d %H:%M:%S.%N%_z)]"
1N/A "}"
1N/A"[s:show?Show the date without setting the system time.]"
1N/A"[u:utc|gmt|zulu|universal?Output dates in \acoordinated universal time\a (UTC).]"
1N/A"[U:unelapsed?Interpret each argument as \bfmtelapsed\b(3) elapsed"
1N/A" time and list the \bstrelapsed\b(3) 1/\ascale\a seconds.]#[scale]"
1N/A"[z:list-zones?List the known time zone table and exit. The table columns"
1N/A" are: country code, standard zone name, savings time zone name,"
1N/A" minutes west of \bUTC\b, and savings time minutes offset. Blank"
1N/A" or empty entries are listed as \b-\b.]"
1N/A
1N/A"\n"
1N/A"\n[ +format | date ... | file ... ]\n"
1N/A"\n"
1N/A
1N/A"[+SEE ALSO?\bcrontab\b(1), \bls\b(1), \btouch\b(1), \bfmtelapsed\b(3),"
1N/A" \bstrftime\b(3), \bstrptime\b(3), \btm\b(3)]"
1N/A;
1N/A
1N/A#include <cmd.h>
1N/A#include <ls.h>
1N/A#include <proc.h>
1N/A#include <tmx.h>
1N/A#include <times.h>
1N/A
1N/Atypedef struct Fmt
1N/A{
1N/A struct Fmt* next;
1N/A char* format;
1N/A} Fmt_t;
1N/A
1N/A#ifndef ENOSYS
1N/A#define ENOSYS EINVAL
1N/A#endif
1N/A
1N/A/*
1N/A * set the system clock
1N/A * the standards wimped out here
1N/A */
1N/A
1N/Astatic int
1N/Asettime(void* context, const char* cmd, Time_t now, int adjust, int network)
1N/A{
1N/A char* s;
1N/A char** argv;
1N/A char* args[5];
1N/A char buf[1024];
1N/A
1N/A if (!adjust && !network)
1N/A return tmxsettime(now);
1N/A argv = args;
1N/A s = "/usr/bin/date";
1N/A if (!streq(cmd, s) && (!eaccess(s, X_OK) || !eaccess(s+=4, X_OK)))
1N/A {
1N/A *argv++ = s;
1N/A if (streq(astconf("UNIVERSE", NiL, NiL), "att"))
1N/A {
1N/A tmxfmt(buf, sizeof(buf), "%m%d%H" "%M%Y.%S", now);
1N/A if (adjust)
1N/A *argv++ = "-a";
1N/A }
1N/A else
1N/A {
1N/A tmxfmt(buf, sizeof(buf), "%Y%m%d%H" "%M.%S", now);
1N/A if (network)
1N/A *argv++ = "-n";
1N/A if (tm_info.flags & TM_UTC)
1N/A *argv++ = "-u";
1N/A }
1N/A *argv++ = buf;
1N/A *argv = 0;
1N/A if (!sh_run(context, argv - args, args))
1N/A return 0;
1N/A }
1N/A return -1;
1N/A}
1N/A
1N/A/*
1N/A * convert s to Time_t with error checking
1N/A */
1N/A
1N/Astatic Time_t
1N/Aconvert(register Fmt_t* f, char* s, Time_t now)
1N/A{
1N/A char* t;
1N/A char* u;
1N/A
1N/A do
1N/A {
1N/A now = tmxscan(s, &t, f->format, &u, now, 0);
1N/A if (!*t && (!f->format || !*u))
1N/A break;
1N/A } while (f = f->next);
1N/A if (!f || *t)
1N/A error(3, "%s: invalid date specification", f ? t : s);
1N/A return now;
1N/A}
1N/A
1N/Aint
1N/Ab_date(int argc, register char** argv, void* context)
1N/A{
1N/A register int n;
1N/A register char* s;
1N/A register Fmt_t* f;
1N/A char* t;
1N/A unsigned long u;
1N/A Time_t now;
1N/A Time_t ts;
1N/A Time_t te;
1N/A Time_t e;
1N/A char buf[1024];
1N/A Fmt_t* fmts;
1N/A Fmt_t fmt;
1N/A struct stat st;
1N/A
1N/A char* cmd = argv[0]; /* original command path */
1N/A char* format = 0; /* tmxfmt() format */
1N/A char* string = 0; /* date string */
1N/A int elapsed = 0; /* args are start/stop pairs */
1N/A int filetime = 0; /* use this st_ time field */
1N/A int increment = 0; /* incrementally adjust time */
1N/A int last = 0; /* display the last time arg */
1N/A Tm_zone_t* listzones = 0; /* known time zone table */
1N/A int network = 0; /* don't set network time */
1N/A int show = 0; /* show date and don't set */
1N/A int unelapsed = 0; /* fmtelapsed() => strelapsed */
1N/A
1N/A cmdinit(argc, argv, context, ERROR_CATALOG, 0);
1N/A tm_info.flags = TM_DATESTYLE;
1N/A fmts = &fmt;
1N/A fmt.format = "";
1N/A fmt.next = 0;
1N/A for (;;)
1N/A {
1N/A switch (optget(argv, usage))
1N/A {
1N/A case 'a':
1N/A case 'c':
1N/A case 'm':
1N/A filetime = opt_info.option[1];
1N/A continue;
1N/A case 'd':
1N/A string = opt_info.arg;
1N/A show = 1;
1N/A continue;
1N/A case 'e':
1N/A format = "%s";
1N/A continue;
1N/A case 'E':
1N/A elapsed = 1;
1N/A continue;
1N/A case 'f':
1N/A format = opt_info.arg;
1N/A continue;
1N/A case 'i':
1N/A increment = 1;
1N/A continue;
1N/A case 'l':
1N/A tm_info.flags |= TM_LEAP;
1N/A continue;
1N/A case 'L':
1N/A last = 1;
1N/A continue;
1N/A case 'n':
1N/A network = 1;
1N/A continue;
1N/A case 'p':
1N/A if (!(f = newof(0, Fmt_t, 1, 0)))
1N/A error(ERROR_SYSTEM|3, "out of space [format]");
1N/A f->next = fmts;
1N/A f->format = opt_info.arg;
1N/A fmts = f;
1N/A continue;
1N/A case 'R':
1N/A format = "%a, %-e %h %Y %H:%M:%S %z";
1N/A continue;
1N/A case 's':
1N/A show = 1;
1N/A continue;
1N/A case 'T':
1N/A switch (opt_info.num)
1N/A {
1N/A case 'd':
1N/A format = "%Y-%m-%d";
1N/A continue;
1N/A case 'n':
1N/A format = "%Y-%m-%d %H:%M:%S.%N%_z";
1N/A continue;
1N/A case 's':
1N/A format = "%Y-%m-%d %H:%M:%S%_z";
1N/A continue;
1N/A }
1N/A continue;
1N/A case 'u':
1N/A tm_info.flags |= TM_UTC;
1N/A continue;
1N/A case 'U':
1N/A unelapsed = (int)opt_info.num;
1N/A continue;
1N/A case 'z':
1N/A listzones = tm_data.zone;
1N/A continue;
1N/A case '?':
1N/A error(ERROR_USAGE|4, "%s", opt_info.arg);
1N/A continue;
1N/A case ':':
1N/A error(2, "%s", opt_info.arg);
1N/A continue;
1N/A }
1N/A break;
1N/A }
1N/A argv += opt_info.index;
1N/A if (error_info.errors)
1N/A error(ERROR_USAGE|4, "%s", optusage(NiL));
1N/A now = tmxgettime();
1N/A if (listzones)
1N/A {
1N/A s = "-";
1N/A while (listzones->standard)
1N/A {
1N/A if (listzones->type)
1N/A s = listzones->type;
1N/A sfprintf(sfstdout, "%3s %4s %4s %4d %4d\n", s, *listzones->standard ? listzones->standard : "-", listzones->daylight ? listzones->daylight : "-", listzones->west, listzones->dst);
1N/A listzones++;
1N/A show = 1;
1N/A }
1N/A }
1N/A else if (elapsed)
1N/A {
1N/A e = 0;
1N/A while (s = *argv++)
1N/A {
1N/A if (!(t = *argv++))
1N/A {
1N/A argv--;
1N/A t = "now";
1N/A }
1N/A ts = convert(fmts, s, now);
1N/A te = convert(fmts, t, now);
1N/A if (te > ts)
1N/A e += te - ts;
1N/A else
1N/A e += ts - te;
1N/A }
1N/A sfputr(sfstdout, fmtelapsed((unsigned long)tmxsec(e), 1), '\n');
1N/A show = 1;
1N/A }
1N/A else if (unelapsed)
1N/A {
1N/A while (s = *argv++)
1N/A {
1N/A u = strelapsed(s, &t, unelapsed);
1N/A if (*t)
1N/A error(3, "%s: invalid elapsed time", s);
1N/A sfprintf(sfstdout, "%lu\n", u);
1N/A }
1N/A show = 1;
1N/A }
1N/A else if (filetime)
1N/A {
1N/A if (!*argv)
1N/A error(ERROR_USAGE|4, "%s", optusage(NiL));
1N/A n = argv[1] != 0;
1N/A while (s = *argv++)
1N/A {
1N/A if (stat(s, &st))
1N/A error(2, "%s: not found", s);
1N/A else
1N/A {
1N/A switch (filetime)
1N/A {
1N/A case 'a':
1N/A now = tmxgetatime(&st);
1N/A break;
1N/A case 'c':
1N/A now = tmxgetctime(&st);
1N/A break;
1N/A default:
1N/A now = tmxgetmtime(&st);
1N/A break;
1N/A }
1N/A tmxfmt(buf, sizeof(buf), format, now);
1N/A if (n)
1N/A sfprintf(sfstdout, "%s: %s\n", s, buf);
1N/A else
1N/A sfprintf(sfstdout, "%s\n", buf);
1N/A show = 1;
1N/A }
1N/A }
1N/A }
1N/A else
1N/A {
1N/A if ((s = *argv) && !format && *s == '+')
1N/A {
1N/A format = s + 1;
1N/A argv++;
1N/A s = *argv;
1N/A }
1N/A if (s || (s = string))
1N/A {
1N/A if (*argv && string)
1N/A error(ERROR_USAGE|4, "%s", optusage(NiL));
1N/A now = convert(fmts, s, now);
1N/A if (*argv && (s = *++argv))
1N/A {
1N/A show = 1;
1N/A do
1N/A {
1N/A if (!last)
1N/A {
1N/A tmxfmt(buf, sizeof(buf), format, now);
1N/A sfprintf(sfstdout, "%s\n", buf);
1N/A }
1N/A now = convert(fmts, s, now);
1N/A } while (s = *++argv);
1N/A }
1N/A }
1N/A else
1N/A show = 1;
1N/A if (format || show)
1N/A {
1N/A tmxfmt(buf, sizeof(buf), format, now);
1N/A sfprintf(sfstdout, "%s\n", buf);
1N/A }
1N/A else if (settime(context, cmd, now, increment, network))
1N/A error(ERROR_SYSTEM|3, "cannot set system time");
1N/A }
1N/A while (fmts != &fmt)
1N/A {
1N/A f = fmts;
1N/A fmts = fmts->next;
1N/A free(f);
1N/A }
1N/A tm_info.flags = 0;
1N/A if (show && sfsync(sfstdout))
1N/A error(ERROR_system(0), "write error");
1N/A return error_info.errors != 0;
1N/A}