2

The glibc version of struct tm has additional fields

long tm_gmtoff;           /* Seconds east of UTC */
const char *tm_zone;      /* Timezone abbreviation */

(Ref: http://linux.die.net/man/3/ctime ) My question is: If I have a data called struct tm a and i would like to copy that to another struct tm b, as per below code:

time_t t = time(0);
const tm *pa = localtime(&t);
struct tm a;
if(pa) {
    memcpy(&a, pa, sizeof(a));
}

But, what will happen to tm_gmtoff and tm_zone? How to copy these fields too?

Dr. Debasish Jana
  • 6,980
  • 4
  • 30
  • 69

3 Answers3

4

This is a job for your compiler. It will take all necessary action.
Just do:

struct tm a = *localtime(&t);
Martin B.
  • 1,567
  • 14
  • 26
  • 1
    Just for those curious about ownership: http://stackoverflow.com/a/8694379/978486 – ManuelSchneid3r Apr 28 '17 at 10:15
  • But tm_zone being a character array, the copying will not be deep copy rather a shallow copy sharing the pointer – Dr. Debasish Jana Apr 28 '17 at 10:25
  • That is true. But what I read is that the timezone value is being ignored and the locale settings are used instead. Could be wrong here but I think thats how it works – Martin B. Apr 28 '17 at 10:29
  • 1
    Seems like this will crash in certain cases. Here a link to a reproducer: https://gitlab.xfce.org/xfce/thunar/-/issues/700#note_40990 – Alex Dec 19 '21 at 22:55
  • 1
    So it clearly is a bad idea to do like suggested in this answer. Here some doc of localtime: https://pubs.opengroup.org/onlinepubs/009604499/functions/localtime.html `.... If an error is detected, localtime() shall return a null pointer and set errno to indicate the error.` So if NULL will be returned by `localtime`, the suggested code will crash. – Alex Dec 19 '21 at 23:54
1

Since locatime() may return a null pointer, avoid

const struct tm a = *localtime(&t);  // Potential UB.

Instead, test for null-ness and then copy with =. memcpy() not needed. All struct tm members will be copied.

struct tm a = { 0 }; // Fill in a default value
const tm *pa = localtime(&t);
if (pa) {
  a = *pa;
}

OP comments But tm_zone being a character array, yet that is amiss. tm_zone is not a character array, but a pointer to a const character array. Copying that pointer is fine here.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Not sure if it will be sufficiient to just copy the pointer of tm_zone. That very much depends on the use-case ... the tm of the system will be changing, so will the time-zone on which we hold a pointer on. E.g. see here: https://stackoverflow.com/questions/24026224/calling-time-in-c-changes-my-struct-tm – Alex Dec 19 '21 at 23:59
  • The pointer, in `*pa` or a copy of it in `a` certainly is valid up to the next `localitme()/gmtime()` call. Up to that point, it is sufficient. After that it may still be OK - an implementation specific issue that linux is silent on. Code could `a = *pa;, a.tm_zone = strdup(pa->tm_zone);` and then manage the string. Your point is valid, yet OP's use case lacks details. – chux - Reinstate Monica Dec 20 '21 at 02:01
0

I think just memcpy is the right thing to do here for most cases.

But, what will happen to tm_gmtoff and tm_zone? How to copy these fields too?

According to the doc you linked, these fields will only be present when _BSD_SOURCE was set before including <time.h>.

So if you are using _BSD_SOURCE, you will need to use strcpy on tm_zone after the memcpy.

Alex
  • 1,602
  • 20
  • 33