9front - general discussion about 9front
 help / color / mirror / Atom feed
* Date and time handling.
@ 2020-04-08  3:56 ori
  2020-04-08  6:48 ` [9front] " Steve Simon
  2020-04-12 18:27 ` magma698hfsp273p9f
  0 siblings, 2 replies; 9+ messages in thread
From: ori @ 2020-04-08  3:56 UTC (permalink / raw)
  To: 9front

Hi,

Date handling on 9front is almost adequate today if you don't
have to parse dates or deal with timezones, and don't do
multithreading. Otherwise, it's difficult to get right, and
we often don't.

We've got a crappy home-rolled date parser in seconds(1),
a few in the upas source tree to deal with mail formats,
and git9 has a few hacks around this as well.

Out of tree, joe9 has been trying to write code that takes
stock information in one timezone and moves them to another,
and our APIs there are completely inadequate.

So, I tried to write a library that is adequate, without
being complicated.

It allows working with times in multiple timezones at the
same time. It is thread safe, including timezone loading.
It allows parsing and formatting arbitrary formats. It lets
you adjust the day and figure out the timeshift-shift
adjusted time.

Eventually, when it's ready (not yet), I'd like to put this
into libc. The API additions are small, and all of our curent
time APIs can be implemented trivially in terms of this api.
At worst, I'd like to add the field additions from struct tmd
to it.

There are a few known bugs -- not all fields of the time struct
are filled in yet, for example.

The code is here:

	https://git.eigenstate.org/ori/date.git

The formatted draft of the manpage is below, for comments and
thoughts:

     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; /* milliseconds 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 */
          };

          Tm   *tmnow(Tm *tm, char *tz);
          Tm   *tmtime(Tm *tm, vlong abs, char *tz);
          Tm   *tmstime(Tm *tm, vlong sec, char *tz);
          Tm   *tmshiftzone(Tm *dst, Tm *src, char *tz);
          Tm   *tmparse(Tm *dst, char *fmt, char *tm);
          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 represented as strings.  They can be provided
          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 name, any instant-dependent adjust-
          ments 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.

          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 format argument takes the form of a special
          date string, with the following components:

          1,Jan,January
               Represents the month in numeric, short, or long form,
               respectively.

          2    The day of month.

          3,Mon,Monday
               The day of week in numeric, short, or long form,
               respectively.

          3,15 The hour in 12 or 24 hour time, respectively.

          4    The minute.

          5    The second.

          6,2006
               The year in 2 and 4 digit forms, respectively.

          7,MST
               The time zone offset in numeric or abbreviated form.
               As a special case, 7:00 will fill in both the hour and
               minutes in the timezone.

          _    Pads the following field with spaces.  0 Pads the fol-
               lowing field with zeroes.

          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 nbuf-1 char-
          actters are written into buf, which is nul-terminated.

          Tmrecalc takes a manually adjusted Tm structure, and recal-
          culates the absolute time from it.  This recalculation
          respects the time zone stored in struct tm.  It also takes
          out of range values and wraps them around, simplifying cal-
          culations

          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, nil, "Sun Nov 2 13:11:11 PST 2019");
           tm.day++;
           tmrecalc(&t);
           print("%T", &t); /*  Mon Nov 3 13:11:11 PST 2019 */



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [9front] Date and time handling.
  2020-04-08  3:56 Date and time handling ori
@ 2020-04-08  6:48 ` Steve Simon
  2020-04-09 14:12   ` ori
  2020-04-12  6:20   ` ori
  2020-04-12 18:27 ` magma698hfsp273p9f
  1 sibling, 2 replies; 9+ messages in thread
From: Steve Simon @ 2020-04-08  6:48 UTC (permalink / raw)
  To: 9front

well done!

i tried to do this years ago whilst writing an ical parser - and gave up in horror.

-Steve


