User avatar
ab1jx
Posts: 885
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

When you use strptime tm_isdst is unitialized

Fri Jun 02, 2017 2:03 am

Well, nothing says it will be but it just bit me. I was comparing dates & times in 2017-05-29, 18:29:41 format that came out of rtl_power by parsing them with strptime() then using mktime() to convert the struct tm to time_t and then comparing them as simple numbers. In one case they converted as DST, in the other case they didn't, so the time_ts were different. The strings were the same. Forcing
tmptime.tm_isdst = 0;
cured the problem, only took me an hour or so to figure it out.

It's probably a wacky idea but initializing all members of a struct to 0 by default when they're allocated seems like a good idea. It would probably cause some overhead though.

Martin Frezman
Posts: 1009
Joined: Mon Oct 31, 2016 10:05 am

Re: When you use strptime tm_isdst is unitialized

Fri Jun 02, 2017 2:38 am

Actually, you are supposed to initialize that field to -1.

This tells the system to figure out whether or not DST is active. The system is usually going to be better at guessing than you are.

P.S. I agree that it is confusing that this is both an "input" and an "output" parameter. I got bit by it on one of my projects recently. I was passing in 0 and my DSTs weren't coming out right.
If this post appears in the wrong forums category, my apologies.

User avatar
ab1jx
Posts: 885
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: When you use strptime tm_isdst is unitialized

Fri Jun 02, 2017 3:28 am

I've mostly programmed under OpenBSD, I suspect their libc or whatever works differently. I never ran into this before, never gave it a thought.

Whether we're currently in DST or not should be irrelevant, nothing says the dates are current. I'm just looping through a bunch of data points for different frequency bins. Rtl_power sometimes puts everything from one time slot on the same line, sometimes not, I was just looking for a change in time from one line to the next.

They can look like

Code: Select all

2017-05-31, 14:24:41, 79750000, 80250000, 976.56, 56520, -22.02, -22.11, -20.57, ...
2017-05-31, 14:24:51, 79750000, 80250000, 976.56, 56740, -22.37, -21.43, -19.96, ...
2017-05-31, 14:25:01, 79750000, 80250000, 976.56, 56600, -23.21, -21.05, -19.55, ...
2017-05-31, 14:25:11, 79750000, 80250000, 976.56, 56860, -20.55, -20.16, -19.35, ...
2017-05-31, 14:25:21, 79750000, 80250000, 976.56, 57140, -21.48, -21.01, -19.45, ...
2017-05-31, 14:25:31, 79750000, 80250000, 976.56, 56980, -21.13, -21.18, -20.33, ...
or

Code: Select all

2016-07-08, 20:00:51, 115000000, 116000000, 1000000.00, 1, 8.53, 8.53
2016-07-08, 20:00:51, 116000000, 117000000, 1000000.00, 1, 4.62, 4.62
2016-07-08, 20:00:51, 117000000, 118000000, 1000000.00, 1, 5.83, 5.83
2016-07-08, 20:00:51, 118000000, 119000000, 1000000.00, 1, 5.82, 5.82
2016-07-08, 20:00:51, 119000000, 120000000, 1000000.00, 1, 4.42, 4.42
2016-07-08, 20:00:51, 120000000, 121000000, 1000000.00, 1, 8.05, 8.05
The 3rd and 4th column are min and max frequency. The listing of frequency bins can run out to over 4000 characters wide, or not. When the time changes it's a different data point.

User avatar
Paeryn
Posts: 2986
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: When you use strptime tm_isdst is unitialized

Fri Jun 02, 2017 9:51 am

In glibc strptime() only updates the values in the struct tm according to what was passed in the format, everything else is left alone (looking at OpenBSD they do the same). This is documented behaviour, it allows you to for example set the day and time in one call and them later on just update the time in another. There is no way of passing a value for DST in a strptime format so it is reasonable that strptime never alters that field in the struct tm.
ab1jx wrote:It's probably a wacky idea but initializing all members of a struct to 0 by default when they're allocated seems like a good idea. It would probably cause some overhead though.
Not whacky if they need zeroing but C doesn't do it by itself because that might be the wrong thing to do. If the first thing you do with a struct is to fill it with valid data then pre-initialising to zero every time is pointless.
She who travels light — forgot something.

User avatar
ab1jx
Posts: 885
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: When you use strptime tm_isdst is unitialized

Fri Jun 02, 2017 2:26 pm

Paeryn wrote:If the first thing you do with a struct is to fill it with valid data then pre-initialising to zero every time is pointless.
So it's up to the user to remember to initialize fields he isn't using? At least OpenBSD has this in their man page, Linux doesn't
There is no way to specify whether Daylight Saving Time is in effect when
calling strptime. To use the resulting tm structure with functions that
check the tm_isdst field, either set it to a negative value, which will
cause mktime(3) to attempt to divine whether Daylight Saving Time would
be in effect for the given time, or compute the value manually.
My point is that there's no conversion, like %DST, in strptime for parsing DST like with year or month. ctime or something similar does say EST or EDT based on something. It's lumped in with time zone and the Linux man page for strptime doesn't mention daylight savings while the OpenBSD one says

