/* date - Display (or set) the date and time Author: V. Archer */ #include #include #include #include #include #include #include #define MIN 60L /* # seconds in a minute */ #define HOUR (60 * MIN) /* # seconds in an hour */ #define DAY (24 * HOUR) /* # seconds in a day */ #define YEAR (365 * DAY) /* # seconds in a (non-leap) year */ int qflag, uflag, sflag, Sflag; /* Default output file descriptor. */ int outfd = 1; _PROTOTYPE(int main, (int argc, char **argv)); _PROTOTYPE(void putchar, (int c)); _PROTOTYPE(void pstring, (char *s, int len)); _PROTOTYPE(void pldecimal, (unsigned long d, int digits)); _PROTOTYPE(void pdecimal, (int d, int digits)); _PROTOTYPE(void fmtdate, (char *format, time_t t, struct tm *p)); _PROTOTYPE(time_t make_time, (char *t)); _PROTOTYPE(struct tm *september, (time_t *tp)); _PROTOTYPE(void usage, (void)); /* Main module. Handles P1003.2 date and system administrator's date. The * date entered should be given GMT, regardless of the system's TZ! */ int main(argc, argv) int argc; char **argv; { time_t t; struct tm *tm; char *format; char time_buf[40]; int n; int i; time(&t); i = 1; while (i < argc && argv[i][0] == '-') { char *opt = argv[i++] + 1, *end; if (opt[0] == '-' && opt[1] == 0) break; while (*opt != 0) switch (*opt++) { case 'q': qflag = 1; break; case 's': sflag = 1; break; case 'u': uflag = 1; break; case 'S': Sflag = 1; break; case 't': /* (obsolete, now -r) */ case 'r': if (*opt == 0) { if (i == argc) usage(); opt = argv[i++]; } t = strtoul(opt, &end, 10); if (*end != 0) usage(); opt = ""; break; default: usage(); } } if (!qflag && i < argc && ('0' <= argv[i][0] && argv[i][0] <= '9')) { t = make_time(argv[i++]); sflag = 1; } format = "%c"; if (i < argc && argv[i][0] == '+') format = argv[i++] + 1; if (i != argc) usage(); if (qflag) { pstring("\nPlease enter date: MMDDYYhhmmss. Then hit the RETURN key.\n", -1); n = read(0, time_buf, sizeof(time_buf)); if (n > 0 && time_buf[n-1] == '\n') n--; if (n >= 0) time_buf[n] = 0; t = make_time(time_buf); sflag = 1; } if (sflag && stime(&t) != 0) { outfd = 2; pstring("No permission to set time\n", -1); return(1); } tm = Sflag ? september(&t) : uflag ? gmtime(&t) : localtime(&t); fmtdate(format, t, tm); putchar('\n'); return(0); } /* Replacement for stdio putchar(). */ void putchar(c) int c; { static char buf[1024]; static char *bp = buf; if (c != 0) *bp++ = c; if (c == 0 || c == '\n' || bp == buf + sizeof(buf)) { write(outfd, buf, bp - buf); bp = buf; } } /* Internal function that prints a n-digits number. Replaces stdio in our * specific case. */ void pldecimal(d, digits) unsigned long d; int digits; { digits--; if (d > 9 || digits > 0) pldecimal(d / 10, digits); putchar('0' + (d % 10)); } void pdecimal(d, digits) int d, digits; { pldecimal((unsigned long) d, digits); } /* Internal function that prints a fixed-size string. Replaces stdio in our * specific case. */ void pstring(s, len) char *s; int len; { while (*s) if (len--) putchar(*s++); else break; } /* Format the date, using the given locale string. A special case is the * TZ which might be a sign followed by four digits (New format time zone). */ void fmtdate(format, t, p) char *format; time_t t; struct tm *p; { int i; char *s; static char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; static char *month[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; while (*format) if (*format == '%') { switch (*++format) { case 'A': pstring(wday[p->tm_wday], -1); break; case 'B': pstring(month[p->tm_mon], -1); break; case 'D': pdecimal(p->tm_mon + 1, 2); putchar('/'); pdecimal(p->tm_mday, 2); putchar('/'); case 'y': pdecimal(p->tm_year % 100, 2); break; case 'H': pdecimal(p->tm_hour, 2); break; case 'I': i = p->tm_hour % 12; pdecimal(i ? i : 12, 2); break; case 'M': pdecimal(p->tm_min, 2); break; case 'X': case 'T': pdecimal(p->tm_hour, 2); putchar(':'); pdecimal(p->tm_min, 2); putchar(':'); case 'S': pdecimal(p->tm_sec, 2); break; case 'U': pdecimal((p->tm_yday - p->tm_wday + 13) / 7, 2); break; case 'W': if (--(p->tm_wday) < 0) p->tm_wday = 6; pdecimal((p->tm_yday - p->tm_wday + 13) / 7, 2); if (++(p->tm_wday) > 6) p->tm_wday = 0; break; case 'Y': pdecimal(p->tm_year + 1900, 4); break; case 'Z': if (uflag) { s = "GMT"; } else { s = (p->tm_isdst == 1) ? tzname[1] : tzname[0]; } pstring(s, strlen(s)); break; case 'a': pstring(wday[p->tm_wday], 3); break; case 'b': case 'h': pstring(month[p->tm_mon], 3); break; case 'c': if (!(s = getenv("LC_TIME"))) s = "%a %b %e %T %Z %Y"; fmtdate(s, t, p); break; case 'd': pdecimal(p->tm_mday, 2); break; case 'e': if (p->tm_mday < 10) putchar(' '); pdecimal(p->tm_mday, 1); break; case 'j': pdecimal(p->tm_yday + 1, 3); break; case 'm': pdecimal(p->tm_mon + 1, 2); break; case 'n': putchar('\n'); break; case 'p': if (p->tm_hour < 12) putchar('A'); else putchar('P'); putchar('M'); break; case 'r': fmtdate("%I:%M:%S %p", t, p); break; case 's': pldecimal((unsigned long) t, 0); break; case 't': putchar('\t'); break; case 'w': putchar('0' + p->tm_wday); break; case 'x': fmtdate("%B %e %Y", t, p); break; case '%': putchar('%'); break; case '\0': format--; } format++; } else putchar(*format++); } /* Convert a local date string into GMT time in seconds. */ time_t make_time(t) char *t; { struct tm tm; /* user specified time */ time_t now; /* current time */ int leap; /* current year is leap year */ int i; /* general index */ int fld; /* number of fields */ int f[6]; /* time fields */ static int days_per_month[2][12] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }}; /* Get current time just in case */ now = time((time_t *) 0); tm = *localtime(&now); tm.tm_sec = 0; tm.tm_mon++; tm.tm_year %= 100; /* Parse the time */ #if '0'+1 != '1' || '1'+1 != '2' || '2'+1 != '3' || '3'+1 != '4' || \ '4'+1 != '5' || '5'+1 != '6' || '6'+1 != '7' || '7'+1 != '8' || '8'+1 != '9' << Code unsuitable for character collating sequence >> #endif for (fld = 0; fld < sizeof(f)/sizeof(f[0]); fld++) { if (*t == 0) break; f[fld] = 0; for (i = 0; i < 2; i++, t++) { if (*t < '0' || *t > '9') usage(); f[fld] = f[fld] * 10 + *t - '0'; } } switch (fld) { case 2: tm.tm_hour = f[0]; tm.tm_min = f[1]; break; case 3: tm.tm_hour = f[0]; tm.tm_min = f[1]; tm.tm_sec = f[2]; break; case 5: tm.tm_mon = f[0]; tm.tm_mday = f[1]; tm.tm_year = f[2]; tm.tm_hour = f[3]; tm.tm_min = f[4]; break; case 6: tm.tm_mon = f[0]; tm.tm_mday = f[1]; tm.tm_year = f[2]; tm.tm_hour = f[3]; tm.tm_min = f[4]; tm.tm_sec = f[5]; break; default: usage(); } /* Convert the time into seconds since 1 January 1970 */ if (tm.tm_year < 70) tm.tm_year += 100; leap = (tm.tm_year % 4 == 0 && tm.tm_year % 400 != 0); if (tm.tm_mon < 1 || tm.tm_mon > 12 || tm.tm_mday < 1 || tm.tm_mday > days_per_month[leap][tm.tm_mon-1] || tm.tm_hour > 23 || tm.tm_min > 59) { outfd = 2; pstring("Illegal date format\n", -1); exit(1); } /* Convert the time into Minix time - zone independent code */ { time_t utctime; /* guess at unix time */ time_t nextbit; /* next bit to try */ int rv; /* result of try */ struct tm *tmp; /* local time conversion */ #define COMPARE(a,b) ((a) != (b)) ? ((a) - (b)) : utctime = 1; do { nextbit = utctime; utctime = nextbit << 1; } while (utctime >= 1); for (utctime = 0; ; nextbit >>= 1) { utctime |= nextbit; tmp = localtime(&utctime); if (tmp == 0) continue; rv = COMPARE(tmp->tm_year, tm.tm_year) COMPARE(tmp->tm_mon + 1, tm.tm_mon) COMPARE(tmp->tm_mday, tm.tm_mday) COMPARE(tmp->tm_hour, tm.tm_hour) COMPARE(tmp->tm_min, tm.tm_min) COMPARE(tmp->tm_sec, tm.tm_sec) 0; if (rv > 0) utctime &= ~nextbit; else if (rv == 0) break; if (nextbit == 0) { uflag = 1; outfd = 2; pstring("Inexact conversion to UTC from ", -1); fmtdate("%c\n", utctime, localtime(&utctime) ); exit(1); } } return utctime; } } /* Correct the time to the reckoning of Eternal September. */ struct tm *september(tp) time_t *tp; { time_t t; int days; struct tm *tm; tm = localtime(tp); t = *tp - (tm->tm_hour - 12) * 3600L; /* No zone troubles around noon. */ days = 0; while (tm->tm_year > 93 || (tm->tm_year == 93 && tm->tm_mon >= 8)) { /* Step back a year or a month. */ days += tm->tm_year > 93 ? tm->tm_yday+1 : tm->tm_mday; t = *tp - days * (24 * 3600L); tm = localtime(&t); } if (days > 0) { tm = localtime(tp); tm->tm_mday = days; tm->tm_year = 93; tm->tm_mon = 8; #if SANITY t = mktime(tm); tm = localtime(&t); #endif } return tm; } /* (Extended) Posix prototype of date. */ void usage() { outfd = 2; pstring("Usage: date [-qsuS] [-r seconds] [[MMDDYY]hhmm[ss]] [+format]\n", -1); exit(1); }