plan9fox/sys/man/2/tmdate
Ori Bernstein 8b3efcfc4e libc, seconds: new time and date apis (try 2)
Redo date handling in libc almost entirely. This allows
handling dates and times from outside your timezones,
fixes timezone loading in multithreaded applications,
and allows parsing and formatting using custom format
strings.

As a test of the APIs, we replace the formatting code in
seconds(1), shrinking it massively.

The last commit missed a few removals, and made it
unnecessarily hard to do an update.
2020-06-14 09:33:32 -07:00

247 lines
6.4 KiB
Plaintext

.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.