std-unix@ut-sally.UUCP (07/18/86)
[ This is part of RFC.001 and is a reposting of V5N65. -mod ]
Date: 02 Mar 86 05:47:32 +1100 (Sun)
From: Robert Elz <munnari!kre@SEISMO.CSS.GOV>
>Subject: localtime(), ctime() and timezones
It seems to me that this discussion is getting a bit overblown,
as far as P1003 is concerned, it doesn't really seem to be
as difficult or complex as some people are making out.
So, I'm going to propose something that could be inserted into
P1003 (with the obvious extra definitions that I'm going to
leave out on the assumption that everyone knows what they are,
like the definition of a "struct tm").
In some words of other, it would go like this (with hopefully
a lot of cleaning up of the typography to get rid of quotes
and things like that where I would really prefer to use italics
or bold):
Implementations shall provide the following functions:
struct tm *gmttime(t) time_t *t;
struct tm *localtime(t) time_t *t;
int settz(p) char *p;
char *asctime(tp) struct tm *tp;
char *ctime(t) time_t *t;
gmttime: converts the time_t "*t" to a "struct tm" representing
the same time (in Universal Co-ordinated Time). (waffle about
the returned value being in a static area, etc, goes here).
localtime: converts the time_t "*t" to a "struct tm" representing
the given time adjusted to represent some local time difference.
"local time" will be specified by a call to "settz", if no such
call has preceded the call to localtime(), localtime() will call
"settz(getenv("TZ"));". Implementors should note that for any defined
past time (from midnight January 1, 1970 until the time the call is made)
the local time returned should be accurate (taking into account the effects
of daylight saving, if any). For future times, the local time returned
should be as likely to be accurate as current projections of
future timezone rules and daylight saving time changes allow.
settz: enables users to specify the local time conversion to be
used by localtime. The string is an implementation specific
representation of the timezone offset desired, with 2 special
cases.. The null pointer (t == (char *)0) will always select
the appropriate local time offset for the host executing the call.
A null string (t != (char *)0 && *t == '\0') will select
no local time transformations (making localtime() equivalent
to gmttime()). Implementations should provide, and document,
some mechanism to allow users to select another timezone.
This mechanism is beyond the scope of the standard. Implementors
should, if possible, allow users to define their own timezones,
and not restrict them to use one of some standard set.
If settz is called with an unrecognisable argument, the effect
is implementation defined. (Users might expect any of three
"reasonable"? actions could be taken here -- use GMT, use local time,
or use local time in the area where the implementation was performed).
settz returns 0 if the timezone selected could be obtained, and
-1 otherwise. settz can be called as many times as needed, each
call affects future calls of localtime, until another call to settz.
acstime: returns a 25 character string representing the time
specified by "*tp". The format of the string is ... (you all know it).
ctime: is defined to be "asctime(localtime(t))".
...................
Notes: this is (about) the right level of detail for the standard.
There is no need to specify what the form of the argument to
settz() is. This enables things like the Sys V "EST5EDT" string,
and Arthur Olson's (elsie!ado) "localtime" "Eastern" etc, to all
be used with impunity - the implementor gets to choose whatever
is appropriate to him - just provided that he can satisfy the
needs of his customers (implementors who provide no means of getting
daylight saving right in the Southern hemisphere can probably
expect not to sell many copies there - but that's their choice).
In particular - this discourages programmers from writing programs
which "know" what the local time should be - there's no reason at
all why a program should ever need to do more than select GMT,
host local time, or a user specified time zone. (nb: while localtime
uses the TZ environment variable in cases where the program has made
no call to settz(), there's nothing to stop a program getting the
argument to settz() from anywhere it pleases, including from several
different environment variables if it chooses, and needs to operate
in several timezones, or from an external configuration file, or
wherever is appropriate).
This works for existing programs (in general) - localtime() performs
the required call to settz() the first time it is called (directly
or via ctime()). There's no need to worry about who sets TZ, if
its not set, getenv("TZ") will return (char *)0 and settz() will
then use the appropriate local time for the host. How settz()
gets that information is an implementation matter. The security
problems (people faking local time for programs that expect it
to be host local time, by setting TZ before running the program)
can easily solved by causing those (comparatively few) programs
to do "settz((char *)0)" before their first call to localtime().
What's missing: So far here there is no mention of the "timezone name".
None of the standard mechanisms is really adequate here. The V7
(and 4.xbsd) "timezone" function is clearly inadequate (although
4.2 bsd allows users to set the environment variable TZNAME to anything
they like) since there can clearly be several possible names for the
same offset, and "timezone" has no way to discover which one is wanted.
Requiring the name to resice in the environment somewhere (Sys V) is also
inadequate (there are too many problems about making sure it is set
in all the right places).
Arthur Olson's scheme causes "localtime" to set a global variable
"tz_abbr" to the correct string name for the timezone just used.
I can't think of any cases where anything more than this is needed,
but it is less flexible then "timezone()" and it would require
programs that currently call timezone() to have to be altered.
(He also has his version of "ctime" (but not "asctime") include
the timezone in its output - I doubt if that is feasible for P1003,
too many existing programs know what every byte in the returned
string from ctime() contains.)
I solicit suggestions for what to do here - one might be to
retain "timezone" but not require that it be capable of returning
anything except the zone name corresponding to the last call of
localtime() - then with ado's implementation it could simply
ignore its arguments and return tz_abbr - I suspect that would
satisfy all existing uses (and the ones it doesn't are quite
likely not to work in general anyway). Opinions?
There's also no discussion of how this relates to processes
and images. Not because there's anything doubtful here,
but just because the necessary words take a lot of space.
(informally, "the first call to localtime" is intended to
be "the first after the program is exec'd, ignoring any
fork()'s it may have since performed, as long as there
has been no subsequent exec). Getting this kind of thing
right is essential for a standatds document, its not essential
here.
...................
A justification for all this ... Today, just about 2 1/2 hours ago
(it's early on a Sun morning as I write this) the daylight saving
rules changed in at least 2 Australian states (no-one really seems
very sure of what happened, or really why). The politicians gave
us less than a month's warning that it was coming (and the month
was February, which isn't a long month...).
If there's anyone who doesn't believe that some form of dynamic
timezone setting is required, they're welcome to come to Australia
and suffer our local politicians (this isn't the first time: a
couple of years ago New South Wales decided to extend daylight
saving for a month to try and save on power bills - the amount of
notice given was about the same - at that time, at least one local
site decided to scrap running on GMT and run on localtime (ala VMS)
instead. They're still doing that, I think, and suffering because
of it).
I'm extremely grateful that Arthur Olson decided to try an implementation,
and donate it to the community - he made the task of converting things here
much easier than it otherwise would have been. His implementation
meets the above specs (in fact, it inspired them...), and will work
for all the contorted exampes that people have been proposing (multiple
shifts in a year, multi-hour saving, even daylight wasting).
But note - there's no need for the standard to require this
generality, market pressures will do that - all the standard
needs to do is supply a suitable interface. Arthur Olson's
implementation proves that the above reccomendation is
implementable (munnari is running his code, in libc, right now)
and effecient enough.
[ Your last sentence gives the reason that I've encouraged
discussions of implementations in the newsgroup: it's good
to know that a proposed standard is implementable and handles
actual cases. But you're right, of course, that the
P1003 standard doesn't need implementation details. -mod ]
Jack Jansen's (mcvax!jack) somewhat similar, but slightly different scheme
would probably work just as well.
Bob Devine's (hao!asgb!devine) "I don't think its needed" attitude
can also fit the standard - if he's right then he's probably going
to have a very fast localtime() which will serve him well.
If he's wrong, then he's not going to get many customers.
That's good - the more the better - that gives us users (or us system
implementors perhaps) a wide choice of methods.
Robert Elz kre%munnari.oz@seismo.css.gov seismo!munnari!kre
Volume-Number: Volume 6, Number 31
std-unix@ut-sally.UUCP (Moderator, John Quarterman) (07/19/86)
From: ll-xn!s3sun!sdcsvax!ncr-sd!greg.@topaz.UUCP Date: Fri, 18 Jul 86 15:47:39 PDT Organization: NCR Corporation, Rancho Bernardo Some comments about the timezone proposal to be submitted to P1003. This is mostly nitpicking, but there are some loose ends that such a proposal will need to specify. I will comment from Elz's text; the text of the proposal to P1003 follows this wording closely. In article <5352@ut-sally.UUCP> Robert Elz writes: >localtime: converts the time_t "*t" to a "struct tm" representing >the given time adjusted to represent some local time difference. >"local time" will be specified by a call to "settz", if no such >call has preceded the call to localtime(), localtime() will call >"settz(getenv("TZ"));". ........ Note that this implies that there must be some way for the implementation to tell (a) if settz has been previously called, and presumably, (b) what value was provided by settz. This information should be part of the interface. It is easy to imagine a utility logging subroutine that would want to use the local time when inserting log entries without disturbing other time-display calculations (the user might be running in a different time zone), so this logging subroutine will need to be able to set and potentially reset the time zone information. [ Perhaps. The assumption is that a process will either use the same variety of localtime throughout, or that it will explicitly set the kind it wants with settz before using localtime. That still leaves the question of how localtime knows settz has been used previously, but as long as it does, it's not clear that an application writer needs to know how it's done. -mod ] >If settz is called with an unrecognisable argument, the effect >is implementation defined. (Users might expect any of three >"reasonable"? actions could be taken here -- use GMT, use local time, >or use local time in the area where the implementation was performed). Since I have been bitten too many times by having the default time zone be that of the implementers, I feel that option three is unreasonable. Presumably, since an attempt was made to set the time zone to a non-local value, using GMT as a canonical non-local time zone is probably reasonable (for everybody except those in England, of course -- perhaps it should be called UCT when in this mode so as not to use the same abbreviation). [ This is an example of something you'll find throughout 1003.1: an attempt to not outlaw existing behavior of existing systems. If option three were not included (ugly as it is), I doubt the committee would be able to reach consensus on including settz. -mod ] >What's missing: So far here there is no mention of the "timezone name". >None of the standard mechanisms is really adequate here. ...... >Arthur Olson's scheme causes "localtime" to set a global variable >"tz_abbr" to the correct string name for the timezone just used. I propose an extension of the System V mechanism. That interface defines "extern char *tzname[2]" for the time zone names and has a field in the tm struct (tm_isdst) that is non-zero if daylight savings is in effect; i.e., the current timezone name is tzname[tm.tm_isdst != 0]. I propose that the standard add "extern char *tzname[]" (note that the length is not specified; the bound would be implementation-defined) and have wording that says that tzname[tm.tm_isdst] is the name of the relevant timezone. Since the current System V implementation only sets tm_isdst to zero or one, this is actually backward compatible. (In fact, I just looked through our System V sources for uses of tzname; most of the uses are of the latter form rather than the former, so this proposal is even more compatible than it looks.) >....[problems simulating BSDs "timezone()" function] - one might be to >retain "timezone" but not require that it be capable of returning >anything except the zone name corresponding to the last call of >localtime() ....... With the above proposal, "timezone()" would return values selected from the tzname array if the time zone was one covered by the last settz(), or otherwise return a string of the form "+-hhmm". This function probably should not be part of the standard, since it is primarily present for backward compatibility. If it is present, it should be depreciated so that it can go away in the future. And while we're on backward compatibility, the SysV function tzset() could be defined as "if(timezone_not_set) settz(getenv("TZ");" to be compatible with the way it currently works; again, if this function is defined, its usage should be depreciated. [ I don't think tzset is in the standard, but that's a useful implementation note. -mod ] System V also defines external variables for the current timezone and daylight savings rule. Are there any programs that actually use these? Should they be part of the interface as well? (Or some equivalent functionality?) -- -- Greg Noel, NCR Rancho Bernardo Greg@ncr-sd.UUCP or Greg@nosc.ARPA Volume-Number: Volume 6, Number 34
std-unix@ut-sally.UUCP (Moderator, John Quarterman) (07/21/86)
Date: Mon, 21 Jul 86 01:23:29 edt From: im4u!caip!mark@cbosgd.ATT.COM (Mark Horton) Organization: AT&T Bell Laboratories, Columbus >System V also defines external variables for the current timezone and daylight >savings rule. Are there any programs that actually use these? Should they be >part of the interface as well? (Or some equivalent functionality?) Yes, there's an important use. If you're generating an RFC 822 compatible >Date: line, you need to know the local offset from GMT or the time zone name. You can't just plug in the time zone name, because only the names in the USA are allowed by 822, and if you try to extend that to the rest of the world, you run into ambiguities. In general, you can't assume that someone in an arbitrary location in the world will understand your local name for your time zone. So you have to generate a zone like "+hhmm". One might even claim that, in a zone like Japan, asking for the time zone name should return "+0900" rather than "JST". Probably not, but it's a thought. Mark Volume-Number: Volume 6, Number 35
std-unix@ut-sally.UUCP (Moderator, John Quarterman) (07/23/86)
Date: 23 Jul 86 08:38:34 +1000 (Wed) From: Robert Elz <munnari!kre@SEISMO.CSS.GOV> I have (more than) a few comments about some comments about the timezone proposal. [ There's only one comment from the moderator in this article: discussions are very useful, but to get in the standard it needs exact wording. In other words, anyone wanting changes to the proposal form I posted should supply *exact wording changes*. (An alternative would be to submit a complete new proposal.) -mod ] Quotes until stated otherwise are from Greg Noel (ncr-sd!greg)... > Note that this implies that there must be some way for the implementation > to tell (a) if settz has been previously called, and presumably, (b) what > value was provided by settz. I agree, I hadn't considered this. However, its essential that when there's an interface that sets some static state, there be some means to determine the current value of that state - I've been frustrated so many times by hardware with write only registers that I should have seen this myself. But, now after thinking about it for a few days, I'm not sure how it should be done. There would seem to be two quite different, but equally possible approaches, though neither is ideal. One would be for settz() to save the arg string it is called with, and for a new function (gettz() ?) to return it. That sounds simple enough, but unfortunately might be something of an implementation headache. The string can be of arbitrary length, so no static buffer in settz to hold it would be appropriate. That means that settz would be required to malloc() memory to hold this string, just on the off chance that it might be needed later (which it very rarely will be). I really have very limited enthusiasm for making library routines at this level get involved in storage allocation. (Settz could presumably just remember the pointer to the string it was passed, and gettz could return that pointer - but this has so many potential problems that its not worth contemplating). The (an?) other implementation would be to define two functions, perhaps savetz() and restoretz(). Savetz would return (in some manner not important here) the internal state that settz has established. restoretz() would restablish that state as settz would have left it. This might be handled by having savetz copy the state into a buffer supplied by the caller, or perhaps it would malloc some memory and return a pointer to that. Malloc here is not a problem, as its only being done by the specific request of the user, its not a hidden side effect. Of the two schemes, I think I prefer the latter, but I would appreciate comments from readers, either to the list if you (and the moderator) think there will be general interest in your comments, or in mail to me. I think John Quarterman (our friendly moderator) answered your query about the "implementors timezone" default possibility for settz. I might just add that I can't imagine how a new implementation could conceivably make that choice - its just there to cope with old code. To implement this proposal, an implementation must be able to obtain both the hosts local time, and GMT without being told anything externally (ie: by being handed either (char *)0 or "" resp). If it can do that, it can also easily choose one of those as the default in cases where it is given an invalid argument. > I propose an extension of the System V mechanism.... I propose > that the standard add "extern char *tzname[]" ... and have wording > that says that tzname[tm.tm_isdst] is the name of the relevant timezone. Yes, this would be nice, unfortunately it can't work. You are assuming that there is just one non-DST timezone name, and all the others are names of various DST types. This just isn't true in general. Since tm_isdst must be zero for any tm_* that is not a daylight savings time representation, your scheme would allow only one non-DST zone name. A new field could be added to struct tm to be the tzname[] index, but this would break all existing code that wants zone names, and if we're going to do that, perhaps we should look and see exactly when a zone name is required. To the best of my knowledge, the only sensible use of a timezone name is to associate with a human visible date/time representation. Since these things aren't unique, they are useless to hold any internal representation of a timezone. In effect, that means that the only time a timezone name will (should) ever be needed is when a date/time has been converted for external output, and the zone that will be wanted will be the zone of the time that was just converted. If anyone has a counter-example, I would be pleased to learn of it. Given this assumption, it seems that all that's needed is for localtime() to arrange that the relevant zone name is available somehow, either in an external "char *" variable, or as the return value of a function. For Sys V compatability, and implementation could provide char *tzname[2] and set both pointers to the zone name needed? Is anyone aware of any code this would break? For v7 conpatability, the timezone(3) function could be made to ignore its args, and just return the selected zone name. > And while we're on backward compatibility, the SysV function tzset() could > be defined as "if(timezone_not_set) settz(getenv("TZ");" to be compatible > with the way it currently works; again, if this function is defined, its > usage should be depreciated. Certainly, AT&T, and anyone who wants to be compatible with current Sys V programs should provide tzset as indicated, and should make it, as far as possible (note "tzname" difficulties as mentioned above) compatible with the functionality of the Sys V tzset. However, including definitions in the standard, along with wording like "don't use this, it shouldn't be here, and is going away" seems ludicrous to me. > System V also defines external variables for the current timezone and daylight > savings rule. I don't know about daylight savings rule, I don't remember that one, and my Sys V manual seems to have walked away, but "timezone" is impossible to get right. There's no doubt that its intended usage is that tzset() should set this variable to the local standard timezone offset. But this assumes that there is such a thing as a "standard timezone offset" that applies for all times in the zone. This just isn't true.. Eg: a couple of years ago now, Singapore (I think) changed its timezone so it would be the same as Malaysia. What value should be put in "timezone" for Singapore? The correct answer depends on what time is being converted - if its a time before the switch, then it should be their old zone, for one after, it should be the new zone. That is, its not a constant! Following quotes are from Mark Horton (cbosgd!mark)... [about uses of "timezone"] > Yes, there's an important use. If you're generating an RFC 822 compatible > Date: line, you need to know the local offset from GMT... Nonsense! This isn't a use of the Sys V "timezone" variable at all. That's not the information it provides. What you need for an RFC822 Date: line is the numeric offset of the time that was converted. That's not necessarily the hosts local time zone (which is what "timezone" is supposed to contain). And even in cases where host local time is what is wanted, "timezone" isn't it - as the local time converted might have been a daylight savings time. To turn the Sys V "timezone" into the correct thing in that case, one would need to imbed some nonsense like "if (tm->tm_isdst) timezone += 60;" (or maybe -= 60, and maybe the number is 3600, details are irrelevant. And no, I wouldn't really modify "timezone"). Getting rid of assumptions about things like DST being one hour forwards is one of the major aims of all this. What *is* needed is a method to obtain the timezone offset that is appropriate for a given time. That's a different problem entirely. An interface to provide this information might be valuable. If there isn't such an interface, then the offset can easily calculated in a portable manner by applications (see my posting to mod.sources, vol 6 issue 12 "datediffs" for an example of doing approximately this). > One might even claim that, in a zone like Japan, asking for the time zone > name should return "+0900" rather than "JST". Probably not, but it's > a thought. This was, of course, a joke (and not even a good one). Robert Elz seismo!munnari!kre kre%munnari.oz@seismo.css.gov Volume-Number: Volume 6, Number 36