summaryrefslogtreecommitdiff
path: root/sys/man/2
diff options
context:
space:
mode:
authorOri Bernstein <ori@eigenstate.org>2020-08-09 18:58:44 -0700
committerOri Bernstein <ori@eigenstate.org>2020-08-09 18:58:44 -0700
commit56e869ac7004a635a7d63596ee751275484f28c3 (patch)
tree6abb2068c51ed8d44b7abebe7f633952cedcb92e /sys/man/2
parent3ba1d83d2026ebac616ab17a2126df97c0a7a24c (diff)
libc: new date apis
The current date and time APIs on Plan 9 are not good. They're inflexible, non-threadsafe, and don't expose timezone information. This commit adds new time APIs that allow parsing arbitrary dates, work from multiple threads, and can handle timezones effectively.
Diffstat (limited to 'sys/man/2')
-rw-r--r--sys/man/2/tmdate275
1 files changed, 275 insertions, 0 deletions
diff --git a/sys/man/2/tmdate b/sys/man/2/tmdate
new file mode 100644
index 000000000..0b314a100
--- /dev/null
+++ b/sys/man/2/tmdate
@@ -0,0 +1,275 @@
+.TH TMDATE 2
+.SH NAME
+tmnow, tzload, 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 {
+ 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 *tzload(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, char **ep);
+vlong 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.
+.PP
+Time zones are loaded by 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
+Tzload 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 point at which the parsing stopped is returned in
+.IR ep .
+If
+.I ep
+is nil, trailing garbage is ignored.
+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, WWW
+The day of week in numeric, 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 t, tt
+The milliseconds in unpadded and padded form, respectively.
+.B u, uu, uuu, uuuu
+The microseconds in unpadded. padded form modulo milliseconds,
+or unpadded, padded forms of the complete value, respectively.
+.B n, nn, nnn, nnnn, nnnnn, nnnnnn
+The nanoseconds in unpadded and padded form modulo milliseconds,
+the unpadded and padded form modulo microseconds,
+and the unpadded and padded complete value, respectively.
+.TP
+.B Z, ZZ, ZZZ
+The timezone in [+-]HHMM and [+-]HH:MM, and named 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. When
+For example,
+.I __h
+will format to a width of 3. When parsing, this acts as whitespace.
+.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, RFC3339 and RFC2822 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 normalizes it,
+returning the absolute timestamp that the date represents.
+Normalizing recomputes the
+.I year, mon, mday, hr, min, sec
+and
+.I tzoff
+fields.
+If
+.I tz
+is non-nil, then
+.I tzoff
+will be recomputed, taking into account daylight savings
+for the absolute time.
+The values not used in the computation are recomputed for
+the resulting absolute time.
+All out of range values are wrapped.
+For example, December 32 will roll over to Jan 1 of the
+following year.
+.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 = tzload("local") == nil)
+ sysfatal("load zone: %r");
+if((zp = tzload("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.
+Done with full, strict error checking.
+
+.IP
+.EX
+Tm a, b;
+
+if(tmparse(&a, nil, "Tue Dec 10 12:36:00 PST 2019", &e) == nil)
+ sysfatal("failed to parse: %r");
+if(*e != '\0')
+ sysfatal("trailing junk %s", e);
+if(tmparse(&b, nil, "Tue Dec 10 15:36:00 EST 2019", &e) == nil)
+ sysfata("failed to parse: %r");
+if(*e != '\0')
+ sysfatal("trailing junk %s", e);
+if(tmnorm(a) == tmnorm(b) && a.nsec == b.nsec)
+ 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 = tzload("local")) == nil)
+ sysfatal("load zone: %r");
+if((zp = tzload("US_Pacific")) == nil)
+ sysfatal("load zone: %r");
+if(tmnow(&here, zl) == nil)
+ sysfatal("get time: %r");
+if(tmtime(&there, tmnorm(&tm), zp) == nil)
+ sysfatal("shift time: %r");
+.EE
+
+.PP
+Add a day. Because cross daylight savings, only 23 hours are added.
+
+.EX
+Tm t;
+char *date = "Sun Nov 2 13:11:11 PST 2019";
+if(tmparse(&t, "W MMM D hh:mm:ss z YYYY, date, &e) == nil)
+ print("failed top parse");
+tm.day++;
+tmnorm(&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.