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
This commit is contained in:
parent
8b3efcfc4e
commit
febe84af75
7 changed files with 903 additions and 1210 deletions
|
@ -314,45 +314,22 @@ extern double fmod(double, double);
|
||||||
/*
|
/*
|
||||||
* Time-of-day
|
* Time-of-day
|
||||||
*/
|
*/
|
||||||
typedef struct Tzone Tzone;
|
|
||||||
#pragma incomplete Tzone
|
|
||||||
|
|
||||||
|
|
||||||
typedef
|
typedef
|
||||||
struct Tm
|
struct Tm
|
||||||
{
|
{
|
||||||
vlong abs; /* seconds since Jan 1 1970, GMT */
|
int sec;
|
||||||
int nsec; /* nseconds (range 0...1e9) */
|
int min;
|
||||||
int sec; /* seconds (range 0..60) */
|
int hour;
|
||||||
int min; /* minutes (0..59) */
|
int mday;
|
||||||
int hour; /* hours (0..23) */
|
int mon;
|
||||||
int mday; /* day of the month (1..31) */
|
int year;
|
||||||
int mon; /* month of the year (0..11) */
|
int wday;
|
||||||
int year; /* year A.D. */
|
int yday;
|
||||||
int wday; /* day of week (0..6, Sunday = 0) */
|
char zone[4];
|
||||||
int yday; /* day of year (0..365) */
|
int tzoff;
|
||||||
char zone[16]; /* time zone name */
|
|
||||||
int tzoff; /* time zone delta from GMT */
|
|
||||||
Tzone *tz; /* time zone associated with this date */
|
|
||||||
} Tm;
|
} 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* gmtime(long);
|
||||||
extern Tm* localtime(long);
|
extern Tm* localtime(long);
|
||||||
extern char* asctime(Tm*);
|
extern char* asctime(Tm*);
|
||||||
|
|
246
sys/man/2/tmdate
246
sys/man/2/tmdate
|
@ -1,246 +0,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.
|
|
|
@ -1,37 +1,237 @@
|
||||||
#include <u.h>
|
|
||||||
#include <libc.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* seconds absolute_date ... - convert absolute_date to seconds since epoch
|
* seconds absolute_date ... - convert absolute_date to seconds since epoch
|
||||||
*/
|
*/
|
||||||
char *formats[] = {
|
|
||||||
/* asctime */
|
#include <u.h>
|
||||||
"W MMM DD hh:mm:ss ?Z YYYY",
|
#include <libc.h>
|
||||||
/* RFC5322 */
|
#include <ctype.h>
|
||||||
"?W ?DD ?MMM ?YYYY hh:mm:ss ?Z",
|
|
||||||
"?W, DD-?MM-YY hh:mm:ss ?Z",
|
typedef ulong Time;
|
||||||
/* RFC822/RFC2822 */
|
|
||||||
"DD MMM YY hh:mm ZZZ",
|
enum {
|
||||||
"DD MMM YY hh:mm Z",
|
AM, PM, HR24,
|
||||||
/* RFC850 */
|
|
||||||
"W, DD-MMM-YY hh:mm:ss MST",
|
/* token types */
|
||||||
/* RFC1123 */
|
Month = 1,
|
||||||
"WW, DD MMM YYYY hh:mm:ss ZZZ",
|
Year,
|
||||||
/* RFC1123Z */
|
Day,
|
||||||
"WW, DD MMM YYYY hh:mm:ss ZZ",
|
Timetok,
|
||||||
/* RFC3339 */
|
Tz,
|
||||||
"YYYY-MM-DD[T]hh:mm:ss[Z]ZZ",
|
Dtz,
|
||||||
"YYYY-MM-DD[T]hh:mm:ss[Z]Z",
|
Ignore,
|
||||||
"YYYY-MM-DD[T]hh:mm:ss ZZ",
|
Ampm,
|
||||||
"YYYY-MM-DD[T]hh:mm:ss Z",
|
|
||||||
/* RFC 3339 and human-readable variants */
|
Maxtok = 6, /* only this many chars are stored in datetktbl */
|
||||||
"YYYY-MM-DD hh:mm:ss",
|
Maxdateflds = 25,
|
||||||
"YYYY-MM-DD hh:mm:ss ?Z",
|
|
||||||
"YYYY-MM-DD [@] hh:mm:ss",
|
|
||||||
"YYYY-MM-DD [@] hh:mm:ss ?Z",
|
|
||||||
nil
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#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
|
static void
|
||||||
usage(void)
|
usage(void)
|
||||||
{
|
{
|
||||||
|
@ -42,31 +242,225 @@ usage(void)
|
||||||
void
|
void
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
Tm tm;
|
int i, sts;
|
||||||
char **f, *fmt;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
fmt = nil;
|
sts = 0;
|
||||||
ARGBEGIN{
|
ARGBEGIN{
|
||||||
case 'f':
|
|
||||||
fmt = EARGF(usage());
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
usage();
|
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){
|
* Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
|
||||||
if(tmparse(&tm, fmt, argv[i], nil) != nil)
|
* is WAY faster than the generic bsearch().
|
||||||
goto Found;
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (cmp < 0)
|
||||||
|
last = pos - 1;
|
||||||
|
else
|
||||||
|
base = pos + 1;
|
||||||
|
}
|
||||||
|
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 {
|
} else {
|
||||||
for(f = formats; *f != nil; f++)
|
char lowtoken[Maxtok+1];
|
||||||
if(tmparse(&tm, *f, argv[i], nil) != nil)
|
char *ltp = lowtoken, *endltp = lowtoken+Maxtok;
|
||||||
goto Found;
|
|
||||||
|
/* 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;
|
||||||
}
|
}
|
||||||
sysfatal("tmparse: %r");
|
|
||||||
Found:
|
|
||||||
print("%lld\n", tm.abs);
|
|
||||||
}
|
}
|
||||||
exits(nil);
|
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);
|
||||||
|
|
|
@ -33,34 +33,269 @@
|
||||||
#include <u.h>
|
#include <u.h>
|
||||||
#include <libc.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*
|
Tm*
|
||||||
localtime(long tim)
|
localtime(long tim)
|
||||||
{
|
{
|
||||||
static Tm tm;
|
Tm *ct;
|
||||||
Tzone *tz;
|
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*
|
Tm*
|
||||||
gmtime(long abs)
|
gmtime(long tim)
|
||||||
{
|
{
|
||||||
static Tm tm;
|
int d0, d1;
|
||||||
return tmtime(&tm, abs, nil);
|
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*
|
char*
|
||||||
ctime(long abs)
|
asctime(Tm *t)
|
||||||
{
|
{
|
||||||
Tzone *tz;
|
char *ncp;
|
||||||
Tm tm;
|
static char cbuf[30];
|
||||||
|
|
||||||
/* No error checking: the API doesn't allow it. */
|
strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n");
|
||||||
tz = tmgetzone("local");
|
ncp = &"SunMonTueWedThuFriSat"[t->wday*3];
|
||||||
tmtime(&tm, abs, tz);
|
cbuf[0] = *ncp++;
|
||||||
return asctime(&tm);
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,202 @@
|
||||||
#include <u.h>
|
#include <u.h>
|
||||||
#include <libc.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
|
long
|
||||||
tm2sec(Tm *tm)
|
tm2sec(Tm *tm)
|
||||||
{
|
{
|
||||||
tmnorm(tm);
|
long secs, *p;
|
||||||
return tm->abs;
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,859 +0,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);
|
|
||||||
}
|
|
|
@ -20,7 +20,6 @@ CFILES=\
|
||||||
cleanname.c\
|
cleanname.c\
|
||||||
crypt.c\
|
crypt.c\
|
||||||
ctype.c\
|
ctype.c\
|
||||||
date.c\
|
|
||||||
encodefmt.c\
|
encodefmt.c\
|
||||||
execl.c\
|
execl.c\
|
||||||
exits.c\
|
exits.c\
|
||||||
|
|
Loading…
Reference in a new issue