> On 8 Apr 2020, at 5:57 am, ori@eigenstate.org wrote:
> 
> Hi,
> 
> Date handling on 9front is almost adequate today if you don't
> have to parse dates or deal with timezones, and don't do
> multithreading. Otherwise, it's difficult to get right, and
> we often don't.
> 
> We've got a crappy home-rolled date parser in seconds(1),
> a few in the upas source tree to deal with mail formats,
> and git9 has a few hacks around this as well.
> 
> Out of tree, joe9 has been trying to write code that takes
> stock information in one timezone and moves them to another,
> and our APIs there are completely inadequate.
> 
> So, I tried to write a library that is adequate, without
> being complicated.
> 
> It allows working with times in multiple timezones at the
> same time. It is thread safe, including timezone loading.
> It allows parsing and formatting arbitrary formats. It lets
> you adjust the day and figure out the timeshift-shift
> adjusted time.
> 
> Eventually, when it's ready (not yet), I'd like to put this
> into libc. The API additions are small, and all of our curent
> time APIs can be implemented trivially in terms of this api.
> At worst, I'd like to add the field additions from struct tmd
> to it.
> 
> There are a few known bugs -- not all fields of the time struct
> are filled in yet, for example.
> 
> The code is here:
> 
>    https://git.eigenstate.org/ori/date.git
> 
> The formatted draft of the manpage is below, for comments and
> thoughts:
> 
>     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; /* milliseconds 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 */
>          };
> 
>          Tm   *tmnow(Tm *tm, char *tz);
>          Tm   *tmtime(Tm *tm, vlong abs, char *tz);
>          Tm   *tmstime(Tm *tm, vlong sec, char *tz);
>          Tm   *tmshiftzone(Tm *dst, Tm *src, char *tz);
>          Tm   *tmparse(Tm *dst, char *fmt, char *tm);
>          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 represented as strings.  They can be provided
>          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 name, any instant-dependent adjust-
>          ments 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.
> 
>          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 format argument takes the form of a special
>          date string, with the following components:
> 
>          1,Jan,January
>               Represents the month in numeric, short, or long form,
>               respectively.
> 
>          2    The day of month.
> 
>          3,Mon,Monday
>               The day of week in numeric, short, or long form,
>               respectively.
> 
>          3,15 The hour in 12 or 24 hour time, respectively.
> 
>          4    The minute.
> 
>          5    The second.
> 
>          6,2006
>               The year in 2 and 4 digit forms, respectively.
> 
>          7,MST
>               The time zone offset in numeric or abbreviated form.
>               As a special case, 7:00 will fill in both the hour and
>               minutes in the timezone.
> 
>          _    Pads the following field with spaces.  0 Pads the fol-
>               lowing field with zeroes.
> 
>          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 nbuf-1 char-
>          actters are written into buf, which is nul-terminated.
> 
>          Tmrecalc takes a manually adjusted Tm structure, and recal-
>          culates the absolute time from it.  This recalculation
>          respects the time zone stored in struct tm.  It also takes
>          out of range values and wraps them around, simplifying cal-
>          culations
> 
>          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, nil, "Sun Nov 2 13:11:11 PST 2019");
>           tm.day++;
>           tmrecalc(&t);
>           print("%T", &t); /*  Mon Nov 3 13:11:11 PST 2019 */



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [9front] Date and time handling.
  2020-04-08  6:48 ` [9front] " Steve Simon
@ 2020-04-09 14:12   ` ori
  2020-04-12  6:20   ` ori
  1 sibling, 0 replies; 9+ messages in thread
From: ori @ 2020-04-09 14:12 UTC (permalink / raw)
  To: steve, 9front

> well done!
> 
> i tried to do this years ago whilst writing an ical parser - and gave up in horror.
> 
> -Steve
> 

I should probably be absolutely clear: the manpage is aspirational, not
documentational, though I think the hard parts are done or close.

One thing that will annoy me: Currently, you need to specify the timezone
as the filename, eg, "US_Eastern", rather than as the well known name,
eg, "EST". There are two reasons for this.

1) We have no way to map from a timezone to the name, other than 
   opening each timezone file and reading the first line.

2) The timezone name that we store in those files is wrong. We only
   have 3 characters for the timezone, so "Australia_Tasmania" becomes
   EST instead of AEST.

I can hard-code a mapping, but that kind of sucks. I'd like to parse
the IANA timezone database and generate files instead, either with a
separate 'tzmap' file, or with duplicates for the abbreviated zones
that we can open directly.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [9front] Date and time handling.
  2020-04-08  6:48 ` [9front] " Steve Simon
  2020-04-09 14:12   ` ori
@ 2020-04-12  6:20   ` ori
  1 sibling, 0 replies; 9+ messages in thread
