|Anonymous | Login||2023-10-02 01:34 UTC|
|Main | My View | View Issues | Change Log | Docs|
|Viewing Issue Simple Details|
|ID||Category||Severity||Type||Date Submitted||Last Update|
|0001614||[1003.1(2016/18)/Issue7+TC2] System Interfaces||Objection||Omission||2022-11-03 13:36||2023-09-04 10:28|
|Priority||normal||Resolution||Accepted As Marked|
|Final Accepted Text||See Note: 0006402.|
|Summary||0001614: XSH 3/mktime does not specify EINVAL and should|
First, it is possible this is a duplicate bug report, if this issue
has already (or is to be already) handled by some other bug, and this
report doesn't add anything new, then please simply link this to that
as a duplicate, and then close this.
It is possible for the broken down local time passed to mktime() to
represent a time that never existed (eg: a time in the gap (most commonly
hour) when summer time commences, and the clock skips forward from
NN:59 to PP:00 (usually, gaps shorter than an hour exist, but for our
purposes here, as an example, that's not important) where PP==(NN+2)%24
and times MM:xx (usually for all xx) where MM==(NN+1)%24 simply never
existed (with appropriate adjustments to tm_mday, etc, if the %24 makes
any difference to the answer in each case).
If one specifies an input time of MM:20 that simply cannot be converted
to a time_t, it kind of represents NN:59 + 21 mionutes, except that local
time is really PP:20 not MM:20.
Note that tm_isdst does not help here, as the time simply does not exist,
it cannot be either summer or standard (or standard or winter as appropriate)
time. It is possible for an implementation to use tm_isdst as a hint
to what happened so cases like this work
timeptr = localtime(&t);
timeptr->tm_min += 20;
t = mktime(timeptr);
where if tm_isdst should have changed value to represent the new time
calculated, but clearly here does not (and since timeptr came from
localtime, we know tm_isdst is 0 or 1) then the implementation might be
able to guess what was intended.
That does not always work however, there can be gaps in times for reasons
other than the tm_isdst changing seasonal adjustment, such as when a locality
decides to switch from one zone offset to another (eg: sometime, I am too
lazy to look up when, Singapore and Malaysia switched from UTC+0700 to
UTC+0800 (to align their clocks with Hong Kong, which was apparently
considered important - at least for Singapore). Neither used summer time,
before or after than change, tm_isdst is 0 in both zones - but there was
an hour there that never existed.
Similarly, when seasonal time ends, and time jumps backwards, there is an
hour (most commonly) of local time when the time runs twice. If one specifies
a time which is in (one of) those periods, along with a tm_isdst = -1, then
it is impossible to determine which time_t value should apply - EINVAL is
returned in that case.
Note that here, tm_isdst is used to handle this overlap when it is caused
by seasonal adjustments - but just as with the gap, that only works when
the duplicated time are for that reason, if Malaysia decided (which would be
odd, indeed, but could happen) that having their clocks match Thailand,
much of Indonesia (including Jakarta), and Laos and Cambodia, rather than
Singapore, they could decide to jump back to UTC+0700 by running one hour
twice - with tm_isdst==0 in both occurrences.
There is another way that (since bug 1533 was applied) could be considered
reasonable to handle the ambiguous case - one could use the value of tm_gmtoff
to determine which of the possible time_t's to return. This even handles
(never seen that I am aware of, and very unlikely to ever happen) cases
where a time runs more than twice, which tm_isdst cannot do.
It appears very tempting to make use of that to resolve this problem, but
I would strongly advise against it. Doing so would break current conforming
applications (which simple addition of the fields does not) as they currently
do not, and cannot (since the tm_gmtoff field does not appear in the existing
standard - it is not even as of the date of this report in a published draft
of an upcoming version of the standard) set that field, its value from such
an application will be unitialised (or perhaps 0), if mktime() were to attempt
to reference it, undefined behaviour might result. That's unacceptable.
It is not even OK to permit implementations to use tm_gmtoff by making its
use for this purpose unspecified, for the same reason - any implementation
that does risks undefined behaviour from a currently conforming application.
mktime() must not be permitted to reference that field (in the incoming
structure) at all.
An error code is required to handle this invalid or ambiguous input.
EINVAL is the usual one.
Between lines 44331 and 44332 (of I7 TC2) add:
[EINVAL] The local time specified represents a local time which
either did not exist in the past, or is not expected to exist
in the future, or the local time specified represents a
local time which existed, or is expected to exist in the
future, more than once, and the value supplied in tm_isdst
is unable to resolve the ambiguity.
It might also be (assuming that you all agree with my reasoning above)
a good idea to add something in the Rationale (line 44359, currently "None")
explaining why tm_gmtoff is not (at least now) considered appropriate to
use to resolve the ambiguous case. I will leave it up to you to determine
whether it would be worth adding a Future Direction indicating that that
might be changed at some future time (ie: advising applications to ensure
that they start setting tm_gmtoff before calling mktime() - it will currently
be ignored, but one day, might be essential in this odd case).
I'm sure this has been discussed at least once before, but on the mailing list not in Mantis. When the specified time does not exist because it is in the gap created by the change to daylight saving time and tm_isdst is -1, mktime() is not allowed to fail. It must treat the time either as standard time or as daylight time and return the corresponding value. (The value of tm_isdst on output distinguishes the two.) Likewise when daylight saving time ends and times are ambiguous when tm_isdst is -1, mktime() must choose to treat it either as standard time or as daylight time. These cases are tested by one of the UNIX certification test suites.
If you know of an implementation where mktime() returns -1 under these conditions, please report it as a bug to the maintainers.
Cases such as Singapore and Malaysia moving to a different time zone are another matter, and I suppose mktime() could legitimately return -1. However, this would only happen with a TZ that specifies a location (rather than "std offset dst offset, rule"), and the behaviour of all functions that use TZ is implementation-defined in that case anyway, so I don't see the need to add an EINVAL error to the standard for this.
> I'm sure this has been discussed at least once before, but on the mailing list not in Mantis. When the specified time does not exist because it is in the gap created by the change to daylight saving time and tm_isdst is -1, mktime() is not allowed to fail.
C does allow this to fail; this has been confirmed at <https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_136.html>. [^] It is valid for POSIX to require specific behaviour in areas that the C standard leaves unspecified, but I see nothing in the CX-marked additions in POSIX specifies the behaviour for this case.
edited on: 2022-11-03 20:48
Re Note: 0006022
I'm sure this has been discussed at least once before,
I thought I remembered it as well, but did not find it, but if it was just
on the list that would explain it. But that's why I included the first
paragraph of the description.
When the specified time does not exist because it is in the gap created
by the change to daylight saving time and tm_isdst is -1, mktime() is not
allowed to fail.
That makes no sense at all. In the ambiguous case, I guess things could
be interpreted that way (the time runs twice), and in that case certainly
the tm_isdst value in the result can distinguish which was chosen (that is,
the implementation picks one if the application did not bother), but I do
not see anything in XSH 3/mktime which actually requires that to happen.
Certainly if the user sets tm_isdst to 0 or 1 in this case, then mktime()
must not fail, but return the appropriate value, but for the -1 case, all
I see in the standard is:
A negative value for tm_isdst shall cause mktime( ) to attempt to
determine whether Daylight Savings Time is in effect for the
Nothing there about it being required to pick one. Note that text I
proposed adding does not prohibit an implementation from picking one and
returning that, merely allows EINVAL if
the value supplied in tm_isdst is unable to resolve the ambiguity.
If an implementation chooses to always pick one or the other (or even roll
the die, and randomly return one or the other) that's fine, in that case it
used the -1 input and used that to resolve the ambiguity, but I see nothing
which requires that kind of result.
But for the gap, the period where the local time specified never existed
at all, no result (except -1 with errno set) makes any sense at all. The
whole idea of mktime() is that localtime() applied to the time_t returned
will produce the same struct tm as that left in *timeptr my mktime() after
it has completed. The two are inverses of each other (to the extent that
EOVERFLOW does not apply, in one direction or the other, anyway). For a
broken down time (a struct tm) passed to mktime() which gives a value that
localtime() can never produce is simply broken. As best I can tell the
standard reference implementation of mktime() acts exactly like this.
Lastly, I have no sympathy at all for an argument by which POSIX deliberately
says that many places in the world don't need to be handled by the standard
TZ definition, and that anything that allows their time zone specification to
correctly translate their times is beyond the standard, and anything is
permitted, is simply outrageous (though this is not the place to debate XBD
8.3: TZ). But if things happen as they are looking like they might, this kind
of issue is likely to occur in the US, and Europe as well, if seasonal
variations are abandoned, and localities are permitted to pick which zone
they will remain in as their standard time - when that transition happens,
and it might not be far away, well before Issue9 of POSIX can possibly appear,
this exact problem will occur in places that the POSIX committee people cannot
simply dismiss as "we don't care what happens in that part of the world",
which seems to be the attitude now.
I meant to add in the previous note (but this is more than I think should
be done by just amending that), that an option (not one I believe correct,
but if it seems to be) would be that rather than adding EINVAL to the "shall
fail" case, at least in the ambiguous input (time runs twice) scenario,
that could be added as a "may fail" section following the current "shall fail".
For the "time in the never existing gap" case (which exists in all zones
which have summer time (also stupidly known as "daylight savings") no matter
what form they use to specify the TZ setting, there really is no option,
the only thing mktime() can do in that case is return an error, nothing else
is correct, that needs to be a "shall fail".
edited on: 2022-11-04 10:01
Re Note: 0006025 Yes, POSIX is more strict than C here. The CX shaded text that matters is:
A negative value for tm_isdst shall cause mktime() to attempt to determine whether Daylight Savings Time is in effect for the specified time.(C has something similar, but in a non-normative footnote.)
As I understand it, this requires that if mktime() determines that DST is in effect, it returns the same result as it would for tm_isdst=1, otherwise it returns the same result as it would for tm_isdst=0. That is how all certified UNIX and POSIX systems behave. Until now I thought it was how all mktime() implementations behave, but I see that the C DR that you cite mentions "Arthur David Olson's popular ``tz'' time zone software" as returning -1. That was back in 1994, so I wonder if it still does or if it has changed to behave like other implementations.
Re Note: 0006022
> But for the gap, the period where the local time specified never existed
> at all, no result (except -1 with errno set) makes any sense at all. The
> whole idea of mktime() is that localtime() applied to the time_t returned
> will produce the same struct tm as that left in *timeptr my mktime() after
> it has completed. The two are inverses of each other
If you just feed in a struct tm obtained from localtime(), or any equivalent source of valid date and time info, obviously the "gap" times will never occur. What you are missing here is that an important feature of mktime() is that you can do calculations using the tm members. That is the reason it specifically allows out-of-range values.
If an application uses mktime() to answer the question "What will the date and time be a month from now" (meaning when the wall clock time is the same as it is now, but on the day that is a month from now) by incrementing tm_mon and setting tm_isdst=-1, it is not helpful to get back the answer "that time won't exist". There are probably a very large number of applications that do this kind of thing that will break if ported from an implementation where mktime() does not return -1 to one where it does (if any exist). Worse still, they will only break under rare circumstances, so the problem could go unnoticed for years, decades even, before biting one day.
If we did decide to allow mktime() to return -1, ask yourself how would an application handle this error? The vast majority of applications will want to obtain a time they can use, rather than reporting an error; if that time is adjusted by an hour because it happens to occur right at a DST change, that's all well and good. So the obvious way for them to handle the error would be to repeat the mktime() call with tm_isdst=0 (or 1). This will produce the same result that the standard currently requires. So all that allowing this error will achieve is to force most application writers to add another mktime() call in order to get the same result they would have got before. If an application actually wants to verify whether a given time exists, it can so do simply by checking whether the tm_hour and tm_min values it gets back are the same ones it fed in. This is actually a more reliable method than assuming that a return of -1 with errno EINVAL indicates a non-existent time would be (if we allow it), because EINVAL could be caused for other reasons as well (e.g. an invalid TZ value).
> For a broken down time (a struct tm) passed to mktime() which gives
> a value that localtime() can never produce is simply broken.
I'm having trouble parsing that. If you are suggesting that passing the return value of mktime() to localtime() could produce different struct tm member values than those returned by mktime(), then that can never happen, since mktime() is required to set them the same way localtime() does.
> As best I can tell the standard reference implementation of mktime()
> acts exactly like this
What implementation do you believe to be a reference implementation? The function was invented by the ANSI C committee. As far as I'm aware they did not produce a reference implementation. In the absence of an implementation by the function's inventors, all implementations have equal standing and none is a reference implementation.
Actually, that's not quite true, because POSIX has additional requirements beyond the C standard, so implementations that only claim conformance to C and not POSIX have no standing in relation to the additional POSIX requirements.
> I have no sympathy at all for an argument by which POSIX deliberately
> says that many places in the world don't need to be handled by the
> standard TZ definition, and that anything that allows their time zone
> specification to correctly translate their times is beyond the standard
Perhaps we should add the Area/Location TZ format to POSIX (with no details, just a reference to another standard, maybe RFC 6557).
Re Note: 0006030
> For the "time in the never existing gap" case [...] there really is
> no option, the only thing mktime() can do in that case is return an
> error, nothing else is correct, that needs to be a "shall fail".
This would make every certified implementation non-conforming, along with most if not all implementations that claim POSIX conformance but aren't certified, and would break many applications. There is absolutely no way this change would achieve consensus.
edited on: 2022-11-04 17:43
Re Note: 0006031
I know this:
A negative value for tm_isdst shall cause mktime() to attempt to determine
whether Daylight Savings Time is in effect for the specified time.
and what your note says about it, but note that it says "attempt to determine"
not "determine" which it would not say if it were guaranteed to be able to
work (and I don't mean anything related to the unpredictability of what
might happen into the indefinite future, all we can possibly do is assume
that the current rules apply forever forward, until we discover differently).
If that attempt fails, what do you contend that mktime() should do?
What you are missing here is that an important feature of mktime()
is that you can do calculations using the tm members.
I don't think I was missing that, if you look back at the description
section, you'll see that it includes an example...
It is possible for an implementation to use tm_isdst as a hint
to what happened so cases like this work
timeptr = localtime(&t);
timeptr->tm_min += 20;
t = mktime(timeptr);
and if you believe that is a requirement, then I don't object to that,
but I'd like a pointer to where in the standard this is stated as required.
But why would any code do, as you postulate:
incrementing tm_mon and setting tm_isdst=-1,
why the "setting tm_isdst=-1" part? What would be the point?
If tm_isdst remained as localtime set it, then the implementation
can use that when normalising the numbers, to deduce what happened
if no time_t for the time can be produced, and further push the
time through the gap (altering tm_hour). But if we don't know what
tm_isdst applied before, how is the implementation supposed to
determine whether the tm passed to mktime() was produced by an
addition (time moving forwards) as in both your example, and mine,
or by a subtraction (looking for an earlier time). The expected
result in the two cases will be different (when we are crossing the
Paul Eggert's C query (from 1994) referenced by Harald in Note: 0006025
suggests that the test suites look for either of two answers, as long as
the implementation produces one of them, all is good. Surely you aren't
going to defend that as a practice? "We don't care whether the answer
you give is correct or meaningful, just as long as you give an answer"?
Really? Surely an error response is always better than an incorrect
answer passed off as being correct?
There are probably a very large number of applications that do this kind
of thing that will break if ported from an implementation where mktime()
does not return -1 to one where it does (if any exist).
If there is any implementation where mktime() cannot return -1, it is
already non-compliant -- the standard is quite clear that mktime()
shall return -1 (with errno == EOVERFLOW) in some cases. Any application
that doesn't deal with a -1 return from mktime() is already defective,
whatever happens here.
What the application should do when it gets a -1 I cannot possibly
say - it entirely depends upon the needs of the application, but it
certainly needs to deal with the possibility (or just hope it never
happens, which would be a behaviour that would not need to be changed).
So the obvious way for them to handle the error would be to repeat the
mktime() call with tm_isdst=0 (or 1). This will produce the same result
that the standard currently requires.
The question would be why they did not do that the first time? Why are
you imagining the tm_isdst=-1 being there? Note that even this cannot
handle all the cases (it certainly doesn't help with EOVERFLOW) ?
I am not going to debate application code writer's possible behaviours,
we don't have enough info to discuss that - using EINVAL to signal
"in the gap" as a message to them was never the purpose. Rather it
is a necessity for any implementation which seeks to provide only correct
answers, rather than "if we do this we pass the validation test, so that's
what we do, regardless of how stupid it is". And yes, implementers sometimes
do act just like that, under pressure.
Do you not see the incongruity of these two comments from your note:
If we did decide to allow mktime() to return -1,
EINVAL could be caused for other reasons as well (e.g. an invalid TZ value).
The second accepts that (even though it is not in the mktime() page,
mktime() can already return -1 with error == EINVAL.
But given all of that, note that tzset() is not actually allowed to
return an error, what happens with an invalid TZ is apparently unspecified,
but tzset() is apparently required to do something in that case, and not
return an error. So, I doubt that your "EINVAL could be caused for other
reasons as well" is actually the case (not that it matters here, a -1
return from mktime() is possible whatever happens as a result of this
issue, including nothing.
But hadn't you already decided that mktime() can return -1 ?? (and
even with errno==EINVAL)
I'm having trouble parsing that.
Sorry, that happens sometimes.
If you are suggesting that passing the return value of mktime() to
localtime() could produce different struct tm member values than those
returned by mktime(), then that can never happen, since mktime() is
required to set them the same way localtime() does.
what I meant is exactly what you say there is required. And I agree.
But mktime() only gets to adjust the input struct tm values (according
to what is written in the specification:
the other components shall be set to represent the specified time
since the Epoch, but with their values forced to the ranges indicated
in the <time.h> entry;
That is, the "other components" (which means all of the relevant ones,
just not tm_wday and tm_yday which are irrelevant here) are set to
represent the specified time since the Epoch (that is: the time specified
by the caller of mktime()) but with any out of range values (according
to what is specified in <time.h>) adjusted so that are in range (and while
it does not say so, and probably should, I would interpret that to also
mean not having 31 days in November, even though 31 is within the range
permitted for tm_mday in <time.h>) but it doesn't say that they can be
adjusted for any other reason.
If we take Paul's example from C defect report #136 (the 1994 issue cited
by Harald in Note: 0006025) and the application create a struct tm, exactly
as he described it, what justification exists to allow mktime() to alter
any of the fields of the struct tm (except tm_isdst, as a -1 input is never
in range as an output value).
I see none. If the result is not -1, the resulting tm should be (for
the specified fields, other than tm_isdst) be identical to the one supplied.
Given that, if mktime() does not return -1 (if it does, what is in the
tm seems unspecified to me) then we have a case where mktime is (apparently)
required to leave the struct tm in a state which is something that localtime()
can never return - violating the requirement that we both agree should exist.
On the other hand, if it does return -1, which the C committee apparently
agree it can (and yes, I know that posix can make stricter requirements
than exist in C ... though I don't see anything in the spec for mktime()
which does so - only apparently in the broken validation suites) then
this is not an issue.
What implementation do you believe to be a reference implementation?
I meant the one referenced in that defect report, which is (I believe) by
far the most widely used implementation (though often with minor variations)
existing at the minute, and is public domain. I believe that android
and probably macos both use it, I think all the BSDs use it, and linux.
Some of the commercial OSs use it as well.
The function was invented by the ANSI C committee.
That is news to me, but I cannot dispute it, as I have no idea of
its origins. I do have vague memories of it in the very ancient
past though. The earliest BSD implementation (taken from the ado
code) I can find is in 4.3 Reno (1990) - but it must have existed
for quite a while before that, as that contains a version of mktime()
based upon one I had done earlier -- using an idea from Bob Kridle, when he
was still at UC Berkeley, Computer Centre -- the time_t desired is
determined by doing a binary search over the range of a time_t (works
for any possible time_t type - including floats) calling localtime on each,
comparing its resulting struct tm with the (already normalised) input
struct tm (and adjusting the upper or lower bound of the search as
appropriate) until equality is found.
When it becomes impossible to subdivide the range smaller (we'd need to
be producing sub-second intervals) if no match has been found, the tm
originally requested, simply cannot exist, and an error happens.
When I did my initial implementation of that, which was adopted (modified
to suit other requirements) in ado's implementation, I was replacing an
earlier algorithmic implementation (kind of like that in XBD 4.16 - but of
course, not nearly as simple as that makes it seem). I moved away from
unix work in the latter 1980's (87 or so) and more into networking and
the IETF for the rest of the century (before that environment became far
too political and commercial after the explosion of the internet in the
late 1990's). In any case, that would mean that there must have been at
least one freely available mktime() implementation around in the mid 1980's.
When did the C committee apparently invent it?
Aside: the advantage of the binary search algorithm is certainly not speed,
it is slower than an algorithmic implementation, usually a lot slower, but
it removes any knowledge of time peculiarities from mktime() - works
whatever (arithmetic) type time_t happens to be, and always guarantees
that one requirement that we agree on, locatime(mktime(tm)) == tm (not that
that is anything like valid C, but you know what I am getting at, and the
error case is being ignored there - that's only for valid time_t returns).
It also works (without adjustment) in environments using UTC, rather than
POSIX time_t (ie: where leap seconds are not simply imagined out of
existence), not that that aspect is relevant here.
While still considering this general point, also note that Paul's C defect
message, also includes this sentence:
The ANSI C Rationale (corresponding to subclause 22.214.171.124) clearly lets
mktime yield -1 in the ``fall-backward fold'' that will occur when the
clock is turned back from 01:59:59 to 01:00:00 on October 30, 1994.
That's the case I have less problem avoiding a -1 result from, as while
it is ambiguous there which time_t is intended, either one will generate
the struct tm that was given to mktime() (and the input tm_isdst, if not -1,
can be used to select which - though there is no help available when the
input tm_isdst == -1) - but this is another case where an error return is
apparently allowed by C, the POSIX mktime() spec claims to defer to the C
standard, and nowhere (I have found) does POSIX say that it is more
restrictive in this area. Where do you believe these additional
requirements are stated?
I really believe that there is no option other than allowing EINVAL in
these cases (if you insist you could make both of them be "may fail if"
rather than "shall fail if") which would allow implementations which
return nonsense results, just because the (apparently broken) conformance
tests require nonsense results, to remain conforming, while still allowing
implementations striving for correctness to generate error returns when no
correct answer is possible.
On this last point:
Perhaps we should add the Area/Location TZ format to POSIX
(with no details, just a reference to another standard, maybe RFC 6557).
That would be a great idea. (Would need a new bug report though, it
doesn't belong in this one). "No details" I would agree with, and I'd
specify even less than you do there, I'd give some requirements on what
must be achieved, but no hints as to how to achieve that. Eg: I believe
that windows systems use the timezone data collected by the tz project,
(and probably more as well), but make use of it in a completely different
way, nothing like what that RFC specifies. No reason to prohibit that,
or even suggest it isn't the correct way - as long as it achieves the
What matters is a TZ value, that isn't a valid POSIX TZ string (for what
use those are, which isn't much, in any meaningful zone) and doesn't
start with a ':' (which should remain as now), will (in an implementation
unspecified manner) provide access to information which allows any
time_t (ideally since standard time was adopted in a locality, and
certainly since the Epoch) to be converted to the appropriate time_t
(and back again) using the rules appropriate for that timezone at that
particular time. Requiring a '/' in the name isn't required I'd
expect, though in practice, all that matter (and aren't also POSIX
strings) meet that format.
edited on: 2022-11-04 23:23
> Re Note: 0006025 Yes, POSIX is more strict than C here. The CX shaded text that matters is:
> A negative value for tm_isdst shall cause mktime() to attempt to determine whether Daylight Savings Time is in effect for the specified time.
> (C has something similar, but in a non-normative footnote.)
Note that this, despite being CX-shaded, is not actually an instance of POSIX being more strict than C. The C footnote, in full, is:
"Thus, a positive or zero value for tm_isdst causes the mktime function to presume initially that Daylight Saving Time, respectively, is or is not in effect for the specified time. A negative value causes it to attempt to determine whether Daylight Saving Time is in effect for the specified time."
The footnote makes it clear that this is considered to follow from the normative wording and there is therefore no need to have this as a separate requirement in the normative wording; the footnote is merely for clarification. I believe that thinking here is correct. Do you disagree?
I'm happy with POSIX choosing to spell this out in normative wording, but if it adds no new requirements, it cannot be the case that it specifies anything that isn't specified by the C standard as well. Either this is unspecified both in ANSI C and in POSIX, or this is defined in both ANSI C and POSIX.
|Re Note: 0006032 This discussion is becoming too long to hold in bugnotes. See my response on the mailing list.|
Re Note: 0006033
> The footnote makes it clear that this is considered to follow from the
> normative wording and there is therefore no need to have this as a separate
> requirement in the normative wording; the footnote is merely for
> clarification. I believe that thinking here is correct. Do you disagree?
Yes I disagree. The word "thus" is what makes it seems like this follows from normative text, but if you look at the normative text where the footnote marker is, there is nothing there from which the text in the footnote could be derived. So the use of "thus" is puzzling.
I believe the real reason that the text is in a footnote instead of normative is because, earlier on, the C standard says "The local time zone and Daylight Saving Time are implementation-defined." Since DST is implementation-defined the C standard can't have normative text requiring mktime() to behave a certain way with regard to DST; it is left to implementations to decide how to interpret tm_isdst.
In POSIX the DST rules are specified in the description of TZ, so there is no problem requiring mktime() to make use of them.
> Yes I disagree. The word "thus" is what makes it seems like this follows from normative text, but if you look at the normative text where the footnote marker is, there is nothing there from which the text in the footnote could be derived. So the use of "thus" is puzzling.
There is. The footnote is attached to a sentence that states that the values on entry of tm_wday and tm_yday are ignored and that the other values not limited to their usual ranges. This tells us all values other than tm_wday and tm_yday are used, including tm_isdst. This means the value on entry of tm_isdst must be interpreted according to the earlier definition of tm_isdst, which is (in normative text) "The value of tm_isdst is positive if Daylight Saving Time is in effect, zero if Daylight Saving Time is not in effect, and negative if the information is not available." The only way to use the value on entry of tm_isdst that is consistent with that definition is what the footnote describes.
It may not be exactly clear, but it's a legitimate reading and one that the wording in the footnote tells us is the intended reading.
Re Note: 0006036
> The only way to use the value on entry of tm_isdst that is consistent
> with that definition is what the footnote describes.
Here's where we disagree. As you say, negative tm_isdst means DST information is "not available"; however, there is nothing in the normative text that says how mktime() must behave when it is told that DST information is not available. The footnote is what does that, but it's non-normative.
> however, there is nothing in the normative text that says how mktime() must behave when it is told that DST information is not available.
We know that on a successful return of mktime(), "the other components are set to represent the specified calendar time". These other components again include tm_isdst, which thus must be set according to the description quoted earlier. If the implementation knows that Daylight Saving Time is in effect for the specified date and time, it must set tm_isdst to a positive value. If the implementation knows that Daylight Savings Time is not in effect for the specified date and time, it must set tm_isdst to zero.
It's one thing to think that this isn't clear from the C standard. It's quite another when it has specifically been stated by the C standard committee that this is how the normative text is meant to be read to argue that it doesn't say that. It doesn't matter whether you're right that it doesn't say that: we have zero doubt about the intent, so even if you can convince the committee that they're wrong, we know the outcome would just be to clear up the wording. We save ourselves a whole lot of time by just treating it as settled now.
Re Note: 0006039
> If the implementation knows that Daylight Saving Time is in effect for the
> specified date and time, it must set tm_isdst to a positive value. If the
> implementation knows that Daylight Saving Time is not in effect for the
> specified date and time, it must set tm_isdst to zero.
Where is the normative text that says that? I can see nothing in C99 that forbids mktime(), or localtime() for that matter, returning a negative tm_isdst because "the information is not available".
> Where is the normative text that says that? I can see nothing in C99 that forbids mktime(), or localtime() for that matter, returning a negative tm_isdst because "the information is not available".
When we're talking about the cases where that information is available to the implementation, it's right there, "the information is not available" does not apply to that case. Of course, in situations where that information truly is unavailable, a negative tm_isdst could be appropriate, and that is consistent with the footnote and with the POSIX wording, as "attempt to determine" allows for the possibility that the determination fails.
Re Note: 0006042 and Note: 0006043
You're right, in C99 it is possible for tm_isdst to be returned with the
value -1, if the implementation has no information on whether summer time
applies or not.
But here we tend to (also) live in a POSIX conforming environment, and then
this issue cannot arise. A conforming POSIX environment always has some
known timezone information, and always knows if summer time ever applies,
and if it does apply, always knows when the transitions occur. Those are
requirements to be conforming.
Any time_t value (which represents a year which will fit into the tm_year
field of a struct tm) will always be able to be converted to a struct_tm
with tm_isdst ==0 or tm_isdst == 1 -- -1 as a return value cannot happen.
> But here we tend to (also) live in a POSIX conforming environment, and then
> this issue cannot arise.
I was referring to the edge cases this issue is about there, the edge cases for which you have been arguing the most reasonable behaviour is for mktime() to return -1, but other implementations do return valid positive time_t values. I did not want to rule out the possibility of implementations setting tm_isdst to a negative value in those cases. We are in agreement that it should not happen in the normal cases.
Re Note: 0006045
No, that possibility is already ruled out. The "missing paragraph"
from C99 that didn't make it into the POSIX spec of mktime() makes
it quite clear that that cannot happen - the struct tm returned from
a successful call of mktime() is required to be identical to a struct
tm that would be returned by a call of localtime() on the result of
mktime (and also, a second call of mktime() using the struct tm returned
from the first must not alter the struct tm, and must return the same
result. Since localtime() can (in POSIX) never return with tm_isdst == -1
mktime() cannot either.
Further re Note: 0006045
Also, if we were in a situation where we don't know whether or
not summer time applies (or perhaps when it applies) in a particular
timezone (somehow) then the "edge cases for which I have been arguing..."
cannot occur either. If we don't know that summer time will commence
(or end), or we don't know when that will happen, then we cannot possibly
determine that a time is in the gap or foldback.
edited on: 2023-07-25 14:16
The following is a suggested resolution which just covers the originally raised issue of the allowed behaviour for "non-existent" times and the function's error handling.
The side-issue of how the conversion from broken-down time to time since the Epoch is performed, particularly with respect to out-of-range struct tm member values, should be addressed in bug 0001627.
The standard clearly states that when an unsuccessful call to mktime() returns (time_t)-1 it sets errno to [EOVERFLOW], and conforming implementations must conform to this.
The RETURN VALUE section on the mktime() page states:
If the time since the Epoch cannot be represented, the function shall return the value (time_t)-1 [CX]and set errno to indicate the error[/CX].This requires that errno is set to indicate "the error", and the beginning of the sentence states the nature of the error condition to which "the error" refers: the time since the Epoch (i.e. the integer value to be returned) cannot be represented. The ERRORS section requires that the error number [EOVERFLOW] is used for this condition.
Thus the standard requires that errno is set to [EOVERFLOW] when an unsuccessful call to mktime() returns (time_t)-1 and an implementation that sets it to [EINVAL] does not conform.
The mktime() function does not have any way to indicate to the caller that an error other than [EOVERFLOW] occurred.
Notes to the Editor (not part of this interpretation):
On page 425 line 14451 section <time.h>, after applying bug 1253 change:
The value of tm_isdst shall be positive if Daylight Saving Time is in effect, 0 if Daylight Saving Time is not in effect, and negative if the information is not available.to:
When tm_isdst is set by an interface defined in this standard, its value shall be positive if Daylight Saving Time (DST) is in effect and 0 if DST is not in effect. [CX]It shall not be set to a negative value by any interface defined in this standard. When tm_isdst is passed to the mktime() function, it specifies how mktime() is to handle DST when calculating the time since the Epoch value; see [xref to mktime()].[/CX]
On page 1331 line 44310 section mktime(), delete:
A positive or 0 value for tm_isdst shall cause mktime() to presume initially that Daylight Savings Time, respectively, is or is not in effect for the specified time. A negative value for tm_isdst shall cause mktime() to attempt to determine whether Daylight Savings Time is in effect for the specified time.
On page 1331 line 44317 section mktime(), change:
corrected for timezone and any seasonal time adjustmentsto:
corrected for the offset of the timezone's standard time from Coordinated Universal Time and further corrected (if applicable--see below) for Daylight Saving Time
After page 1331 line 44321 section mktime(), add these new paragraphs:
[CX]If the timezone is one that includes Daylight Saving Time (DST) adjustments, the value of tm_isdst in the tm structure controls whether or not mktime() adjusts the calculated seconds since the Epoch value by the DST offset (after it has made the timezone adjustment), as follows:
On page 1331 line 44323 section mktime(), after applying bug 1613 change:
with the specified time since the Epoch as its argumentto:
with the calculated time since the Epoch as its argument
On page 1331 line 44327 section mktime(), change:
The mktime() function shall return the specified time since the Epoch encoded as a value of type time_t. If the time since the Epoch cannot be represented, the function shall return the value (time_t)-1 [CX]and set errno to indicate the error[/CX].to:
The mktime() function shall return the calculated time since the Epoch encoded as a value of type time_t. If the time since the Epoch cannot be represented as a time_t [CX]or the value to be returned in the tm_year member of the structure pointed to by timeptr cannot be represented as an int[/CX], the function shall return the value (time_t)-1 [CX]and set errno to [EOVERFLOW], and shall not change the value of the tm_wday component of the structure.[/CX]
On page 1332 line 44348 section mktime(), change:
to:if (mktime(&time_str) == -1)
time_str.tm_wday = -1; if (mktime(&time_str) == (time_t)-1 && time_str.tm_wday == -1)
On page 1332 line 44359 section mktime(), change RATIONALE from "None" to:
In order to allow applications to distinguish between a successful return of (time_t)-1 and an [EOVERFLOW] error, mktime() is required not to change tm_wday on error. This mechanism is used rather than the convention used for other functions whereby the application sets errno to zero before the call and the call does not change errno on error because the ISO C standard does not require mktime() to set errno on error. The next revision of the ISO C standard is expected to require that mktime() does not change tm_wday when returning (time_t)-1 to indicate an error, and that this return convention is used both for the case where the value to be returned by the function cannot be represented as a time_t and the case where the value to be returned in the tm_year member of the tm structure cannot be represented as an int.
|An interpretation is proposed: 27 July 2023|
|Interpretation approved: 31 August 2023|
|2022-11-03 13:36||kre||New Issue|
|2022-11-03 13:36||kre||Name||=> Robert Elz|
|2022-11-03 13:36||kre||Section||=> XSH 3/mktime|
|2022-11-03 13:36||kre||Page Number||=> 1331|
|2022-11-03 13:36||kre||Line Number||=> 44331-44332|
|2022-11-03 16:54||geoffclare||Note Added: 0006022|
|2022-11-03 18:04||hvd||Note Added: 0006025|
|2022-11-03 20:02||kre||Note Added: 0006028|
|2022-11-03 20:27||kre||Note Added: 0006030|
|2022-11-03 20:43||kre||Note Edited: 0006028|
|2022-11-03 20:48||kre||Note Edited: 0006028|
|2022-11-04 09:36||geoffclare||Note Added: 0006031|
|2022-11-04 10:01||geoffclare||Note Edited: 0006031|
|2022-11-04 17:37||kre||Note Added: 0006032|
|2022-11-04 17:43||kre||Note Edited: 0006032|
|2022-11-04 23:23||hvd||Note Added: 0006033|
|2022-11-04 23:23||hvd||Note Edited: 0006033|
|2022-11-07 12:33||geoffclare||Note Added: 0006034|
|2022-11-07 12:42||geoffclare||Note Added: 0006035|
|2022-11-07 12:59||hvd||Note Added: 0006036|
|2022-11-08 15:24||geoffclare||Note Added: 0006037|
|2022-11-09 00:01||hvd||Note Added: 0006039|
|2022-11-09 17:07||geoffclare||Note Added: 0006042|
|2022-11-09 17:40||hvd||Note Added: 0006043|
|2022-11-09 21:15||kre||Note Added: 0006044|
|2022-11-09 23:18||hvd||Note Added: 0006045|
|2022-11-10 10:19||kre||Note Added: 0006046|
|2022-11-10 10:28||kre||Note Added: 0006047|
|2023-07-25 14:08||geoffclare||Note Added: 0006402|
|2023-07-25 14:09||geoffclare||Relationship added||related to 0001627|
|2023-07-25 14:09||geoffclare||Note Edited: 0006402|
|2023-07-25 14:16||geoffclare||Note Edited: 0006402|
|2023-07-27 15:18||Don Cragun||Interp Status||=> ---|
|2023-07-27 15:18||Don Cragun||Final Accepted Text||=> See Note: 0006402.|
|2023-07-27 15:18||Don Cragun||Status||New => Interpretation Required|
|2023-07-27 15:18||Don Cragun||Resolution||Open => Accepted As Marked|
|2023-07-27 15:20||Don Cragun||Tag Attached: issue8|
|2023-07-27 15:29||agadmin||Interp Status||--- => Proposed|
|2023-07-27 15:29||agadmin||Note Added: 0006404|
|2023-08-31 13:28||ajosey||Interp Status||Proposed => Approved|
|2023-08-31 13:28||ajosey||Note Added: 0006436|
|2023-09-04 10:28||geoffclare||Status||Interpretation Required => Applied|
|2023-09-04 10:29||geoffclare||Tag Attached: applied_after_i8d3|
|Mantis 1.1.6[^] Copyright © 2000 - 2008 Mantis Group|