Bug 3 - mktime: tm_mday>31 and daylight saving influences tm_hour
Summary: mktime: tm_mday>31 and daylight saving influences tm_hour
Status: RESOLVED INVALID
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: unspecified
: P3 minor
Target Milestone: ---
Assignee: GOTO Masanori
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-01-28 13:16 UTC by Moritz Franosch
Modified: 2021-11-09 06:59 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Moritz Franosch 2004-01-28 13:16:01 UTC
Release:	libc-2.2.5 
Environment: Linux kernel 2.4.18 of Suse Linux 8.0 
Host type: i486-suse-linux-gnu 
System: Linux gateway 2.4.18-4GB #1 Wed Mar 27 13:57:05 UTC 2002 i686 unknown 
Architecture: i686 
 
Description: 
 
	In a struct tm, if you set tm_mday=d to a value greater than 31, 
	and you also set tm_hours=10, what sould that mean? In my 
	opinion 10 o'clock local time, d-1 calendar days after the time that 
	would have been specified if you had set tm_mday=1. So, after 
	calling mktime, you should end up with a normalized time of 
	tm_hour=10. But in fact, tm_hour may be changed by mktime if 
	dayligt saving is in effect. 
 
Code: 
#include <ctime> 
#include <iostream> 
 
using namespace std; 
 
int main() { 
  struct tm date; 
  date.tm_sec=0; 
  date.tm_min=0; 
  date.tm_hour=10;         // Here I want to set 10.00 on the 
  date.tm_mday=150;        // 150th day  
  date.tm_mon=0;           // of the year 
  date.tm_year=2003-1900;  // 2003. 
  date.tm_wday=0; 
  date.tm_yday=0; 
  date.tm_isdst=0; 
  time_t t=mktime(&date);  // Normalize date and time. 
  cout << "t=" << t << endl; 
  cout << "mday=" << date.tm_mday << endl; 
  cout << "mon=" << date.tm_mon << endl; 
  cout << "yday=" << date.tm_yday << endl; 
  cout << "isdst=" << date.tm_isdst << endl; 
  cout << "gmtoff=" << date.tm_gmtoff << endl; 
  cout << "zone=" << date.tm_zone << endl; 
  cout << "hour=" << date.tm_hour << endl; 
  // date.tm_hour should be always 10 (unchanged from the value preset 
  // above) but in fact it is influenced by the daylight saving offset 
  // in the TZ environment variable. E.g. if 
  // TZ="TEST-1TESTDS-3,100,200", then date.tm_hour=12, which is not 
  // what was intended. 
} 
 
Compilation: 
g++ mktime.cpp 
 
Result: 
mf@gateway:~/projects/bugreports/glibc_mktime> TZ="TEST-1TESTDS-2,100,200" ./a.out 
t=1054285200 
mday=30 
mon=4 
yday=149 
isdst=1 
gmtoff=7200 
zone=TESTDS 
hour=11 
mf@gateway:~/projects/bugreports/glibc_mktime> TZ="TEST-1TESTDS-3,100,200" ./a.out 
t=1054285200 
mday=30 
mon=4 
yday=149 
isdst=1 
gmtoff=10800 
zone=TESTDS 
hour=12 
 
I get hour=11 or hour=12 where I would expect hour=10. Obviously, 
glibc takes daylight saving into account somehow.  
 
The manual says in the mktime section: 
 
     The `mktime' function ignores the specified contents of the 
     `tm_wday' and `tm_yday' members of the broken-down time structure. 
     It uses the values of the other components to determine the 
     calendar time; it's permissible for these components to have 
     unnormalized values outside their normal ranges.  The last thing 
     that `mktime' does is adjust the components of the BROKENTIME 
     structure (including the `tm_wday' and `tm_yday'). 
 
But it does not describe exactly what an unnormalized value of tm_mday 
means. I would expect that tm_mday += 10 always means "The same local 
time, as specified in tm_hour, ten calendar days later". But in fact 
it means something like "The same local time, as specified in tm_hour, 
ten calendar days later, or, if tm_mday becomes greater than 31, 
exactly 10*24 hours later." So, if during these 10 days daylight 
saving changes, glibc changes tm_hour accordingly, which is wrong. 
 
Either this is a bug in glibc or the manual entry regarding the mktime 
function is unclear. I would vote for a bug because if you meant 
"exactly 10*24 hours later" you would have set tm_hour += 10*24. 
 
 
Thank you, 
 
Moritz
Comment 1 Ulrich Drepper 2004-02-16 19:24:18 UTC
And now we try this again without C++.  This is the C library.  Everything
related to C++ is ignored.
Comment 2 Moritz Franosch 2004-02-16 20:11:24 UTC
In C, the behavior of the program is the same. 
 
Code: 
#include <time.h> 
 