From: ori @ 2020-04-12  6:20 UTC (permalink / raw)
  To: steve, 9front

> 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 */



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Date and time handling.
  2020-04-08  3:56 Date and time handling ori
  2020-04-08  6:48 ` [9front] " Steve Simon
@ 2020-04-12 18:27 ` magma698hfsp273p9f
  2020-04-13  0:36   ` [9front] " ori
  1 sibling, 1 reply; 9+ messages in thread
From: magma698hfsp273p9f @ 2020-04-12 18:27 UTC (permalink / raw)
  To: 9front

ori@eigenstate.org writes:

>                 vlong    abs; /* milliseconds since Jan 1 1970, GMT */

Do you really mean "GMT" or "UTC"?

>                 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) */

The "sec" field should probably be 0..60, to allow for leap seconds.
Why do you specify the month as 0..11, rather than 1..12?

>                 int tzoff;    /* time   zone delta from GMT */

Is the value in tzoff specified in seconds?  Does a negative value mean
east or west of GMT?

>           Time zones are represented as strings.  They can be provided
>           as the abbreviated timezone name, the full timezone name,
>           the path to a timezone file, or an absolute offset in the
>           HHMM form.

Is that HHMM allowed to have a leading +/- sign?

>           Tmtime converts the millisecond-resolution timestamp 'abs'
>           into a Tm struct in the requested timezone.

If abs has millisecond resolution, and secs resolution of one second,
then where in struct Tm do the fractional seconds go?

>           6,2006
>                The year in 2 and 4 digit forms, respectively.

(2006 - 1900) != 6

>           ISO-8601,RFC-3339 and RFC-2822 dates.

It would also be nice to support ISO-8601 without the "T" separator
between the date and time, which looks ugly and confuses people who
haven't read the standard.  I often use a space or "@" sign for improved
readability, even though it's technically not ISO.

>           Tmfmt formats a Tm struct according to the format fmt. If
>           fmt is nil, we format as in ctime(2). At most nbuf-1 char-
>           actters are written into buf, which is nul-terminated.

Does that (nbuf-1) count include the terminating NUL?

>            if(a.abs == b.abs)
>                 print("same0);
>            else
>                 print("different0);

These are missing closing quotation marks.

Also, how does the library handle dates prior to the Gregorian
Reformation?

It's nice to have input on a man page before it gets printed and bound.
:) Thanks!


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [9front] Re: Date and time handling.
  2020-04-12 18:27 ` magma698hfsp273p9f
@ 2020-04-13  0:36   ` ori
  2020-04-16 20:40     ` ori
  0 siblings, 1 reply; 9+ messages in thread
From: ori @ 2020-04-13  0:36 UTC (permalink / raw)
  To: magma698hfsp273p9f, 9front

Thanks for the review. Good questions and comments!

> ori@eigenstate.org writes:
> 
>>                 vlong    abs; /* milliseconds since Jan 1 1970, GMT */
> 
> Do you really mean "GMT" or "UTC"?

I mean /adm/timezone/GMT. If we renamed it to UTC or TAI, I'd be happy
with that too. Also, bug in comment: should be seconds.

>>                 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) */
> 
> The "sec" field should probably be 0..60, to allow for leap seconds.
> Why do you specify the month as 0..11, rather than 1..12?

Agreed. I'll change the comment for sec, though right now I
dont' think our timezone files know how to represent leap seconds.

Some fields are (IMO, unexpectedly) 0 based because this is intended to
be merged with  struct Tm eventaully. That means keeping the unfortunate
definitions compatible.
 
>>                 int tzoff;    /* time   zone delta from GMT */
> 
> Is the value in tzoff specified in seconds?  Does a negative value mean
> east or west of GMT?