Code: Select all

     %z    an ISO 8601 or RFC 5322 timezone specification.  This is one of the
           following: the offset from Coordinated Universal Time (`UTC')
           specified as: ``[+-]hhmm'', ``[+-]hh:mm'', or ``[+-]hh''; `UTC'
           specified as: ``GMT'' (`Greenwich Mean Time'), ``UT'' (`Universal
           Time'), or ``Z'' (`Zulu Time'); a three character US timezone
           specified as: ``EDT'', ``EST'', ``CDT'', ``CST'', ``MDT'', ``MST'',
           ``PDT'', or ``PST'', with the first letter standing for `Eastern'
           (``E''), `Central' (``C''), `Mountain' (``M'') or `Pacific'
           (``P''), and the second letter standing for `Daylight' (``D'' or
           summer) time or `Standard' (``S'') time; a single letter military
           timezone specified as: ``A'' through ``I'' and ``K'' through ``Y''.

     %Z    timezone name or no characters when timezone information is
           unavailable.
Time zones here would serve no purpose, my input data doesn't have them. It's just a relative time, not absolute. I think daylight savings time is stupid anyway, if you look on change.org there have been several petitions to try to get rid of it. The best thing about it is that it forces you to set your manual clocks twice a year so clocks that might run slow or fast are corrected.

In this case I was declaring a struct tm as a local variable and sometimes DST would be on and sometimes it wouldn't. Which of course skews the time_t value by an hour, randomly.

Code: Select all

time_t gimmet(char *inp) {  // gimme t, return a time_t from a string
  struct tm tmptime;  
  char timestr[50];
  char *testch;
  memcpy(timestr,inp,20);
  timestr[20] = '\0';
  testch = strptime(timestr,"%Y-%m-%d, %H:%M:%S",&tmptime);
...
return mktime(&tmptime);

User avatar
ab1jx
Posts: 885
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: When you use strptime tm_isdst is unitialized

Tue Jun 13, 2017 8:15 pm

As it turns out, by design global structs do get initialized to sensible values, but not local ones. I read about it here: https://stackoverflow.com/a/18610323/4548383
It must be in other places too but I don't remember it from my intro C course 20 years ago.

User avatar
jojopi
Posts: 3274
Joined: Tue Oct 11, 2011 8:38 pm

Re: When you use strptime tm_isdst is unitialized

Tue Jun 13, 2017 10:30 pm

ab1jx wrote:As it turns out, by design global structs do get initialized to sensible values, but not local ones.
If you think about it, there are good reasons for the difference. Automatic (local) variables are created on the stack, at run time whenever the relevant functions are called. Writing specific values onto the stack requires more instructions than simply making room, so unless you explicitly assign to your local variables they are left uninitialized.

On the other hand, static and global variables are created just once at compile time, and the space allocated in the program's data section. Because a lot of code and data have to be read from disk at program start up anyway, there is relatively little cost in initializing all static and global variables to sensible defaults.

(Also, the kernel has to overwrite all of your program's memory on first allocation, so that you do not inherit sensitive data from other processes. In the case of sections that are not subject to reuse, the initial state may as well be something useful.)

User avatar
ab1jx
Posts: 885
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: When you use strptime tm_isdst is unitialized

Wed Jun 14, 2017 2:59 am

I guess I'd like to think that if I declare a local object, some optimized assembly routine is filling sizeof(object) bytes with zeroes. If you use a simple variable GCC will warn you if there's a chance that due to a branch in your code you might be using it uninitialized. Most of the time anyway. But not for structs apparently. Maybe there's a compiler switch for that, the GCC manual is huge. If there was a way to exploit it they'd probably fix it.

jahboater
Posts: 5825
Joined: Wed Feb 04, 2015 6:38 pm
Location: West Dorset

Re: When you use strptime tm_isdst is unitialized

Wed Jun 14, 2017 7:10 am

ab1jx wrote:I guess I'd like to think that if I declare a local object, some optimized assembly routine is filling sizeof(object) bytes with zeroes.
No, that would be pointless if the struct is about to be filled with something else.
Allocating a struct on the stack simply involves subtracting its size from the stack pointer. One single instruction allocates space for all the stack variables in the function (very fast).
Use "memset" or "struct xxx = { 0 };" if you need it initialized to zero.
Of course scalar variables will likely never end up on the stack anyway, they stay in registers (especially in aarch64 mode which has 31 general purpose registers!).
ab1jx wrote:If you use a simple variable GCC will warn you if there's a chance that due to a branch in your code you might be using it uninitialized. Most of the time anyway. But not for structs apparently. Maybe there's a compiler switch for that, the GCC manual is huge.I
see -Wuninitialized and -Wmaybe-uninitialized (and compile with -O3 which does a deeper analysis yielding more accurate results).

See also the valgrind tool which will check all references at runtime, and will even tell you if just one individual field has not been initialized.

Return to “C/C++”