views:

118

answers:

3

Here is my current situation:

  • I have two tm structs, both set to the current time
  • I make a change to the hour in one of the structs
  • The change is occurring in the other struct magically....
  • How do I prevent this from occurring? I need to be able to compare and know the number of seconds between two different times -- the current time and a time in the future. I've been using difftime and mktime to determine this. I recognize that I don't technically need two tm structs (the other struct could just be a time_t loaded with raw time) but I'm still interested in understanding why this occurs.

void Tracker::monitor(char* buffer){

// time handling
time_t systemtime, scheduletime, currenttime;
struct tm * dispatchtime;
struct tm * uiuctime;
double remainingtime;


// let's get two structs operating with current time
dispatchtime = dispatchtime_tm();
uiuctime = uiuctime_tm();

// set the scheduled parameters
dispatchtime->tm_hour = 5;
dispatchtime->tm_min = 05;
dispatchtime->tm_sec = 14;

uiuctime->tm_hour = 0;

    // both of these will now print the same time! (0:05:14)
    // what's linking them??

// print the scheduled time
printf ("Current Time :  %2d:%02d:%02d\n", uiuctime->tm_hour, uiuctime->tm_min, uiuctime->tm_sec);
printf ("Scheduled Time :  %2d:%02d:%02d\n", dispatchtime->tm_hour, dispatchtime->tm_min, dispatchtime->tm_sec);

}

struct tm* Tracker::uiuctime_tm(){
    time_t uiucTime;
    struct tm *ts_uiuc;

    // give currentTime the current time
    time(&uiucTime);

    // change the time zone to UIUC
    putenv("TZ=CST6CDT");
    tzset();

    // get the localtime for the tz selected
    ts_uiuc = localtime(&uiucTime);

    // set back the current timezone
    unsetenv("TZ");
    tzset();

    // set back our results
    return ts_uiuc;
}

struct tm* Tracker::dispatchtime_tm(){
    time_t currentTime;
    struct tm *ts_dispatch;

    // give currentTime the current time
    time(&currentTime);

    // get the localtime for the tz selected
    ts_dispatch = localtime(&currentTime);

    // set back our results
    return ts_dispatch;
}
+2  A: 

http://www.cplusplus.com/reference/clibrary/ctime/localtime/

This structure is statically allocated and shared by the functions gmtime and localtime. Each time either one of these functions is called the content of this structure is overwritten.

You'll need to copy the value out of the struct. Your functions could return a tm struct by value, you could dereference the functions in the main program, etc.

Cogwheel - Matthew Orlando
But localtime is called first, then the struct is modified. I fail to see the relevance in what you quoted.
BSchlinker
@BSchlinker The struct is returned and stored by pointer, but the two different pointers are both pointing at the same static memory.
Mark B
Inside `uiuctime_tm` and `dispatchtime_tm`, you're simply returning the pointer given by calling `localtime`. In your main program, you're storing the same pointer. This pointer will always point to the same struct. By calling `uiuctime_tm` after `dispatchtime_tm`, you're overwriting the struct. You need to copy the data out of the struct since all of your pointers are always pointing to the same data.
Cogwheel - Matthew Orlando
+2  A: 

You have to do this:

struct tm* temp_tm;
struct tm dispatchtime; // No longer a pointer
struct tm uiuctime;     // No longer a pointer

temp_tm = dispatchtime_tm();
dispatchtime = *temp_tm; // Member to member copy

temp_tm = uiuctime_tm();
uiuctime = *temp_tm; // Member to member copy

This way you will keep a local copy of the tm struct. This struct is allocated internally in the standard library, each call to localtime will point to the same memory address!

Lorenzo
Your solution could have been even simpler. I'm not sure why you assigned first to a temp_tm and then to the final tm, the following works also:dispatchtime = *dispatchtime_tm();uiuctime = *uiuctime_tm();
BSchlinker
You are right. My sample uses an intermediate step just to be as understandable and readable as possible.
Lorenzo
@Lorenzo Understood. I appreciate the clarity, I was just dumbfounded for a moment after I looked at your solution as to why you included that step.
BSchlinker
+1  A: 

You actually don't have two different tm structs at all. What you have is two tm struct pointers, both pointing at the same static structure returned by localtime. Thus it appears that changes to one affect the other, but in reality it's simply one structure has two different pointers to it.

The safest way to solve this is to not rely on localtime's static structure, but use localtime_r which requires you to pass in your own tm structure pointer, which will then be filled out. For example:

void Tracker::uiuctime_tm(struct tm* out){
    time_t uiucTime;

    // give currentTime the current time
    time(&uiucTime);

    // change the time zone to UIUC
    putenv("TZ=CST6CDT");
    tzset();

    // get the localtime for the tz selected, and set back the result into the output parameter.
    localtime_r(&uiucTime, out);

    // set back the current timezone
    unsetenv("TZ");
    tzset();
}

struct tm uiuctime;
uiuctime_tm(&uiuctime);
Mark B
I appreciate your explanation of localtime_r. It was not listed on cplusplus.com so I was unaware of it.
BSchlinker