ref: febe84af755d8b6941c6e59c1b350f889c4fccb0
parent: 8b3efcfc4e3b38eab3f0ff503e573c072ff890f5
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Jun 14 20:12:57 EDT 2020
libc: revert date change again. this is getting ridicuoulus. this breaks the sample from the seconds manpage, and overall produces funky results. this needs alot more testing. term% seconds '23 may 2011' seconds: tmparse: invalid date 23 may 2011 near 'may 2011' term% seconds '2019-01-01 00:00:00' -118370073600
--- a/sys/include/libc.h
+++ b/sys/include/libc.h
@@ -314,44 +314,21 @@
/*
* Time-of-day
*/
-typedef struct Tzone Tzone;
-#pragma incomplete Tzone
-
typedef
struct Tm
{
- vlong abs; /* seconds since Jan 1 1970, GMT */
- int nsec; /* nseconds (range 0...1e9) */
- int sec; /* seconds (range 0..60) */
- int min; /* minutes (0..59) */
- int hour; /* hours (0..23) */
- int mday; /* day of the month (1..31) */
- int mon; /* month of the year (0..11) */
- int year; /* year A.D. */
- int wday; /* day of week (0..6, Sunday = 0) */
- int yday; /* day of year (0..365) */
- char zone[16]; /* time zone name */
- int tzoff; /* time zone delta from GMT */
- Tzone *tz; /* time zone associated with this date */
+ int sec;
+ int min;
+ int hour;
+ int mday;
+ int mon;
+ int year;
+ int wday;
+ int yday;
+ char zone[4];
+ int tzoff;
} Tm;
-
-typedef
-struct Tmfmt {
- char *fmt;
- Tm *tm;
-} Tmfmt;
-
-#pragma varargck type "τ" Tmfmt
-
-extern Tzone* tmgetzone(char *name);
-extern Tm* tmnow(Tm*, Tzone*);
-extern Tm* tmtime(Tm*, vlong, Tzone*);
-extern Tm* tmtimens(Tm*, vlong, int, Tzone*);
-extern Tm* tmparse(Tm*, char*, char*, Tzone*);
-extern Tm* tmnorm(Tm*);
-extern Tmfmt tmfmt(Tm*, char*);
-extern void tmfmtinstall(void);
extern Tm* gmtime(long);
extern Tm* localtime(long);
--- a/sys/man/2/tmdate
+++ /dev/null
@@ -1,246 +1,0 @@
-.TH TMDATE 2
-.SH NAME
-tmnow, tmgetzone, tmtime, tmparse, tmfmt, tmnorm, - convert date and time
-.SH SYNOPSIS
-.B #include <u.h>
-.br
-.B #include <libc.h>
-.PP
-.ft L
-.nf
-.EX
-typedef struct Tmd Tmd;
-typedef struct Tmfmt Tmfmt;
-
-struct {
- vlong abs; /* seconds since Jan 1 1970, UTC */
- int nsec; /* nanoseconds (range 0..1e9) */
- int sec; /* seconds (range 0..59) */
- int min; /* minutes (0..59) */
- int hour; /* hours (0..23) */
- int mday; /* day of the month (1..31) */
- int mon; /* month of the year (0..11) */
- int year; /* C.E year - 1900 */
- int wday; /* day of week (0..6, Sunday = 0) */
- int yday; /* day of year (0..365) */
- char zone[]; /* time zone name */
- int tzoff; /* time zone delta from GMT, seconds */
-};
-
-Tzone *tmgetzone(char *name);
-Tm *tmnow(Tm *tm, char *tz);
-Tm *tmtime(Tm *tm, vlong abs, Tzone *tz);
-Tm *tmtimens(Tm *tm, vlong abs, int ns, Tzone *tz);
-Tm *tmparse(Tm *dst, char *fmt, char *tm, Tzone *zone);
-void tmnorm(Tm *tm);
-Tmfmt tmfmt(Tm *tm, char *fmt);
-void tmfmtinstall(void);
-.EE
-.SH DESCRIPTION
-.PP
-This family of functions handles simple date and time manipulation.
-Times are represented as an absolute instant in time, combined with a time zone.
-.PP
-Time zones are loaded by as name.
-They can be specified as the abbreviated timezone name,
-the full timezone name, the path to a timezone file,
-or an absolute offset in the HHMM form.
-.PP
-When given as a timezone, any instant-dependent adjustments such as leap
-seconds and daylight savings time will be applied to the derived fields of
-struct tm, but will not affect the absolute time.
-The time zone name local always refers to the time in /env/timezone.
-The nil timezone always refers to GMT.
-.PP
-Tmgetzone loads a timezone by name. The returned timezone is
-cached for the lifetime of the program, and should not be freed.
-Loading a timezone repeatedly by name loads from the cache, and
-does not leak.
-.PP
-Tmnow gets the current time of day in the requested time zone.
-.PP
-Tmtime converts the millisecond-resolution timestamp 'abs'
-into a Tm struct in the requested timezone.
-Tmtimens does the same, but with a nanosecond accuracy.
-.PP
-Tmstime is identical to tmtime, but accepts the time in sec-
-onds.
-.PP
-Tmparse parses a time from a string according to the format argument.
-The result is returned in the timezone requested.
-If there is a timezone in the date, and a timezone is provided
-when parsing, then the zone is shifted to the provided timezone.
-Parsing is case-insensitive.
-.PP
-The format argument contains zero or more of the following components:
-.TP
-.B Y, YY, YYYY
-Represents the year.
-.I YY
-prints the year in 2 digit form.
-.TP
-.B M, MM, MMM, MMMM
-The month of the year, in unpadded numeric, padded numeric, short name, or long name,
-respectively.
-.TP
-.B D, DD
-The day of month in unpadded or padded numeric form, respectively.
-.TP
-.B W, WW
-The day of week in short or long name form, respectively.
-.TP
-.B h, hh
-The hour in unpadded or padded form, respectively
-.TP
-.B m, mm
-The minute in unpadded or padded form, respectively
-.TP
-.B s, ss
-The second in unpadded or padded form, respectively
-.TP
-.B z, Z, ZZ
-The timezone in named, [+-]HHMM and [+-]HH:MM form, respectively
-.TP
-.B a, A
-Lower and uppercase 'am' and 'pm' specifiers, respectively.
-.TP
-.B [...]
-Quoted text, copied directly to the output.
-.TP
-.B _
-When formatting, this inserts padding into the date format.
-The padded width of a field is the sum of format and specifier
-characters combined.
-For example,
-.I __h
-will format to a width of 3.
-.TP
-.B ?
-When parsing, this makes the following argument match fuzzily.
-Fuzzy matching means that all formats are tried, from most to least specific.
-For example,
-.I ?M
-will match
-.IR January ,
-.IR Jan ,
-.IR 01 ,
-and
-.IR 1 ,
-in that order of preference.
-.TP
-.B ~
-When parsing a date, this slackens range enforcement, accepting
-out of range values such as January
-.IR 32 ,
-which would get normalized to February 1st.
-.PP
-Any characters not specified above are copied directly to output,
-without modification.
-
-
-
-.PP
-If the format argument is nil, it makes an
-attempt to parse common human readable date formats. These
-formats include ISO-8601,RFC-3339 and RFC-2822 dates.
-.
-.PP
-Tmfmt produces a format description structure suitable for passing
-to
-.IR fmtprint (2) .
-If fmt is nil, we default to the format used in
-.IR ctime (2).
-The format of the format string is identical to
-.IR tmparse.
-
-.PP
-When parsing, any amount of whitespace is treated as a single token.
-All string matches are case insensitive, and zero padding is optional.
-
-.PP
-Tmnorm takes a manually adjusted Tm structure, and recal-
-culates the absolute time from the
-.I year, mon, mday, hr, min
-and
-.I sec
-fields. Other fields are ignored.
-This recalculation respects the time zone stored in struct tm.
-Out of range values are wrapped. For example, December 32nd
-becomes January 1st.
-
-.PP
-Tmfmtinstall installs a time format specifier %τ. The time
-format behaves as in tmfmt
-
-.SH Examples
-.PP
-All examples assume tmfmtinstall has been called.
-.PP
-Get the current date in the local timezone, UTC, and
-US_Pacific time. Print it using the default format.
-
-.IP
-.EX
-Tm t;
-Tzone *zl, *zp;
-if((zl = tmgetzone("local") == nil)
- sysfatal("load zone: %r");
-if((zp = tmgetzone("US_Pacific") == nil)
- sysfatal("load zone: %r");
-print("local: %τ\\n", tmfmt(tmnow(&t, zl), nil));
-print("gmt: %τ\\n", tmfmt(tmnow(&t, nil), nil));
-print("eastern: %τ\\n", tmfmt(tmnow(&t, zp), nil));
-.EE
-.PP
-Compare if two times are the same, regardless of timezone.
-
-.IP
-.EX
-Tm a, b;
-
-tmparse(&a, nil, "Tue Dec 10 12:36:00 PST 2019");
-tmparse(&b, nil, "Tue Dec 10 15:36:00 EST 2019");
-if(a.abs == b.abs)
- print("same\\n");
-else
- print("different\\n");
-.EE
-
-.PP
-Convert from one timezone to another.
-
-.IP
-.EX
-Tm here, there;
-Tzone *zl, *zp;
-if((zl = tmgetzone("local")) == nil)
- sysfatal("load zone: %r");
-if((zp = tmgetzone("US_Pacific")) == nil)
- sysfatal("load zone: %r");
-if(tmnow(&here, zl) == nil)
- sysfatal("get time: %r");
-if(tmtime(&there, here.abs, zp) == nil)
- sysfatal("shift time: %r");
-.EE
-
-.PP
-Add a day to two times. Because we picked daylight savings
-time to adjust over, only 23 hours are added.
-
-.EX
-Tm t;
-tmparse(&t, "W MMM D hh:mm:ss z YYYY, "Sun Nov 2 13:11:11 PST 2019");
-tm.day++;
-tmrecalc(&t);
-print("%τ", &t); /* Mon Nov 3 13:11:11 PST 2019 */
-.EE
-
-.SH BUGS
-.PP
-There is no way to format specifier for subsecond precision.
-.PP
-The timezone information that we ship is out of date.
-.PP
-The plan 9 timezone format has no way to express leap seconds.
-.PP
-We provide no way to manipulate timezones.
--- a/sys/src/cmd/seconds.c
+++ b/sys/src/cmd/seconds.c
@@ -1,37 +1,237 @@
+/*
+ * seconds absolute_date ... - convert absolute_date to seconds since epoch
+ */
+
#include <u.h>
#include <libc.h>
+#include <ctype.h>
+typedef ulong Time;
+
+enum {
+ AM, PM, HR24,
+
+ /* token types */
+ Month = 1,
+ Year,
+ Day,
+ Timetok,
+ Tz,
+ Dtz,
+ Ignore,
+ Ampm,
+
+ Maxtok = 6, /* only this many chars are stored in datetktbl */
+ Maxdateflds = 25,
+};
+
/*
- * seconds absolute_date ... - convert absolute_date to seconds since epoch
+ * macros for squeezing values into low 7 bits of "value".
+ * all timezones we care about are divisible by 10, and the largest value
+ * (780) when divided is 78.
*/
-char *formats[] = {
- /* asctime */
- "W MMM DD hh:mm:ss ?Z YYYY",
- /* RFC5322 */
- "?W ?DD ?MMM ?YYYY hh:mm:ss ?Z",
- "?W, DD-?MM-YY hh:mm:ss ?Z",
- /* RFC822/RFC2822 */
- "DD MMM YY hh:mm ZZZ",
- "DD MMM YY hh:mm Z",
- /* RFC850 */
- "W, DD-MMM-YY hh:mm:ss MST",
- /* RFC1123 */
- "WW, DD MMM YYYY hh:mm:ss ZZZ",
- /* RFC1123Z */
- "WW, DD MMM YYYY hh:mm:ss ZZ",
- /* RFC3339 */
- "YYYY-MM-DD[T]hh:mm:ss[Z]ZZ",
- "YYYY-MM-DD[T]hh:mm:ss[Z]Z",
- "YYYY-MM-DD[T]hh:mm:ss ZZ",
- "YYYY-MM-DD[T]hh:mm:ss Z",
- /* RFC 3339 and human-readable variants */
- "YYYY-MM-DD hh:mm:ss",
- "YYYY-MM-DD hh:mm:ss ?Z",
- "YYYY-MM-DD [@] hh:mm:ss",
- "YYYY-MM-DD [@] hh:mm:ss ?Z",
- nil
-};
+#define TOVAL(tp, v) ((tp)->value = (v) / 10)
+#define FROMVAL(tp) ((tp)->value * 10) /* uncompress */
+
+/* keep this struct small since we have an array of them */
+typedef struct {
+ char token[Maxtok];
+ char type;
+ schar value;
+} Datetok;
+
+int dtok_numparsed;
+
+/* forwards */
+Datetok *datetoktype(char *s, int *bigvalp);
+
+static Datetok datetktbl[];
+static unsigned szdatetktbl;
+
+/* parse 1- or 2-digit number, advance *cpp past it */
+static int
+eatnum(char **cpp)
+{
+ int c, x;
+ char *cp;
+
+ cp = *cpp;
+ c = *cp;
+ if (!isascii(c) || !isdigit(c))
+ return -1;
+ x = c - '0';
+
+ c = *++cp;
+ if (isascii(c) && isdigit(c)) {
+ x = 10*x + c - '0';
+ cp++;
+ }
+ *cpp = cp;
+ return x;
+}
+/* return -1 on failure */
+int
+parsetime(char *time, Tm *tm)
+{
+ tm->hour = eatnum(&time);
+ if (tm->hour == -1 || *time++ != ':')
+ return -1; /* only hour; too short */
+
+ tm->min = eatnum(&time);
+ if (tm->min == -1)
+ return -1;
+ if (*time++ != ':') {
+ tm->sec = 0;
+ return 0; /* no seconds; okay */
+ }
+
+ tm->sec = eatnum(&time);
+ if (tm->sec == -1)
+ return -1;
+
+ /* this may be considered too strict. garbage at end of time? */
+ return *time == '\0' || isascii(*time) && isspace(*time)? 0: -1;
+}
+
+/*
+ * try to parse pre-split timestr in fields as an absolute date
+ */
+int
+tryabsdate(char **fields, int nf, Tm *now, Tm *tm)
+{
+ int i, mer = HR24, bigval = -1;
+ long flg = 0, ty;
+ Datetok *tp;
+
+ now = localtime(time(0)); /* default to local time (zone) */
+ tm->tzoff = now->tzoff;
+ strncpy(tm->zone, now->zone, sizeof tm->zone);
+
+ tm->mday = tm->mon = tm->year = -1; /* mandatory */
+ tm->hour = tm->min = tm->sec = 0;
+ dtok_numparsed = 0;
+
+ for (i = 0; i < nf; i++) {
+ if (fields[i][0] == '\0')
+ continue;
+ tp = datetoktype(fields[i], &bigval);
+ ty = (1L << tp->type) & ~(1L << Ignore);
+ if (flg & ty)
+ return -1; /* repeated type */
+ flg |= ty;
+ switch (tp->type) {
+ case Year:
+ tm->year = bigval;
+ if (tm->year < 1970 || tm->year > 2106)
+ return -1; /* can't represent in ulong */
+ /* convert 4-digit year to 1900 origin */
+ if (tm->year >= 1900)
+ tm->year -= 1900;
+ break;
+ case Day:
+ tm->mday = bigval;
+ break;
+ case Month:
+ tm->mon = tp->value - 1; /* convert to zero-origin */
+ break;
+ case Timetok:
+ if (parsetime(fields[i], tm) < 0)
+ return -1;
+ break;
+ case Dtz:
+ case Tz:
+ /* tm2sec mangles timezones, so we do our own handling */
+ tm->tzoff = FROMVAL(tp);
+ snprint(tm->zone, sizeof(tm->zone), "GMT");
+ break;
+ case Ignore:
+ break;
+ case Ampm:
+ mer = tp->value;
+ break;
+ default:
+ return -1; /* bad token type: CANTHAPPEN */
+ }
+ }
+ if (tm->year == -1 || tm->mon == -1 || tm->mday == -1)
+ return -1; /* missing component */
+ if (mer == PM)
+ tm->hour += 12;
+ return 0;
+}
+
+int
+prsabsdate(char *timestr, Tm *now, Tm *tm)
+{
+ int nf;
+ char *fields[Maxdateflds];
+ static char delims[] = "- \t\n/,";
+
+ nf = gettokens(timestr, fields, nelem(fields), delims+1);
+ if (nf > nelem(fields))
+ return -1;
+ if (tryabsdate(fields, nf, now, tm) < 0) {
+ char *p = timestr;
+
+ /*
+ * could be a DEC-date; glue it all back together, split it
+ * with dash as a delimiter and try again. Yes, this is a
+ * hack, but so are DEC-dates.
+ */
+ while (--nf > 0) {
+ while (*p++ != '\0')
+ ;
+ p[-1] = ' ';
+ }
+ nf = gettokens(timestr, fields, nelem(fields), delims);
+ if (nf > nelem(fields) || tryabsdate(fields, nf, now, tm) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int
+validtm(Tm *tm)
+{
+ if (tm->year < 0 || tm->mon < 0 || tm->mon > 11 ||
+ tm->mday < 1 || tm->hour < 0 || tm->hour >= 24 ||
+ tm->min < 0 || tm->min > 59 ||
+ tm->sec < 0 || tm->sec > 61) /* allow 2 leap seconds */
+ return 0;
+ return 1;
+}
+
+Time
+seconds(char *timestr)
+{
+ Tm date;
+
+ memset(&date, 0, sizeof date);
+ if (prsabsdate(timestr, localtime(time(0)), &date) < 0)
+ return -1;
+ return validtm(&date)? tm2sec(&date) - 60*date.tzoff: -1;
+}
+
+int
+convert(char *timestr)
+{
+ char *copy;
+ Time tstime;
+
+ copy = strdup(timestr);
+ if (copy == nil)
+ sysfatal("out of memory");
+ tstime = seconds(copy);
+ free(copy);
+ if (tstime == -1) {
+ fprint(2, "%s: `%s' not a valid date\n", argv0, timestr);
+ return 1;
+ }
+ print("%lud\n", tstime);
+ return 0;
+}
+
static void
usage(void)
{
@@ -42,31 +242,225 @@
void
main(int argc, char **argv)
{
- Tm tm;
- char **f, *fmt;
- int i;
+ int i, sts;
- fmt = nil;
+ sts = 0;
ARGBEGIN{
- case 'f':
- fmt = EARGF(usage());
- break;
default:
usage();
- }ARGEND;
+ }ARGEND
+ if (argc == 0)
+ usage();
+ for (i = 0; i < argc; i++)
+ sts |= convert(argv[i]);
+ exits(sts != 0? "bad": 0);
+}
- for(i = 0; i < argc; i++){
- if(fmt != nil){
- if(tmparse(&tm, fmt, argv[i], nil) != nil)
- goto Found;
- }else{
- for(f = formats; *f != nil; f++)
- if(tmparse(&tm, *f, argv[i], nil) != nil)
- goto Found;
+/*
+ * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
+ * is WAY faster than the generic bsearch().
+ */
+Datetok *
+datebsearch(char *key, Datetok *base, unsigned nel)
+{
+ int cmp;
+ Datetok *last = base + nel - 1, *pos;
+
+ while (last >= base) {
+ pos = base + ((last - base) >> 1);
+ cmp = key[0] - pos->token[0];
+ if (cmp == 0) {
+ cmp = strncmp(key, pos->token, Maxtok);
+ if (cmp == 0)
+ return pos;
}
- sysfatal("tmparse: %r");
-Found:
- print("%lld\n", tm.abs);
+ if (cmp < 0)
+ last = pos - 1;
+ else
+ base = pos + 1;
}
- exits(nil);
+ return 0;
}
+
+Datetok *
+datetoktype(char *s, int *bigvalp)
+{
+ char *cp = s;
+ char c = *cp;
+ static Datetok t;
+ Datetok *tp = &t;
+
+ if (isascii(c) && isdigit(c)) {
+ int len = strlen(cp);
+
+ if (len > 3 && (cp[1] == ':' || cp[2] == ':'))
+ tp->type = Timetok;
+ else {
+ if (bigvalp != nil)
+ *bigvalp = atoi(cp); /* won't fit in tp->value */
+ if (len == 4)
+ tp->type = Year;
+ else if (++dtok_numparsed == 1)
+ tp->type = Day;
+ else
+ tp->type = Year;
+ }
+ } else if (c == '-' || c == '+') {
+ int val = atoi(cp + 1);
+ int hr = val / 100;
+ int min = val % 100;
+
+ val = hr*60 + min;
+ TOVAL(tp, c == '-'? -val: val);
+ tp->type = Tz;
+ } else {
+ char lowtoken[Maxtok+1];
+ char *ltp = lowtoken, *endltp = lowtoken+Maxtok;
+
+ /* copy to lowtoken to avoid modifying s */
+ while ((c = *cp++) != '\0' && ltp < endltp)
+ *ltp++ = (isascii(c) && isupper(c)? tolower(c): c);
+ *ltp = '\0';
+ tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
+ if (tp == nil) {
+ tp = &t;
+ tp->type = Ignore;
+ }
+ }
+ return tp;
+}
+
+
+/*
+ * to keep this table reasonably small, we divide the lexval for Tz and Dtz
+ * entries by 10 and truncate the text field at MAXTOKLEN characters.
+ * the text field is not guaranteed to be NUL-terminated.
+ */
+static Datetok datetktbl[] = {
+/* text token lexval */
+ "acsst", Dtz, 63, /* Cent. Australia */
+ "acst", Tz, 57, /* Cent. Australia */
+ "adt", Dtz, -18, /* Atlantic Daylight Time */
+ "aesst", Dtz, 66, /* E. Australia */
+ "aest", Tz, 60, /* Australia Eastern Std Time */
+ "ahst", Tz, 60, /* Alaska-Hawaii Std Time */
+ "am", Ampm, AM,
+ "apr", Month, 4,
+ "april", Month, 4,
+ "ast", Tz, -24, /* Atlantic Std Time (Canada) */
+ "at", Ignore, 0, /* "at" (throwaway) */
+ "aug", Month, 8,
+ "august", Month, 8,
+ "awsst", Dtz, 54, /* W. Australia */
+ "awst", Tz, 48, /* W. Australia */
+ "bst", Tz, 6, /* British Summer Time */
+ "bt", Tz, 18, /* Baghdad Time */
+ "cadt", Dtz, 63, /* Central Australian DST */
+ "cast", Tz, 57, /* Central Australian ST */
+ "cat", Tz, -60, /* Central Alaska Time */
+ "cct", Tz, 48, /* China Coast */
+ "cdt", Dtz, -30, /* Central Daylight Time */
+ "cet", Tz, 6, /* Central European Time */
+ "cetdst", Dtz, 12, /* Central European Dayl.Time */
+ "cst", Tz, -36, /* Central Standard Time */
+ "dec", Month, 12,
+ "decemb", Month, 12,
+ "dnt", Tz, 6, /* Dansk Normal Tid */
+ "dst", Ignore, 0,
+ "east", Tz, -60, /* East Australian Std Time */
+ "edt", Dtz, -24, /* Eastern Daylight Time */
+ "eet", Tz, 12, /* East. Europe, USSR Zone 1 */
+ "eetdst", Dtz, 18, /* Eastern Europe */
+ "est", Tz, -30, /* Eastern Standard Time */
+ "feb", Month, 2,
+ "februa", Month, 2,
+ "fri", Ignore, 5,
+ "friday", Ignore, 5,
+ "fst", Tz, 6, /* French Summer Time */
+ "fwt", Dtz, 12, /* French Winter Time */
+ "gmt", Tz, 0, /* Greenwish Mean Time */
+ "gst", Tz, 60, /* Guam Std Time, USSR Zone 9 */
+ "hdt", Dtz, -54, /* Hawaii/Alaska */
+ "hmt", Dtz, 18, /* Hellas ? ? */
+ "hst", Tz, -60, /* Hawaii Std Time */
+ "idle", Tz, 72, /* Intl. Date Line, East */
+ "idlw", Tz, -72, /* Intl. Date Line, West */
+ "ist", Tz, 12, /* Israel */
+ "it", Tz, 22, /* Iran Time */
+ "jan", Month, 1,
+ "januar", Month, 1,
+ "jst", Tz, 54, /* Japan Std Time,USSR Zone 8 */
+ "jt", Tz, 45, /* Java Time */
+ "jul", Month, 7,
+ "july", Month, 7,
+ "jun", Month, 6,
+ "june", Month, 6,
+ "kst", Tz, 54, /* Korea Standard Time */
+ "ligt", Tz, 60, /* From Melbourne, Australia */
+ "mar", Month, 3,
+ "march", Month, 3,
+ "may", Month, 5,
+ "mdt", Dtz, -36, /* Mountain Daylight Time */
+ "mest", Dtz, 12, /* Middle Europe Summer Time */
+ "met", Tz, 6, /* Middle Europe Time */
+ "metdst", Dtz, 12, /* Middle Europe Daylight Time*/
+ "mewt", Tz, 6, /* Middle Europe Winter Time */
+ "mez", Tz, 6, /* Middle Europe Zone */
+ "mon", Ignore, 1,
+ "monday", Ignore, 1,
+ "mst", Tz, -42, /* Mountain Standard Time */
+ "mt", Tz, 51, /* Moluccas Time */
+ "ndt", Dtz, -15, /* Nfld. Daylight Time */
+ "nft", Tz, -21, /* Newfoundland Standard Time */
+ "nor", Tz, 6, /* Norway Standard Time */
+ "nov", Month, 11,
+ "novemb", Month, 11,
+ "nst", Tz, -21, /* Nfld. Standard Time */
+ "nt", Tz, -66, /* Nome Time */
+ "nzdt", Dtz, 78, /* New Zealand Daylight Time */
+ "nzst", Tz, 72, /* New Zealand Standard Time */
+ "nzt", Tz, 72, /* New Zealand Time */
+ "oct", Month, 10,
+ "octobe", Month, 10,
+ "on", Ignore, 0, /* "on" (throwaway) */
+ "pdt", Dtz, -42, /* Pacific Daylight Time */
+ "pm", Ampm, PM,
+ "pst", Tz, -48, /* Pacific Standard Time */
+ "sadt", Dtz, 63, /* S. Australian Dayl. Time */
+ "sast", Tz, 57, /* South Australian Std Time */
+ "sat", Ignore, 6,
+ "saturd", Ignore, 6,
+ "sep", Month, 9,
+ "sept", Month, 9,
+ "septem", Month, 9,
+ "set", Tz, -6, /* Seychelles Time ?? */
+ "sst", Dtz, 12, /* Swedish Summer Time */
+ "sun", Ignore, 0,
+ "sunday", Ignore, 0,
+ "swt", Tz, 6, /* Swedish Winter Time */
+ "thu", Ignore, 4,
+ "thur", Ignore, 4,
+ "thurs", Ignore, 4,
+ "thursd", Ignore, 4,
+ "tue", Ignore, 2,
+ "tues", Ignore, 2,
+ "tuesda", Ignore, 2,
+ "ut", Tz, 0,
+ "utc", Tz, 0,
+ "wadt", Dtz, 48, /* West Australian DST */
+ "wast", Tz, 42, /* West Australian Std Time */
+ "wat", Tz, -6, /* West Africa Time */
+ "wdt", Dtz, 54, /* West Australian DST */
+ "wed", Ignore, 3,
+ "wednes", Ignore, 3,
+ "weds", Ignore, 3,
+ "wet", Tz, 0, /* Western Europe */
+ "wetdst", Dtz, 6, /* Western Europe */
+ "wst", Tz, 48, /* West Australian Std Time */
+ "ydt", Dtz, -48, /* Yukon Daylight Time */
+ "yst", Tz, -54, /* Yukon Standard Time */
+ "zp4", Tz, -24, /* GMT +4 hours. */
+ "zp5", Tz, -30, /* GMT +5 hours. */
+ "zp6", Tz, -36, /* GMT +6 hours. */
+};
+static unsigned szdatetktbl = nelem(datetktbl);
--- a/sys/src/libc/9sys/ctime.c
+++ b/sys/src/libc/9sys/ctime.c
@@ -33,34 +33,269 @@
#include <u.h>
#include <libc.h>
+static char dmsize[12] =
+{
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * The following table is used for 1974 and 1975 and
+ * gives the day number of the first day after the Sunday of the
+ * change.
+ */
+
+static int dysize(int);
+static void ct_numb(char*, int);
+
+#define TZSIZE 150
+static void readtimezone(void);
+static int rd_name(char**, char*);
+static int rd_long(char**, long*);
+static
+struct
+{
+ char stname[4];
+ char dlname[4];
+ long stdiff;
+ long dldiff;
+ long dlpairs[TZSIZE];
+} timezone;
+
+char*
+ctime(long t)
+{
+ return asctime(localtime(t));
+}
+
Tm*
localtime(long tim)
{
- static Tm tm;
- Tzone *tz;
+ Tm *ct;
+ long t, *p;
+ int dlflag;
- /* No error checking: the API doesn't allow it. */
- tz = tmgetzone("local");
- tmtime(&tm, tim, tz);
- return &tm;
-
+ if(timezone.stname[0] == 0)
+ readtimezone();
+ t = tim + timezone.stdiff;
+ dlflag = 0;
+ for(p = timezone.dlpairs; *p; p += 2)
+ if(t >= p[0])
+ if(t < p[1]) {
+ t = tim + timezone.dldiff;
+ dlflag++;
+ break;
+ }
+ ct = gmtime(t);
+ if(dlflag){
+ strcpy(ct->zone, timezone.dlname);
+ ct->tzoff = timezone.dldiff;
+ } else {
+ strcpy(ct->zone, timezone.stname);
+ ct->tzoff = timezone.stdiff;
+ }
+ return ct;
}
Tm*
-gmtime(long abs)
+gmtime(long tim)
{
- static Tm tm;
- return tmtime(&tm, abs, nil);
+ int d0, d1;
+ long hms, day;
+ static Tm xtime;
+
+ /*
+ * break initial number into days
+ */
+ hms = tim % 86400L;
+ day = tim / 86400L;
+ if(hms < 0) {
+ hms += 86400L;
+ day -= 1;
+ }
+
+ /*
+ * generate hours:minutes:seconds
+ */
+ xtime.sec = hms % 60;
+ d1 = hms / 60;
+ xtime.min = d1 % 60;
+ d1 /= 60;
+ xtime.hour = d1;
+
+ /*
+ * day is the day number.
+ * generate day of the week.
+ * The addend is 4 mod 7 (1/1/1970 was Thursday)
+ */
+
+ xtime.wday = (day + 7340036L) % 7;
+
+ /*
+ * year number
+ */
+ if(day >= 0)
+ for(d1 = 1970; day >= dysize(d1); d1++)
+ day -= dysize(d1);
+ else
+ for (d1 = 1970; day < 0; d1--)
+ day += dysize(d1-1);
+ xtime.year = d1-1900;
+ xtime.yday = d0 = day;
+
+ /*
+ * generate month
+ */
+
+ if(dysize(d1) == 366)
+ dmsize[1] = 29;
+ for(d1 = 0; d0 >= dmsize[d1]; d1++)
+ d0 -= dmsize[d1];
+ dmsize[1] = 28;
+ xtime.mday = d0 + 1;
+ xtime.mon = d1;
+ strcpy(xtime.zone, "GMT");
+ return &xtime;
}
char*
-ctime(long abs)
+asctime(Tm *t)
{
- Tzone *tz;
- Tm tm;
+ char *ncp;
+ static char cbuf[30];
- /* No error checking: the API doesn't allow it. */
- tz = tmgetzone("local");
- tmtime(&tm, abs, tz);
- return asctime(&tm);
+ strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n");
+ ncp = &"SunMonTueWedThuFriSat"[t->wday*3];
+ cbuf[0] = *ncp++;
+ cbuf[1] = *ncp++;
+ cbuf[2] = *ncp;
+ ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->mon*3];
+ cbuf[4] = *ncp++;
+ cbuf[5] = *ncp++;
+ cbuf[6] = *ncp;
+ ct_numb(cbuf+8, t->mday);
+ ct_numb(cbuf+11, t->hour+100);
+ ct_numb(cbuf+14, t->min+100);
+ ct_numb(cbuf+17, t->sec+100);
+ ncp = t->zone;
+ cbuf[20] = *ncp++;
+ cbuf[21] = *ncp++;
+ cbuf[22] = *ncp;
+ ct_numb(cbuf+24, (t->year+1900) / 100 + 100);
+ ct_numb(cbuf+26, t->year+100);
+ return cbuf;
+}
+
+static
+dysize(int y)
+{
+
+ if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
+ return 366;
+ return 365;
+}
+
+static
+void
+ct_numb(char *cp, int n)
+{
+
+ cp[0] = ' ';
+ if(n >= 10)
+ cp[0] = (n/10)%10 + '0';
+ cp[1] = n%10 + '0';
+}
+
+static
+void
+readtimezone(void)
+{
+ char buf[TZSIZE*11+30], *p;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+ i = open("/env/timezone", 0);
+ if(i < 0)
+ goto error;
+ if(read(i, buf, sizeof(buf)) >= sizeof(buf)){
+ close(i);
+ goto error;
+ }
+ close(i);
+ p = buf;
+ if(rd_name(&p, timezone.stname))
+ goto error;
+ if(rd_long(&p, &timezone.stdiff))
+ goto error;
+ if(rd_name(&p, timezone.dlname))
+ goto error;
+ if(rd_long(&p, &timezone.dldiff))
+ goto error;
+ for(i=0; i<TZSIZE; i++) {
+ if(rd_long(&p, &timezone.dlpairs[i]))
+ goto error;
+ if(timezone.dlpairs[i] == 0)
+ return;
+ }
+
+error:
+ timezone.stdiff = 0;
+ strcpy(timezone.stname, "GMT");
+ timezone.dlpairs[0] = 0;
+}
+
+static
+rd_name(char **f, char *p)
+{
+ int c, i;
+
+ for(;;) {
+ c = *(*f)++;
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ for(i=0; i<3; i++) {
+ if(c == ' ' || c == '\n')
+ return 1;
+ *p++ = c;
+ c = *(*f)++;
+ }
+ if(c != ' ' && c != '\n')
+ return 1;
+ *p = 0;
+ return 0;
+}
+
+static
+rd_long(char **f, long *p)
+{
+ int c, s;
+ long l;
+
+ s = 0;
+ for(;;) {
+ c = *(*f)++;
+ if(c == '-') {
+ s++;
+ continue;
+ }
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ if(c == 0) {
+ *p = 0;
+ return 0;
+ }
+ l = 0;
+ for(;;) {
+ if(c == ' ' || c == '\n')
+ break;
+ if(c < '0' || c > '9')
+ return 1;
+ l = l*10 + c-'0';
+ c = *(*f)++;
+ }
+ if(s)
+ l = -l;
+ *p = l;
+ return 0;
}
--- a/sys/src/libc/9sys/tm2sec.c
+++ b/sys/src/libc/9sys/tm2sec.c
@@ -1,9 +1,202 @@
#include <u.h>
#include <libc.h>
+#define TZSIZE 150
+static void readtimezone(void);
+static int rd_name(char**, char*);
+static int rd_long(char**, long*);
+static
+struct
+{
+ char stname[4];
+ char dlname[4];
+ long stdiff;
+ long dldiff;
+ long dlpairs[TZSIZE];
+} timezone;
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int *
+yrsize(int y)
+{
+ if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+/*
+ * compute seconds since Jan 1 1970 GMT
+ * and convert to our timezone.
+ */
long
tm2sec(Tm *tm)
{
- tmnorm(tm);
- return tm->abs;
+ long secs, *p;
+ int i, yday, year, *d2m;
+
+ if(strcmp(tm->zone, "GMT") != 0 && timezone.stname[0] == 0)
+ readtimezone();
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ year = tm->year + 1900;
+ for(i = 1970; i < year; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * if mday is set, use mon and mday to compute yday
+ */
+ if(tm->mday){
+ yday = 0;
+ d2m = yrsize(year);
+ for(i=0; i<tm->mon; i++)
+ yday += d2m[i+1];
+ yday += tm->mday-1;
+ }else{
+ yday = tm->yday;
+ }
+ secs += yday * SEC2DAY;
+
+ /*
+ * hours, minutes, seconds
+ */
+ secs += tm->hour * SEC2HOUR;
+ secs += tm->min * SEC2MIN;
+ secs += tm->sec;
+
+ /*
+ * Only handles zones mentioned in /env/timezone,
+ * but things get too ambiguous otherwise.
+ */
+ if(strcmp(tm->zone, timezone.stname) == 0)
+ secs -= timezone.stdiff;
+ else if(strcmp(tm->zone, timezone.dlname) == 0)
+ secs -= timezone.dldiff;
+ else if(tm->zone[0] == 0){
+ secs -= timezone.dldiff;
+ for(p = timezone.dlpairs; *p; p += 2)
+ if(secs >= p[0] && secs < p[1])
+ break;
+ if(*p == 0){
+ secs += timezone.dldiff;
+ secs -= timezone.stdiff;
+ }
+ }
+ return secs;
+}
+
+static
+void
+readtimezone(void)
+{
+ char buf[TZSIZE*11+30], *p;
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+ i = open("/env/timezone", 0);
+ if(i < 0)
+ goto error;
+ if(read(i, buf, sizeof(buf)) >= sizeof(buf))
+ goto error;
+ close(i);
+ p = buf;
+ if(rd_name(&p, timezone.stname))
+ goto error;
+ if(rd_long(&p, &timezone.stdiff))
+ goto error;
+ if(rd_name(&p, timezone.dlname))
+ goto error;
+ if(rd_long(&p, &timezone.dldiff))
+ goto error;
+ for(i=0; i<TZSIZE; i++) {
+ if(rd_long(&p, &timezone.dlpairs[i]))
+ goto error;
+ if(timezone.dlpairs[i] == 0)
+ return;
+ }
+
+error:
+ timezone.stdiff = 0;
+ strcpy(timezone.stname, "GMT");
+ timezone.dlpairs[0] = 0;
+}
+
+static int
+rd_name(char **f, char *p)
+{
+ int c, i;
+
+ for(;;) {
+ c = *(*f)++;
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ for(i=0; i<3; i++) {
+ if(c == ' ' || c == '\n')
+ return 1;
+ *p++ = c;
+ c = *(*f)++;
+ }
+ if(c != ' ' && c != '\n')
+ return 1;
+ *p = 0;
+ return 0;
+}
+
+static int
+rd_long(char **f, long *p)
+{
+ int c, s;
+ long l;
+
+ s = 0;
+ for(;;) {
+ c = *(*f)++;
+ if(c == '-') {
+ s++;
+ continue;
+ }
+ if(c != ' ' && c != '\n')
+ break;
+ }
+ if(c == 0) {
+ *p = 0;
+ return 0;
+ }
+ l = 0;
+ for(;;) {
+ if(c == ' ' || c == '\n')
+ break;
+ if(c < '0' || c > '9')
+ return 1;
+ l = l*10 + c-'0';
+ c = *(*f)++;
+ }
+ if(s)
+ l = -l;
+ *p = l;
+ return 0;
}
--- a/sys/src/libc/port/date.c
+++ /dev/null
@@ -1,859 +1,0 @@
-#include <u.h>
-#include <libc.h>
-
-typedef struct Tzabbrev Tzabbrev;
-typedef struct Tzoffpair Tzoffpair;
-
-#define Ctimefmt "W MMM _D hh:mm:ss ZZZ YYYY\n"
-enum {
- Tzsize = 150,
- Nsec = 1000*1000*1000,
- Daysec = (vlong)24*3600,
- Days400y = 365*400 + 4*25 - 3,
- Days4y = 365*4 + 1,
-};
-
-enum {
- Cend,
- Cspace,
- Cnum,
- Cletter,
- Cpunct,
-};
-
-struct Tzone {
- char tzname[16];
- char stname[4];
- char dlname[4];
- long stdiff;
- long dldiff;
- long dlpairs[Tzsize];
-};
-
-static QLock zlock;
-static int nzones;
-static Tzone **zones;
-static int mdays[] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
-static char *wday[] = {
- "Sunday","Monday","Tuesday",
- "Wednesday","Thursday","Friday",
- "Saturday", nil,
-};
-static char *month[] = {
- "January", "February", "March",
- "April", "May", "June", "July",
- "August", "September", "October",
- "November", "December", nil
-};
-
-struct Tzabbrev {
- char *abbr;
- char *name;
-};
-
-struct Tzoffpair {
- char *abbr;
- int off;
-};
-
-/* Obsolete time zone names. Hardcoded to match RFC5322 */
-static Tzabbrev tzabbrev[] = {
- {"UT", "GMT"}, {"GMT", "GMT"}, {"UTC", "GMT"},
- {"EST", "US_Eastern"}, {"EDT", "US_Eastern"},
- {"CST", "US_Central"}, {"CDT", "US_Central"},
- {"MST", "US_Mountain"}, {"MDT", "US_Mountain"},
- {"PST", "US_Pacific"}, {"PDT", "US_Pacific"},
- {nil},
-};
-
-/* Military timezone names */
-static Tzoffpair milabbrev[] = {
- {"A", -1*3600}, {"B", -2*3600}, {"C", -3*3600},
- {"D", -4*3600}, {"E", -5*3600}, {"F", -6*3600},
- {"G", -7*3600}, {"H", -8*3600}, {"I", -9*3600},
- {"K", -10*3600}, {"L", -11*3600}, {"M", -12*3600},
- {"N", +1*3600}, {"O", +2*3600}, {"P", +3*3600},
- {"Q", +4*3600}, {"R", +5*3600}, {"S", +6*3600},
- {"T", +7*3600}, {"U", +8*3600}, {"V", +9*3600},
- {"W", +10*3600}, {"X", +11*3600}, {"Y", +12*3600},
- {"Z", 0}, {nil, 0}
-};
-
-static int
-isleap(int y)
-{
- return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
-}
-
-static int
-rdname(char **f, char *p)
-{
- int c, i;
-
- while((c = *(*f)++) != 0)
- if(c != ' ' && c != '\n')
- break;
- for(i=0; i<3; i++) {
- if(c == ' ' || c == '\n')
- return 1;
- *p++ = c;
- c = *(*f)++;
- }
- if(c != ' ' && c != '\n')
- return 1;
- *p = 0;
- return 0;
-}
-
-static int
-rdlong(char **f, long *p)
-{
- int c, s;
- long l;
-
- s = 0;
- while((c = *(*f)++) != 0){
- if(c == '-')
- s++;
- else if(c != ' ' && c != '\n')
- break;
- }
- if(c == 0) {
- *p = 0;
- return 0;
- }
- l = 0;
- for(;;) {
- if(c == ' ' || c == '\n')
- break;
- if(c < '0' || c > '9')
- return 1;
- l = l*10 + c-'0';
- c = *(*f)++;
- }
- if(s)
- l = -l;
- *p = l;
- return 0;
-}
-
-static int
-loadzone(Tzone *tz, char *name)
-{
- char buf[Tzsize*11+30], path[128], *p;
- int i, f, r;
-
- if(strcmp(name, "local") == 0)
- snprint(path, sizeof(path), "/env/timezone");
- else
- snprint(path, sizeof(path), "/adm/timezone/%s", name);
- memset(buf, 0, sizeof(buf));
- if((f = open(path, 0)) == -1)
- return -1;
- r = read(f, buf, sizeof(buf));
- close(f);
- if(r == sizeof(buf) || r == -1)
- return -1;
- p = buf;
- if(rdname(&p, tz->stname))
- return -1;
- if(rdlong(&p, &tz->stdiff))
- return -1;
- if(rdname(&p, tz->dlname))
- return -1;
- if(rdlong(&p, &tz->dldiff))
- return -1;
- for(i=0; i < Tzsize; i++) {
- if(rdlong(&p, &tz->dlpairs[i]))
- return -1;
- if(tz->dlpairs[i] == 0)
- return 0;
- }
- return -1;
-}
-
-Tzone*
-tmgetzone(char *tzname)
-{
- Tzone *tz, **newzones;
- int i;
-
- if(tzname == nil)
- tzname = "GMT";
- qlock(&zlock);
- for(i = 0; i < nzones; i++){
- tz = zones[i];
- if(strcmp(tz->stname, tzname) == 0)
- goto found;
- if(strcmp(tz->dlname, tzname) == 0)
- goto found;
- if(strcmp(tz->tzname, tzname) == 0)
- goto found;
- }
-
- tz = malloc(sizeof(Tzone));
- if(tz == nil)
- goto error;
- newzones = realloc(zones, (nzones + 1) * sizeof(Tzone*));
- if(newzones == nil)
- goto error;
- if(loadzone(tz, tzname) != 0)
- goto error;
- if(snprint(tz->tzname, sizeof(tz->tzname), tzname) >= sizeof(tz->tzname)){
- werrstr("timezone name too long");
- return nil;
- }
- zones = newzones;
- zones[nzones] = tz;
- nzones++;
-found:
- qunlock(&zlock);
- return tz;
-error:
- free(tz);
- qunlock(&zlock);
- return nil;
-}
-
-static void
-getzoneoff(Tzone *tz, vlong abs, Tm *tm)
-{
- long dl, *p;
- dl = 0;
- if(tz == nil){
- snprint(tm->zone, sizeof(tm->zone), "GMT");
- tm->tzoff = 0;
- return;
- }
- for(p = tz->dlpairs; *p; p += 2)
- if(abs >= p[0] && abs < p[1])
- dl = 1;
- if(dl){
- snprint(tm->zone, sizeof(tm->zone), tz->dlname);
- tm->tzoff = tz->dldiff;
- }else{
- snprint(tm->zone, sizeof(tm->zone), tz->stname);
- tm->tzoff = tz->stdiff;
- }
-}
-
-static Tm*
-tmfill(Tm *tm, vlong abs, vlong nsec)
-{
- vlong zrel, j, y, m, d, t, e;
- int i;
-
- tm->abs = abs;
- zrel = abs + tm->tzoff;
- t = zrel % Daysec;
- e = zrel / Daysec;
- if(t < 0){
- t += Daysec;
- e -= 1;
- }
-
- t += nsec/Nsec;
- tm->sec = t % 60;
- t /= 60;
- tm->min = t % 60;
- t /= 60;
- tm->hour = t;
- tm->wday = (e + 4) % 7;
-
- /*
- * Split up year, month, day.
- *
- * Implemented according to "Algorithm 199,
- * conversions between calendar date and
- * Julian day number", Robert G. Tantzen,
- * Air Force Missile Development
- * Center, Holloman AFB, New Mex.
- *
- * Lots of magic.
- */
- j = (zrel + 2440588 * Daysec) / (Daysec) - 1721119;
- y = (4 * j - 1) / Days400y;
- j = 4 * j - 1 - Days400y * y;
- d = j / 4;
- j = (4 * d + 3) / Days4y;
- d = 4 * d + 3 - Days4y * j;
- d = (d + 4) / 4 ;
- m = (5 * d - 3) / 153;
- d = 5 * d - 3 - 153 * m;
- d = (d + 5) / 5;
- y = 100 * y + j;
-
- if(m < 10)
- m += 3;
- else{
- m -= 9;
- y++;
- }
-
- /* there's no year 0 */
- if(y <= 0)
- y--;
- /* and if j negative, the day and month are also negative */
- if(m < 0)
- m += 12;
- if(d < 0)
- d += mdays[m - 1];
-
- tm->yday = d;
- for(i = 0; i < m - 1; i++)
- tm->yday += mdays[i];
- if(m > 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
- tm->yday++;
- tm->year = y - 1900;
- tm->mon = m - 1;
- tm->mday = d;
- tm->nsec = nsec%Nsec;
- return tm;
-}
-
-
-Tm*
-tmtime(Tm *tm, vlong abs, Tzone *tz)
-{
- return tmtimens(tm, abs, 0, tz);
-}
-
-Tm*
-tmtimens(Tm *tm, vlong abs, int ns, Tzone *tz)
-{
- tm->tz = tz;
- getzoneoff(tz, abs, tm);
- return tmfill(tm, abs, ns);
-}
-
-Tm*
-tmnow(Tm *tm, Tzone *tz)
-{
- vlong ns;
-
- ns = nsec();
- return tmtimens(tm, nsec()/Nsec, ns%Nsec, tz);
-}
-
-Tm*
-tmnorm(Tm *tm)
-{
- vlong c, yadj, j, abs, y, m, d;
-
- if(tm->mon > 1){
- m = tm->mon - 2;
- y = tm->year + 1900;
- }else{
- m = tm->mon + 10;
- y = tm->year - 1901;
- }
- d = tm->mday;
- c = y / 100;
- yadj = y - 100 * c;
- j = (c * Days400y / 4 +
- Days4y * yadj / 4 +
- (153 * m + 2)/5 + d -
- 719469);
- abs = j * Daysec;
- abs += tm->hour * 3600;
- abs += tm->min * 60;
- abs += tm->sec;
- abs -= tm->tzoff;
- return tmfill(tm, abs, tm->nsec);
-}
-
-static int
-τconv(Fmt *f)
-{
- int depth, n, w, h, m, c0, sgn, pad, off;
- char *p, *am;
- Tmfmt tf;
- Tm *tm;
-
- n = 0;
- tf = va_arg(f->args, Tmfmt);
- tm = tf.tm;
- p = tf.fmt;
- if(p == nil)
- p = Ctimefmt;
- while(*p){
- w = 1;
- pad = 0;
- while(*p == '_'){
- pad++;
- p++;
- }
- c0 = *p++;
- while(c0 && *p == c0){
- w++;
- p++;
- }
- pad += w;
- switch(c0){
- case 0:
- break;
- case 'Y':
- switch(w){
- case 1: n += fmtprint(f, "%*d", pad, tm->year + 1900); break;
- case 2: n += fmtprint(f, "%*d", pad, tm->year % 100); break;
- case 4: n += fmtprint(f, "%*d", pad, tm->year + 1900); break;
- default: goto badfmt;
- }
- break;
- case 'M':
- switch(w){
- case 1: n += fmtprint(f, "%*d", pad, tm->mon + 1); break;
- case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->mon + 1); break;
- case 3: n += fmtprint(f, "%*.3s", pad, month[tm->mon]); break;
- case 4: n += fmtprint(f, "%*s", pad, month[tm->mon]); break;
- default: goto badfmt;
- }
- break;
- case 'D':
- switch(w){
- case 1: n += fmtprint(f, "%*d", pad, tm->mday); break;
- case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->mday); break;
- default: goto badfmt;
- }
- break;
- case 'W':
- switch(w){
- case 1: n += fmtprint(f, "%*.3s", pad, wday[tm->wday]); break;
- case 2: n += fmtprint(f, "%*s", pad, wday[tm->wday]); break;
- default: goto badfmt;
- }
- break;
- case 'H':
- switch(w){
- case 1: n += fmtprint(f, "%*d", pad, tm->hour % 12); break;
- case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->hour % 12); break;
- default: goto badfmt;
- }
- break;
- case 'h':
- switch(w){
- case 1: n += fmtprint(f, "%*d", pad, tm->hour); break;
- case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->hour); break;
- default: goto badfmt;
- }
- break;
- case 'm':
- switch(w){
- case 1: n += fmtprint(f, "%*d", pad, tm->min); break;
- case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->min); break;
- default: goto badfmt;
- }
- break;
- case 's':
- switch(w){
- case 1: n += fmtprint(f, "%*d", pad, tm->sec); break;
- case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->sec); break;
- default: goto badfmt;
- }
- break;
- case 'z':
- if(w != 1)
- goto badfmt;
- case 'Z':
- sgn = (tm->tzoff < 0) ? '-' : '+';
- off = (tm->tzoff < 0) ? -tm->tzoff : tm->tzoff;
- h = off/3600;
- m = (off/60)%60;
- if(w < 3 && pad < 5)
- pad = 5;
- switch(w){
- case 1: n += fmtprint(f, "%*s%c%02d%02d", pad-5, "", sgn, h, m); break;
- case 2: n += fmtprint(f, "%*s%c%02d:%02d", pad-5, "", sgn, h, m); break;
- case 3: n += fmtprint(f, "%*s", pad, tm->zone); break;
- }
- break;
- case 'A':
- case 'a':
- if(w != 1)
- goto badfmt;
- if(c0 == 'a')
- am = (tm->hour < 12) ? "am" : "pm";
- else
- am = (tm->hour < 12) ? "AM" : "PM";
- n += fmtprint(f, "%*s", pad, am);
- break;
- case '[':
- depth = 1;
- while(*p){
- if(*p == '[')
- depth++;
- if(*p == ']')
- depth--;
- if(*p == '\\')
- p++;
- if(depth == 0)
- break;
- fmtrune(f, *p++);
- }
- if(*p++ != ']')
- goto badfmt;
- break;
- default:
- n += fmtrune(f, c0);
- break;
- }
- }
- return n;
-badfmt:
- werrstr("garbled format %s", tf.fmt);
- return -1;
-}
-
-static int
-getnum(char **ps, int maxw, int *ok)
-{
- char *s, *e;
- int n;
-
- n = 0;
- e = *ps + maxw;
- for(s = *ps; s != e && *s >= '0' && *s <= '9'; s++){
- n *= 10;
- n += *s - '0';
- }
- *ok = s != *ps;
- *ps = s;
- return n;
-}
-
-static int
-lookup(char **s, char **tab, int len, int *ok)
-{
- int nc, i;
-
- *ok = 0;
- for(i = 0; *tab; tab++){
- nc = (len != -1) ? len : strlen(*tab);
- if(cistrncmp(*s, *tab, nc) == 0){
- *s += nc;
- *ok = 1;
- return i;
- }
- i++;
- }
- *ok = 0;
- return -1;
-}
-
-Tm*
-tmparse(Tm *tm, char *fmt, char *str, Tzone *tz)
-{
- int depth, w, c0, zs, z0, z1, ampm, zoned, sloppy, tzo, ok;
- char *s, *p, *q;
- Tzone *zparsed;
- Tzabbrev *a;
- Tzoffpair *m;
-
- p = fmt;
- s = str;
- tzo = 0;
- ampm = -1;
- zoned = 0;
- zparsed = nil;
- sloppy = 0;
- /* Default all fields */
- tmtime(tm, 0, nil);
- if(*p == '~'){
- sloppy = 1;
- p++;
- }
- while(*p){
- w = 1;
- c0 = *p++;
- if(c0 == '?'){
- w = -1;
- c0 = *p++;
- }
- while(*p == c0){
- if(w != -1) w++;
- p++;
- }
- ok = 1;
- switch(c0){
- case 'Y':
- switch(w){
- case -1:
- tm->year = getnum(&s, 4, &ok);
- if(tm->year > 100) tm->year -= 1900;
- break;
- case 1: tm->year = getnum(&s, 4, &ok) - 1900; break;
- case 2: tm->year = getnum(&s, 2, &ok); break;
- case 3:
- case 4: tm->year = getnum(&s, 4, &ok) - 1900; break;
- default: goto badfmt;
- }
- break;
- case 'M':
- switch(w){
- case -1:
- tm->mon = getnum(&s, 2, &ok) - 1;
- if(!ok) tm->mon = lookup(&s, month, -1, &ok);
- if(!ok) tm->mon = lookup(&s, month, 3, &ok);
- break;
- case 1:
- case 2: tm->mon = getnum(&s, 2, &ok) - 1; break;
- case 3: tm->mon = lookup(&s, month, 3, &ok); break;
- case 4: tm->mon = lookup(&s, month, -1, &ok); break;
- default: goto badfmt;
- }
- break;
- case 'D':
- switch(w){
- case -1:
- case 1:
- case 2: tm->mday = getnum(&s, 2, &ok); break;
- default: goto badfmt;
- }
- break;
- case 'W':
- switch(w){
- case -1:
- tm->wday = lookup(&s, wday, -1, &ok);
- if(!ok) tm->wday = lookup(&s, wday, 3, &ok);
- break;
- case 1: tm->wday = lookup(&s, wday, 3, &ok); break;
- case 2: tm->wday = lookup(&s, wday, -1, &ok); break;
- default: goto badfmt;
- }
- break;
- case 'h':
- switch(w){
- case -1:
- case 1:
- case 2: tm->hour = getnum(&s, 2, &ok); break;
- default: goto badfmt;
- }
- break;
- case 'm':
- switch(w){
- case -1:
- case 1:
- case 2: tm->min = getnum(&s, 2, &ok); break;
- default: goto badfmt;
- }
- break;
- case 's':
- switch(w){
- case -1:
- case 1:
- case 2: tm->sec = getnum(&s, 2, &ok); break;
- default: goto badfmt;
- }
- break;
- case 'z':
- if(w != 1)
- goto badfmt;
- case 'Z':
- zs = 0;
- zoned = 1;
- switch(*s++){
- case '+': zs = 1; break;
- case '-': zs = -1; break;
- default: s--; break;
- }
- switch(w){
- case -1:
- case 3:
- for(a = tzabbrev; a->abbr; a++)
- if(strncmp(s, a->abbr, strlen(a->abbr)) == 0)
- break;
- if(a->abbr != nil){
- s += strlen(a->abbr);
- zparsed = tmgetzone(a->name);
- if(zparsed == nil){
- werrstr("unloadable zone %s (%s)", a->abbr, a->name);
- return nil;
- }
- break;
- }
- for(m = milabbrev; m->abbr != nil; m++)
- if(strncmp(s, m->abbr, strlen(m->abbr)) == 0)
- break;
- if(m->abbr != nil){
- snprint(tm->zone, sizeof(tm->zone), "%s", m->abbr);
- tzo = m->off;
- break;
- }
- /* fall through */
- case 1:
- /* offset: [+-]hhmm */
- q = s;
- z0 = getnum(&s, 4, &ok);
- if(s - q == 4){
- z1 = z0 % 100;
- if(z0/100 > 13 || z1 >= 60)
- goto baddate;
- tzo = zs*(3600*z0/100 + 60*z1);
- snprint(tm->zone, sizeof(tm->zone), "%c%02d%02d", zs<0?'-':'+', z0/100, z1);
- break;
- }
- if(w != -1)
- goto baddate;
- s = q;
- /* fall through */
- case 2:
- /* offset: [+-]hh:mm */
- z0 = getnum(&s, 2, &ok);
- if(*s++ != ':')
- goto baddate;
- z1 = getnum(&s, 2, &ok);
- if(z1 > 60)
- goto baddate;
- tzo = zs*(3600*z0 + 60*z1);
- snprint(tm->zone, sizeof(tm->zone), "%c%d02:%02d", zs<0?'-':'+', z0, z1);
- break;
- }
- break;
- case 'A':
- case 'a':
- if(cistrncmp(s, "am", 2) == 0)
- ampm = 0;
- else if(cistrncmp(s, "pm", 2) == 0)
- ampm = 1;
- else
- goto baddate;
- break;
- case '[':
- depth = 1;
- while(*p){
- if(*p == '[')
- depth++;
- if(*p == ']')
- depth--;
- if(*p == '\\')
- p++;
- if(depth == 0)
- break;
- if(*s == 0)
- goto baddate;
- if(*s++ != *p++)
- goto baddate;
- }
- if(*p != ']')
- goto badfmt;
- p++;
- break;
- case ',':
- case ' ':
- case '\t':
- if(*s != ' ' && *s != '\t' && *s != ',')
- goto baddate;
- while(*p == ' ' || *p == '\t' || *p == ',')
- p++;
- while(*s == ' ' || *s == '\t' || *s == ',')
- s++;
- break;
- default:
- if(*s == 0)
- goto baddate;
- if(*s++ != c0)
- goto baddate;
- break;
- }
- if(!ok)
- goto baddate;
- }
-
- if(!sloppy && ampm != -1 && tm->hour > 12)
- goto baddate;
- if(ampm == 1)
- tm->hour += 12;
- /*
- * If we're allowing sloppy date ranges,
- * we'll normalize out of range values.
- */
- if(!sloppy){
- if(tm->yday < 0 && tm->yday > 365 + isleap(tm->year + 1900))
- goto baddate;
- if(tm->wday < 0 && tm->wday > 6)
- goto baddate;
- if(tm->mon < 0 || tm->mon > 11)
- goto baddate;
- if(tm->mday < 0 || tm->mday > mdays[tm->mon])
- goto baddate;
- if(tm->hour < 0 || tm->hour > 24)
- goto baddate;
- if(tm->min < 0 || tm->min > 59)
- goto baddate;
- if(tm->sec < 0 || tm->sec > 60)
- goto baddate;
- if(tm->nsec < 0 || tm->nsec > Nsec)
- goto baddate;
- }
-
- /*
- * Normalizing gives us the local time,
- * but because we havnen't applied the
- * timezone, we think we're GMT. So, we
- * need to shift backwards. Then, we move
- * the "GMT that was local" back to local
- * time.
- */
- tmnorm(tm);
- tm->tzoff = tzo;
- if(!zoned)
- getzoneoff(tz, tm->abs, tm);
- else if(zparsed != nil)
- getzoneoff(zparsed, tm->abs, tm);
- tm->abs -= tm->tzoff;
- if(tz != nil || !zoned)
- tmtime(tm, tm->abs, tz);
- return tm;
-baddate:
- werrstr("invalid date %s near '%s'", str, s);
- return nil;
-badfmt:
- werrstr("garbled format %s near '%s'", fmt, p);
- return nil;
-}
-
-Tmfmt
-tmfmt(Tm *d, char *fmt)
-{
- return (Tmfmt){fmt, d};
-}
-
-void
-tmfmtinstall(void)
-{
- fmtinstall(L'τ', τconv);
-}
-
-/* These legacy functions need access to τconv */
-static char*
-dotmfmt(Fmt *f, ...)
-{
- static char buf[30];
- va_list ap;
-
- va_start(ap, f);
- f->runes = 0;
- f->start = buf;
- f->to = buf;
- f->stop = buf + sizeof(buf) - 1;
- f->flush = nil;
- f->farg = nil;
- f->nfmt = 0;
- f->args = ap;
- τconv(f);
- va_end(ap);
- buf[sizeof(buf) - 1] = 0;
- return buf;
-}
-
-char*
-asctime(Tm* tm)
-{
- Tmfmt tf;
- Fmt f;
-
- tf = tmfmt(tm, nil);
- return dotmfmt(&f, tf);
-}
--- a/sys/src/libc/port/mkfile
+++ b/sys/src/libc/port/mkfile
@@ -20,7 +20,6 @@
cleanname.c\
crypt.c\
ctype.c\
- date.c\
encodefmt.c\
execl.c\
exits.c\