Robert Elz seismo!munnari!kre
Sun Feb 8 19:36:18 UTC 1987

That's a reasonable point, though I think most of the argument
for mktime has been as an inverter of localtime(), and there
simply using the timezone info from TZ is the right thing to do.

However, I can certainly see uses where converting from some other
timezone would be a nice ability to have - especially parsing rfc822
style Date: lines.  But I think this is a pretty specialized usage,
there aren't that many other applications where the necessary zone
information is going to be available in just the right form.

Most places where times are used with the zone included, the zone
is converted to its zone name form, and that is not an invertible
operation - there is no definable mapping from zone names to numeric
offsets, so the ability to make this transformation correctly isn't
going to happen all that often.

What's more, in cases where I simply want to convert a local time
in struct tm format to a time_t, I am not necessarily going to
know what the zone is.  Consider the use of mktime() inside date(1).
Some user types a date/time which is easy to parse into a struct tm,
from there it should be possible to use mktime() to get the time_t.
But if you have to know the zone first things aren't nearly that easy.
It certainly isn't a constant - it depends on the particular time
that the user typed.

So, I would suggest a new function to handle mktime() with a supplied
zone, which would look at extra info in the struct tm.

I have a couple of misgivings about your implementation though.
I'm not sure exactly what the point of having 2 fields for hours
and minutes is - that assumes that the offset is an even number
of minutes (no odd seconds) which while likely, isn't something
I would want to bet my life upon (I would assume that the USG people
assumed it likely that there would be no minute offsets, only even
hours when doing the standard Sys III/Sys V implementation...)
So, I would suggest a single field that contains the offset in seconds
from GMT.

Secondly, I'm not sure why you chose to have West of Greenwich being
negative.  All previous uses of timezone offsets in Unix have been
the other way - West of Greenwich positive.  I know that the ISO
standard defines it as West == negative, but its surely an arbitrary
choice,  its trivial to negate the value anywhere an ISO form
of offset is needed, and keeping this compatability with current (old)
usages (from v6 through 4.3bsd, and pwb to Sys V) certainly seems
to be a worthwhile objective.

Finally .. such a field just happens to be in the new struct tm
that is used with the latest mods I made to localtime and returned
to elsie!ado just a few days ago....  (localtime provides this information
for every time_t it converts to a srtuct tm.  I also added the zone
name in struct tm.  Both of those had been suggested before by others,
and both seemed to be clearly the right thing to do.)  

On another subject, since I'm sending this to the list...

Perverting mktime to be able to deal with out of range struct tm's
is clearly absurd.  It can't be done correctly - I don't know who
suggested this to x3j11 but I suspect that they were simply looking
at their implementation, seeing that this "seems to work" and that
it "seems to be useful" so deciding to standardise it.

I have no contact whatever with x3j11, but I suspect some of the
readers of this list do, so please ask them how they cope with
this example (change the details to be more relevant in the US
if you prefer).

According to the rationale, I'm supposed to be able to add an hour
to a struct tm, and convert that to a time_t, to produce the
corresponding effect to the time_t.  For that to make any sense
at all, I must also be able to subtract an hour from a struct tm
and have mktime convert that to the corresponding time_t.

Consider tm.{tm_year = 86, tm_mon = 9 /*October*/, tm_mday = 19,
		tm_hour = 1, tm_min=30, tm_sec = 0}

That just happens to be 30 minutes before daylight saving switched
on here (Australia) this summer.

now consider	tm.tm_hour++;
		t = mktime(&tm);
		tm = *localtime(&t);
		t = mktime(&tm);
		tm = *localtime(&t);

What is supposed to happen here exactly?  X3J11 have (apparently) 
defined mktime() to work even when the time_t is out of range, so
the first call to mktime() must succeed.  Beats me what value it returns,
but there had better be one.  Now since localtime() and mktime()
are supposed to be inverses, the first call to localtime should
simply have the effect of "normalizing" the struct tm after the
addition, it will represent the value mktime() returned.  In any
rational system of arithmetic, subtracting 1 has the effect of
reversing a previous addition of 1, so the final value of tm
above should be the same thing as it was before we started.
(As long as we haven't ever gone outside the range of legal values
of course).  Any other result is too absurd to consider.

Does anyone here want to find me a version of mktime() that
will actually achieve this?

I would kind of suspect that the result of the first localtime
is going to be 03:30 (which is one hour elapsed time more than
01:30 where we started, which is what the aim of all this is).
Subtract an hour from that, and you get 02:30 in the tm, which
is the exact same value that was there when an hour was added to
the 01:30 we started with.  How is mktime() supposed to distinguish
those two cases?  But the second time its supposed to resolve the
illegal time the other way and give 01:30.  Some hope!

Any rational mktime() would define the first call above to be an
error, since the time handed to it does not (never did, never will)
exist.  Doing that means that the values have gone out of range,
and arithmetic no longer needs to behave normally, so everything is OK.

Its probably possible to play similar games with the times around
when daylight saving switches off.

Trying to do arithmetic on non-continuous domains (is this the
right word?) isn't productive, and struct tm's aren't continuous.

Clearly the right thing to do is to do arithmetic on time_t's
which are nicely continuous.

I'm not sure exactly what's going on here, but I suspect that x3j11
are allowing for the possibility that a time_t is not an integral
type represnting seconds since the epoch.  Is that right?  I suppose
that they do realize that changing that is going to break just about
every program using time_t's written in C anywhere?  Though that doesn't
seem to have bothered x3j11 much anywhere else.  I really do wish that
posix would completely cut itself away from that abortion - not depend
on it anywhere.


More information about the tz mailing list