discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: NSCalendar bug (mktime related)


From: Fred Kiefer
Subject: Re: NSCalendar bug (mktime related)
Date: Sat, 25 Apr 2020 15:57:44 +0200

Hi Andreas,

I checked the code of NSCalendar, which you could have done yourself, this is 
free software, and we are not using mtkime there. The problem is a lot worse. 
We just ignore the handed in time zone of the NSDateComponents. This could be 
repaired but I am no expert on NSCalendar so if anybody else wants to look into 
this feel free. It would be great to have a few more tests for NSCalendar. 
Anybody willing to provide a few test cases?

Fred

> Am 23.04.2020 um 10:51 schrieb Andreas Fink <list@fink.org>:
> 
> Hello all,
> 
> I have run into a very weird thing in conversion from NSStrings to NSDate. 
> The result is we are always off by 1h under LInux.
> Under MacOS X I have the same problem but only with mktime() not with 
> NSCalendar.
> I am suspecting Gnustep implementation probably uses mktime() in the back and 
> thus inherits this issue also for NSCalendar.
> 
> What I try to do is to convert aNSString with a timestamp which is always in 
> UTC into a date.
> So the timestamp I supply has timezone GMT+0 and no daylight savings time.
> If the current system currently experiences daylight savings time, the result 
> by mktime is off by 1h even if I specify timezone to be UTC in struct tm.
> 
> Here is a test programm for this:
> 
> 
> #define _BSD_SOURCE
> #include <time.h>
> #include <string.h>
> #include <stdio.h>
> time_t test(void)
> {
>    struct tm tm;
>       memset(&tm,0, sizeof(tm));
> 
>     int year = 1970;
>     int month = 1;
>     int day = 1;
>     int hour = 0;
>     int minute = 0;
>     int second = 0;
>        
>     tm.tm_year = year -1900;
>       tm.tm_mon = month -1,
>     tm.tm_mday = day;
>     tm.tm_hour = hour;
>     tm.tm_min = minute;
>     tm.tm_sec = second;
>     tm.tm_zone = "UTC";
>     tm.tm_isdst = -1;
>     tm.tm_gmtoff = 0;
>       
>     time_t t = mktime(&tm);
>       return t;
> }
> 
> 
> int main(int argc, const char * argv[])
> {
>       time_t t;
> 
>       setenv("TZ","UTC",1);
>       t = test();
>       printf("TZ=UTC:  t=%d\n",t);
> 
>       setenv("TZ","CET",1);
>       t = test();
>       printf("TZ=CET:  t=%d\n",t);
> 
>       setenv("TZ","CEST",1);
>       t = test();
>       printf("TZ=CEST:  t=%d\n",t);
> 
>       setenv("TZ","Europe/Zurich",1);
>       t = test();
>       printf("TZ=Europe/Zurich:  t=%d\n",t);
> 
> }
> 
> 
> So the timestamp I supply has timezone GMT+0 and no daylight savings time.
> So the time_t value returned should always be 0 but its off by -1h if the 
> envirnment has the timezone set to Europe/Zurich or CET. Interestingly CEST 
> is correct (which is the current timezone in Zurich CET + Daylight savings 
> time)
> 
> TZ=UTC:  t=0
> TZ=CET:  t=-3600
> TZ=CEST:  t=0
> TZ=Europe/Zurich:  t=-3600
> 
> The output of this is indicating that the daylight savings yes/no  from the 
> supplied timezone is ignore as well as the timezone. It always bases the date 
> on the current environmental variable even though the current timezone might 
> not be the one in effect at that date.
> So it assumes that on 1.11970 we had daylight savings time (because TZ says 
> we have now) despite being in January and despite it wasn't in use in that 
> year even.
> 
> Now lets say this is a bug of mktime or maybe a wanted feature. Be is at it 
> is.
> 
> The problem is NSCalendar fails for the same issue.
> 
> 
> This piece of code works under MacOS X but fails under Linux.
> Under NSCalendar I specify the timezone explicitly and TZ environment 
> variable should not be relevant. But if NSCalendar implementation uses 
> mktime, it inherits above strange behaviour.
> 
> NSDate *dateFromStringNSCalendar(NSString *str, const char *ctimezone_str) /* 
> expects YYYY-MM-DD hh.mm.ss.SSSSSS TZ  timestamps */
> {
>     int year;
>     int month;
>     int day;
>     int hour;
>     int minute;
>     int seconds;
>     double subsecond = 0;
>     const char *cdate_str;
>     const char *ctime_str;
>     
>     NSArray *components = [str componentsSeparatedByString:@" "];
>     if(components.count >0)
>     {
>         NSString *s = components[0];
>         cdate_str = s.UTF8String;
>     }
>     if(components.count > 1)
>     {
>         NSString *s = components[1];
>         ctime_str = s.UTF8String;
>     }
>     if(components.count > 2)
>     {
>         NSMutableArray *arr =  [components mutableCopy];
>         [arr removeObjectsInRange:NSMakeRange(0,2)];
>         NSString *s = [arr componentsJoinedByString:@" "];
>         ctimezone_str = s.UTF8String;
>     }
> 
>     /* parsing date */
>     sscanf(cdate_str,"%04d-%02d-%02d",
>            &year,
>            &month,
>            &day);
>     if(strlen(ctime_str) ==8 ) /* HH:mm:ss.SSSSSS */
>     {
>         sscanf(ctime_str,"%02d:%02d:%02d",
>                &hour,
>                &minute,
>                &seconds);
>     }
>     else if(strlen(ctime_str) >=9  ) /* HH:mm:ss.SSSSSS */
>     {
>         sscanf(ctime_str,"%02d:%02d:%lf",
>                &hour,
>                &minute,
>                &subsecond);
>         seconds = (int)subsecond;
>         subsecond = subsecond - (double)seconds;
>     }
>     else
>     {
>         return NULL;
>     }
>     NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
>     dateComponents.day = day;
>     dateComponents.month = month;
>     dateComponents.year = year;
>     dateComponents.hour = hour;
>     dateComponents.minute = minute;
>     dateComponents.second = seconds;
> #ifdef __APPLE__
>     dateComponents.nanosecond = subsecond * 1000000000;
> #endif
>     if(ctimezone_str!=NULL)
>     {
>         NSTimeZone *tz      = [NSTimeZone timeZoneWithName:@(ctimezone_str)];
>         dateComponents.timeZone = tz;
>     }
>     NSCalendar *gregorianCalendar = [[NSCalendar alloc] 
> initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
>     NSDate *date = [gregorianCalendar dateFromComponents:dateComponents];
>     return date;
> }
> 
> 
> 
> 
> 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]