int main() { 
  struct tm date; 
  date.tm_sec=0; 
  date.tm_min=0; 
  date.tm_hour=10;         // Here I want to set 10.00 on the 
  date.tm_mday=150;        // 150th day  
  date.tm_mon=0;           // of the year 
  date.tm_year=2003-1900;  // 2003. 
  date.tm_wday=0; 
  date.tm_yday=0; 
  date.tm_isdst=0; 
  time_t t=mktime(&date);  // Normalize date and time. 
  printf("t=%d\n",t); 
  printf("mday=%d\n",date.tm_mday); 
  printf("mon=%d\n",date.tm_mon); 
  printf("yday=%d\n",date.tm_yday); 
  printf("isdst=%d\n",date.tm_isdst); 
  printf("gmtoff=%d\n",date.tm_gmtoff); 
  printf("zone=%d\n",date.tm_zone); 
  printf("hour=%d\n",date.tm_hour); 
  // date.tm_hour should be always 10 (unchanged from the value preset 
  // above) but in fact it is influenced by the daylight saving offset 
  // in the TZ environment variable. E.g. if 
  // TZ="TEST-1TESTDS-3,100,200", then date.tm_hour=12, which is not 
  // what was intended. 
} 
 
 
Compiling: 
gcc mktime.c 
 
Result: 
jfranosc@koma:~/projects/bugreports/glibc_mktime> TZ="TEST-1TESTDS-2,100,200" 
./a.out 
t=1054285200 
mday=30 
mon=4 
yday=149 
isdst=1 
gmtoff=7200 
zone=134520904 
hour=11 
jfranosc@koma:~/projects/bugreports/glibc_mktime> TZ="TEST-1TESTDS-3,100,200" 
./a.out 
t=1054285200 
mday=30 
mon=4 
yday=149 
isdst=1 
gmtoff=10800 
zone=134520904 
hour=12 
 
Thank you for your fast response. 
 
Moritz 
 
Comment 3 GOTO Masanori 2004-02-17 15:22:03 UTC
I think you also know that tm_mday is recommended to be specified
in the range [1,31] (ex: SuSv3).  

And looking at SuSv3 mktime (line 25160):

   Local timezone information shall be set as though mktime( ) called tzset( ).
   The relationship between the tm structure (defined in the <time.h> header)
and the time in
   seconds since the Epoch is that the result shall be as specified in the
expression given in the
   definition of seconds since the Epoch (see the Base Definitions volume of
IEEE Std 1003.1-200x,
   Section 4.14, Seconds Since the Epoch) corrected for timezone and any
seasonal time
   adjustments, where the names in the structure and in the expression correspond.
   Upon successful completion, the values of the tm_wday and tm_yday components
of the structure
   shall be set appropriately, and the other components are set to represent the
specified time since
   the Epoch, but with their values forced to the ranges indicated in the
<time.h> entry; the final
   value of tm_mday shall not be set until tm_mon and tm_year are determined.

So I think adjusting with seasonal time (daylight saving time) is acceptable.


BTW, I checked it on some OSes:

FreeBSD 4.3:
> TZ="TEST-1TESTDS-2,100,200"  ./a.out 
t=1054285200
mday=30
mon=4
yday=149
isdst=1
gmtoff=7200
zone=672058865
hour=11

Solaris 7:
> TZ="TEST-1TESTDS-2,100,200"  ./a.out
t=1054285200
mday=30
mon=4
yday=149
isdst=1
hour=11

They also seem "Epoch + seconds" based behavior.
Comment 4 Moritz Franosch 2004-02-17 16:51:05 UTC
"Upon successful completion, the values of the tm_wday and tm_yday components of the structure shall be set appropriately, and the other components are set to represent the specified time since the Epoch, but with their values forced to the ranges indicated in the <time.h> entry;"  The point is: What is the "specified time"? Or, what do users of mktime mean by 2004-01-32, 10:00? Is it 10:00 on 2004-02-01 or exactly 24 hours after 10:00 on 2004-01-31?  I thought it should be 10:00 on 2004-01-02. Else, the behavior would be inconsistent: mday+=1 would sometimes mean "the same local time the next day" and sometimes "exactly 24 hours later".  Moreover, if I specify 10:00 2004-02-31, e.g. in a video recording program, and am not aware that February has 30 only days, I mean 10:00 local time the next day and not 24 hours later.  But as other OSs behave the same, the documentation should say cleary that you should not specify an mday out of range, although normalization takes place. This would be somewhat inconvenient, because you can not compute 10:00 on the 100th day of the year (you can not specify yday because it will be overwritten, and an out of range mday leads to +/- daylight saving hours).  Moritz   
Comment 5 Ulrich Drepper 2005-09-23 19:54:19 UTC
The results are exactly right.  The adjustment must be made because the isdst
information and the date do not match.  Just set isdst correctly and the 10
isn't changed.
Comment 6 svitkaz 2021-11-09 06:59:12 UTC Comment hidden (spam)
Comment 7 svitkaz 2021-11-09 06:59:25 UTC Comment hidden (spam)