From: ori@eigenstate.org
To: steve@quintile.net, 9front@9front.org
Subject: Re: [9front] Date and time handling.
Date: Sat, 11 Apr 2020 23:20:01 -0700 [thread overview]
Message-ID: <62BBC3C1416503E1E58923A464814EE0@eigenstate.org> (raw)
In-Reply-To: <43B494B5-78E6-4967-ACE5-289ADD02CD2F@quintile.net>
> well done!
>
> i tried to do this years ago whilst writing an ical parser - and gave up in horror.
>
> -Steve
>
>
>>
>> The formatted draft of the manpage is below, for comments and
>> thoughts:
>>
After some experimentation and discussion, the API was tweaked a bit.
First, the formatting code was rewritten. Instead of using Go style
format strings, I ended up going with moment.js[1] style format strings,
with some tweaks and simplifications. There were too many edge cases
in parsing the date and guessing at what the numbers meant for an
elegant parser.
Second, loading the timezone was made explicit. There were a number
of errors that were easily ignored when loading invalid timezones,
and timezone naming is a huge mess -- so making loading the timezone
explicit. So, now you'd write:
if((local = tmgetzone("local")) == nil)
sysfatal("get zone: %r\n");
if(tmtime(&tm, 1586218416ll, local) == nil)
sysfatal("now: %r");
tmfmt(buf, sizeof(buf), "YYYY/MM/DD hh:mm:ss", &tm);
The updated manpage is below:
TMDATE(2) TMDATE(2)
NAME
tmnow, tmtime, tmstime, tmshiftzone, tmparse, tmfmt, tmnorm,
- convert date and time
SYNOPSIS
#include <u.h>
#include <libc.h>
typedef struct Tmd Tmd;
struct {
vlong abs; /* seconds since Jan 1 1970, GMT */
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; /* year A.D. - 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 */
};
Tzone *tmgetzone(char *name);
Tm *tmnow(Tm *tm, char *tz);
Tm *tmtime(Tm *tm, vlong abs, Tzone *tz);
Tm *tmstime(Tm *tm, vlong sec, Tzone *tz);
Tm *tmshiftzone(Tm *dst, Tm *src, Tzone *tz);
Tm *tmparse(char *fmt, char *tm, Tzone *zone, Tm *dst);
int tmfmt(char *buf, usize nbuf, char *fmt, Tm *tm);
void tmnorm(Tm *tm);
void tmfmtinstall(char *fmt);
DESCRIPTION
This family of functions handles simple date and time manpu-
lation. Times are represented as an absolute instant in
time, combined with a time zone.
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.
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.
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.
Tmnow gets the current time of day in the requested time
zone.
Tmtime converts the millisecond-resolution timestamp 'abs'
into a Tm struct in the requested timezone.
Tmstime is identical to tmtime, but accepts the time in sec-
onds.
Tmshiftzone moves a time from one timezone to another, doing
the appropriate conversions.
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, then we tzshift to the
local timezone.
The format argument takes contains zero or more of the fol-
lowing components:
Y, YY, YYYY
Represents the year. YY prints the year in 2 digit
form.
M, MM, MMM, MMMM
The month of the year, in unpadded numeric, padded
numeric, short name, or long name, respectively.
D, DD
The day of month in unpadded or padded numeric form,
respectively.
W, WW
The day of week in short or long name form, respec-
tively.
h, hh
The hour in unpadded or padded form, respectively
m, mm
The minute in unpadded or padded form, respectively
s, ss
The second in unpadded or padded form, respectively
z, Z, ZZ
The timezone in named, [+-]HHMM and [+-]HH:MM form,
respectively
a, A Lower and uppercase 'am' and 'pm' specifiers, respec-
tively.
[...]
Quoted text, copied directly to the output.
Any characters not specified above are copied directly to
output, without modification.
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.
Tmfmt formats a Tm struct according to the format fmt. If
fmt is nil, we format as in ctime(2). At most characters are
written into buf, including the terminator. The format is
identical to tmparse.
When parsing, any amount of whitespace is treated as a sin-
gle token. All string matches are case insensitive, and
zero padding is optional.
Tmrecalc takes a manually adjusted Tm structure, and recal-
culates the absolute time from the year, mon, mday, hr, min
and sec fields. Other fields are ignored. This recalcula-
tion respects the time zone stored in struct tm. Out of
range values are wrapped. For example, December 32nd becomes
January 1st.
Tmfmtinstall installs a time format specifier %T. The time
format behaves as in tmfmt
Examples
All examples assume tmfmtinstall has been called.
Get the current date in the local timezone, GMT, and
US_Pacific time. Print it using the default format.
Tm t;
print("local: %T0, tmnow(&t, "local"));
print("gmt: %T0, tmnow(&t, "GMT"));
print("eastern: %T0, tmnow(&t, "US_Pacific"));
Compare if two times are the same, regardless of timezone.
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("same0);
else
print("different0);
Add a day to two times. Because we picked daylight savings
time to adjust over, only 23 hours are added.
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", &t); /* Mon Nov 3 13:11:11 PST 2019 */
next prev parent reply other threads:[~2020-04-12 6:20 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-04-08 3:56 ori
2020-04-08 6:48 ` [9front] " Steve Simon
2020-04-09 14:12 ` ori
2020-04-12 6:20 ` ori [this message]
2020-04-12 18:27 ` magma698hfsp273p9f
2020-04-13 0:36 ` [9front] " ori
2020-04-16 20:40 ` ori
2020-05-04 4:08 ` ori
2020-05-05 17:29 ` magma698hfsp273p9f
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=62BBC3C1416503E1E58923A464814EE0@eigenstate.org \
--to=ori@eigenstate.org \
--cc=9front@9front.org \
--cc=steve@quintile.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).