Seconds, west is negative, as in /adm/timezone/*

>>           Time zones are represented as strings.  They can be provided
>>           as the abbreviated timezone name, the full timezone name,
>>           the path to a timezone file, or an absolute offset in the
>>           HHMM form.
> 
> Is that HHMM allowed to have a leading +/- sign?

Not yet implemented, but that'd make sense.
 
>>           Tmtime converts the millisecond-resolution timestamp 'abs'
>>           into a Tm struct in the requested timezone.
> 
> If abs has millisecond resolution, and secs resolution of one second,
> then where in struct Tm do the fractional seconds go?
>

abs :)

But for now, I've changed resolution to seconds. I'd be willing to change
it to anything else.

>>           6,2006
>>                The year in 2 and 4 digit forms, respectively.
> 
> (2006 - 1900) != 6

Yep, but that's how Go specifies it. Anyways, I've completely changed
the formatting. Now we have YY, YYYY for those two formats.
 
>>           ISO-8601,RFC-3339 and RFC-2822 dates.
> 
> It would also be nice to support ISO-8601 without the "T" separator
> between the date and time, which looks ugly and confuses people who
> haven't read the standard.  I often use a space or "@" sign for improved
> readability, even though it's technically not ISO.

Another change: I'm making the time parsing explicit. I think that the list
of reasonable formats cam vary by caller, and if it turns out that it's
duplicated a lot, we can revisit this decision easily.

>>           Tmfmt formats a Tm struct according to the format fmt. If
>>           fmt is nil, we format as in ctime(2). At most nbuf-1 char-
>>           actters are written into buf, which is nul-terminated.
> 
> Does that (nbuf-1) count include the terminating NUL?

No. I think I've rephrased this in the manpage already -- and, actually,
just moved to format strings entirely. Now, you'd do something like:

	snprint(buf, sizeof(buf), "%τ", tmfmt(&tm, "YYYY MMM DD hh:mm:ss");

> 
>>            if(a.abs == b.abs)
>>                 print("same0);
>>            else
>>                 print("different0);
> 
> These are missing closing quotation marks.
> 
> Also, how does the library handle dates prior to the Gregorian
> Reformation?

This library only deals with proleptic gregorian time. Which is
jargon for "Get bent".

> It's nice to have input on a man page before it gets printed and bound.
> :) Thanks!

It's nice to avoid making easy mistakes.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [9front] Re: Date and time handling.
  2020-04-13  0:36   ` [9front] " ori
@ 2020-04-16 20:40     ` ori
  2020-05-04  4:08       ` ori
  0 siblings, 1 reply; 9+ messages in thread
From: ori @ 2020-04-16 20:40 UTC (permalink / raw)
  To: ori, magma698hfsp273p9f, 9front

> Thanks for the review. Good questions and comments!
> 

And thanks to joe9 for adding a bunch of test cases, which
now all pass. I think the code is ready for testing.

The next step, if there aren't any objections, will be to
merge the `struct Tmd` with libc's `struct Tm`, and move
this library over to use it.

Once we've got some experience and are happy with this, I'd
like to commit this to 9front and move utilities like date
over to it.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [9front] Re: Date and time handling.
  2020-04-16 20:40     ` ori
@ 2020-05-04  4:08       ` ori
  2020-05-05 17:29         ` magma698hfsp273p9f
  0 siblings, 1 reply; 9+ messages in thread
From: ori @ 2020-05-04  4:08 UTC (permalink / raw)
  To: ori, magma698hfsp273p9f, 9front

>> Thanks for the review. Good questions and comments!
>> 
> 
> And thanks to joe9 for adding a bunch of test cases, which
> now all pass. I think the code is ready for testing.
> 
> The next step, if there aren't any objections, will be to
> merge the `struct Tmd` with libc's `struct Tm`, and move
> this library over to use it.
> 
> Once we've got some experience and are happy with this, I'd
> like to commit this to 9front and move utilities like date
> over to it.

I started rewriting seconds(1), trying to loop together a
mass of permutations of date formats, and decided I hate it.

I think we should do two things with seconds:

1) Pick a common list of date formats we should try,
   one after the other.

2) add a '-f' option (same for date) that will accept a libdate
   format, instead of looping through them all and trying them
   one by one.

What formats do people use with seconds? I'd like to propose
the following, cribbed from Go's constant formats, but with
the addition of RFC5322

asctime:
	WWW MMM DD hh:mm:ss YYYY
RFC5322:
    Note: there are a lot of variants, and I'm considering
	      adding a '?' modifier  to indicate fuzzy parsing,
	      where '1, 01, Jan, January' would all match '?M'

	WWW DD MMM YYYY hh:mm:ss Z
	WWW DD MMMM YYYY hh:mm:ss Z
	WWWW DD MMM YYYY hh:mm:ss Z
	WWWW DD MMMM YYYY hh:mm:ss Z
	WWW, DD-MM-YY hh:mm:ss Z
	WWW, DD-MM-YY hh:mm:ss Z
	WWW, DD-MMM-YY hh:mm:ss Z
	WWW, DD-MMMM-YY hh:mm:ss Z
	WWWW, DD-MM-YY hh:mm:ss Z
	WWWW, DD-MM-YY hh:mm:ss Z
	WWWW, DD-MMM-YY hh:mm:ss Z
	WWWW, DD-MMMM-YY hh:mm:ss Z
RFC822/RFC8222:
	DD MMM YY hh:mm ZZZ
	DD MMM YY hh:mm Z

RFC850:
	WW, DD-MMM-06 hh:mm:ss MST
RFC1123:
	WWW, DD MMM YYYY hh:mm:ss ZZZ
RFC1123Z:
	WWW, DD MMM YYYY hh:mm:ss ZZ
RFC3339:
	YYYY-01-DDThh:mm:ss[Z]ZZ



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [9front] Re: Date and time handling.
  2020-05-04  4:08       ` ori
@ 2020-05-05 17:29         ` magma698hfsp273p9f
  0 siblings, 0 replies; 9+ messages in thread
From: magma698hfsp273p9f @ 2020-05-05 17:29 UTC (permalink / raw)
  To: 9front

ori@eigenstate.org writes:

> One thing that will annoy me: Currently, you need to specify the timezone
> as the filename, eg, "US_Eastern", rather than as the well known name,
> eg, "EST". There are two reasons for this.
>
> 1) We have no way to map from a timezone to the name, other than 
>    opening each timezone file and reading the first line.
>
> 2) The timezone name that we store in those files is wrong. We only
>    have 3 characters for the timezone, so "Australia_Tasmania" becomes
>    EST instead of AEST.
>
> I can hard-code a mapping, but that kind of sucks. I'd like to parse
> the IANA timezone database and generate files instead, either with a
> separate 'tzmap' file, or with duplicates for the abbreviated zones
> that we can open directly.

# hm... why not use ndb?  file=... to split the db among multiple files
tzfs -f /ndb/timezone /srv/tzfs
mount /srv/tzfs /adm/timezone

> RFC3339:
> 	YYYY-01-DDThh:mm:ss[Z]ZZ

More human-readable variants of RFC3339:

YYYY-MM-DD hh:mm:ss
YYYY-MM-DD hh:mm:ss Z
YYYY-MM-DD hh:mm:ss z
YYYY-MM-DD hh:mm:ss 0
YYYY-MM-DD hh:mm:ss +0400
YYYY-MM-DD hh:mm:ss -0400
YYYY-MM-DD hh:mm:ss +5
YYYY-MM-DD hh:mm:ss -0
YYYY-MM-DD @ hh:mm:ss
YYYY-MM-DD @ hh:mm:ss +830
YYYY-MM-DD @ hh:mm:ss -0315
YYYY-MM-DD @ hh:mm:ss -3
YYYY-MM-DD @ hh:mm:ss +0
YYYY-MM-DD @ hh:mm:ss 0000



^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2020-05-05 20:03 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-08  3:56 Date and time handling ori
2020-04-08  6:48 ` [9front] " Steve Simon
2020-04-09 14:12   ` ori
2020-04-12  6:20   ` ori
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

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