0

When using the stat() function in minGW-W64, and the struct it returns, here info, why does ctime return the same exact strings for info.st_atime, mtime, and ctime, even though the their integer values are different?

If localtime is used to get each time component separately, the results differ and appear accurate; that is, they match the dates in the file folders I've observed.

ctime(&info.st_atime )  versus   localtime(&info.st_atime )->tm_wday,
                                 localtime(&info.st_atime )->tm_mon,
                                 localtime(&info.st_atime )->tm_mday,
                                 localtime(&info.st_atime )->tm_hour,
                                 localtime(&info.st_atime )->tm_min,
                                 localtime(&info.st_atime )->tm_sec,
                                 localtime(&info.st_atime )->tm_year

A similar question was asked over three years ago without answer.

Does anyone know why and whether or not there is any documentation on dates in minGW-W64?

Thank you.


Here is the full code example. It's an ugly printf set.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>

int main (void)

{
  DIR *dp;
  struct dirent *ep;
  struct stat info;
  int rc;
  char fl_name[300];

  dp = opendir("./SQLite3");

  if ( dp != NULL )
    {
      while ( ep = readdir( dp ) )
        {
          printf( "Name : %s, ", ep->d_name );
          if ( *( ep->d_name ) == '.' )
            { 
              printf(" non-useful file\n");
              continue;
            }

          sprintf( fl_name, "%s%s", "./SQLite3/", ep->d_name );

          if ( ( rc = stat( fl_name, &info ) ) != 0 )
             {
               printf( "rc : %d\n", rc );
               printf( "errno : %d, strerror : %s\n", errno, strerror( errno ) );
               break;
             }

          printf( "mode : %d, size : %d,\nst_atime : %slocal : %d %d %d %d : %d : %d %d,\ninteger value : %d\n"           
                                        "st_mtime : %slocal : %d %d %d %d : %d : %d %d,\ninteger value : %d\n"          
                                        "st_ctime : %slocal : %d %d %d %d : %d : %d %d,\ninteger value : %d\n\n",

                  info.st_mode, info.st_size, 
                  ctime(&info.st_atime ),
                  localtime(&info.st_atime )->tm_wday,
                  localtime(&info.st_atime )->tm_mon,
                  localtime(&info.st_atime )->tm_mday,
                  localtime(&info.st_atime )->tm_hour,
                  localtime(&info.st_atime )->tm_min,
                  localtime(&info.st_atime )->tm_sec,
                  localtime(&info.st_atime )->tm_year,
                  info.st_atime,

                  ctime(&info.st_mtime ),
                  localtime(&info.st_mtime )->tm_wday,
                  localtime(&info.st_mtime )->tm_mon,
                  localtime(&info.st_mtime )->tm_mday,
                  localtime(&info.st_mtime )->tm_hour,
                  localtime(&info.st_mtime )->tm_min,
                  localtime(&info.st_mtime )->tm_sec,
                  localtime(&info.st_mtime )->tm_year,
                  info.st_mtime,

                  ctime(&info.st_ctime ),
                  localtime(&info.st_ctime )->tm_wday,
                  localtime(&info.st_ctime )->tm_mon,
                  localtime(&info.st_ctime )->tm_mday,
                  localtime(&info.st_ctime )->tm_hour,
                  localtime(&info.st_ctime )->tm_min,
                  localtime(&info.st_ctime )->tm_sec,
                  localtime(&info.st_ctime )->tm_year ),
                  info.st_ctime;    
        }    
      printf( "Now : %ld\n", time(NULL) );
      printf( "Broke" );
      (void) closedir (dp);
    }
  else
    perror ("Couldn't open the directory");    
  return 0;
}

Results for one file looks as follows.

Name : testing.c, mode : 33206, size : 21092,
st_atime : Thu Nov 26 23:56:20 2020
local : 4 10 26 23 : 56 : 20 120,
integer value : 1606452980
st_mtime : Thu Nov 26 23:56:20 2020
local : 5 10 27 0 : 16 : 58 120,
integer value : 1606454218
st_ctime : Thu Nov 26 23:56:20 2020
local : 6 9 31 23 : 8 : 28 120,
integer value : 5767254

Note that the data strings from ctime() are identical although the integer values differ for all three. Also, the integer value for ctime is a different format than for atime and mtime. The atime and mtime appear to be seconds since Jan 1, 1970 but I don't know what ctime is as 5767254.

Thanks.


Fixed code according to comment by @KamilCuk. My stupidity and ugly printf were the cause. Changes below and new results.

  struct tm *local_tm_ptr;

    
          local_tm_ptr = localtime(&info.st_atime );
          printf( "mode : %d, size : %d,\nst_atime : %slocal : %d %d %d %d : %d : %d %d,\ninteger value : %ld\n",           
                  info.st_mode, info.st_size, 
                  ctime(&info.st_atime ),
                  local_tm_ptr->tm_wday,
                  local_tm_ptr->tm_mon,
                  local_tm_ptr->tm_mday,
                  local_tm_ptr->tm_hour,
                  local_tm_ptr->tm_min,
                  local_tm_ptr->tm_sec,
                  local_tm_ptr->tm_year,
                  info.st_atime );


          local_tm_ptr = localtime(&info.st_mtime );
          printf( "st_mtime : %slocal : %d %d %d %d : %d : %d %d,\ninteger value : %ld\n",
                  ctime(&info.st_mtime ),
                  local_tm_ptr->tm_wday,
                  local_tm_ptr->tm_mon,
                  local_tm_ptr->tm_mday,
                  local_tm_ptr->tm_hour,
                  local_tm_ptr->tm_min,
                  local_tm_ptr->tm_sec,
                  local_tm_ptr->tm_year,
                  info.st_mtime );


          local_tm_ptr = localtime(&info.st_ctime );
          printf( "st_ctime : %slocal : %d %d %d %d : %d : %d %d,\ninteger value : %ld\n\n",
                  ctime(&info.st_ctime ),
                  local_tm_ptr->tm_wday,
                  local_tm_ptr->tm_mon,
                  local_tm_ptr->tm_mday,
                  local_tm_ptr->tm_hour,
                  local_tm_ptr->tm_min,
                  local_tm_ptr->tm_sec,
                  local_tm_ptr->tm_year,
                  info.st_ctime );

New results.

Name : testing.c, mode : 33206, size : 21092,
st_atime : Thu Nov 26 23:56:20 2020
local : 4 10 26 23 : 56 : 20 120,
integer value : 1606452980
st_mtime : Fri Nov 27 00:16:58 2020
local : 5 10 27 0 : 16 : 58 120,
integer value : 1606454218
st_ctime : Sat Oct 31 23:08:28 2020
local : 6 9 31 23 : 8 : 28 120,
integer value : 1604200108
Gary
  • 2,393
  • 12
  • 31
  • @DavidC.Rankin Thanks that's helpful but not exactly what I am asking about. `info` does contain the time struct information for `atime, mtime, ctime` but when pass it to the `ctime` function it returns the same date string for all three, even though each is of a different integer value. If a similar date string is constructed through combining separate references to each member of the struct using the `localtime()` function, then the date strings differ. I don't understand why `ctime()` returns the same string for `st_atime, st_mtime, st_ctime` when they are different integer values. Thanks. – Gary Nov 28 '20 at 21:38
  • Okay, sorry, now I think I understand. Can you show your declaration for `info`? (like `struct stat info = {.st_dev = 0};`?) and your call to `stat` (e.g. `stat (some_path, &info);`). If so, your use of `ctime(&info.st_atime)` looks fine. The only difference between the `ctime()` call and the `localtime()` call will be the user's specified timezone (from a time reported standpoint) – David C. Rankin Nov 29 '20 at 02:00
  • @DavidC.Rankin Thanks for responding. I added the code that I've been using to test and one result block from it for a single file. `atime` seems to match for `ctime()` and `localtime()` components. The `mtime` and `ctime` are the same as `atime` from `ctime()`, atlhough have different integer values, and their `localtime()` components appear to reflect that. The integer value of `ctime` looks to be a different format. I don't know if Windows is just challenging or I am doing something stupid. Thank you. – Gary Nov 29 '20 at 02:37
  • 2
    Why are you calling `localtime` that many times inside argument list? Why not call it once? Isn't it undefined behavior? Does your code depend on specific order of arguments evaluation? Both `ctime` and `localtime` return pointers to statically allocated memory, __same__ pointer each time. Of course it's all going to be the same. – KamilCuk Nov 29 '20 at 02:41
  • @KamilCuk I see what you mean; that was stupid of me. This isn't my actual production code but is just a file I've been messing with to try to understand the results and I just copied the lines over to print without considering that. I don't know if it is undefined behavior. It should be pointing to same data and return the same result even if it is inefficient but I'll fix that and see, Thanks. – Gary Nov 29 '20 at 02:50
  • @KamilCuk - the repeated `localtime` calls wouldn't be UB, you would just be overwriting the (potentially static) internal buffer with the same value many times unnecessarily. Can you confirm the `localtime` values are the correct date/time. – David C. Rankin Nov 29 '20 at 02:59
  • Yes, but what about `->` ? `->` are not sequenced. When `localtime` returns the same pointer (as it does), `->` evaluates the same pointer unsequenced. – KamilCuk Nov 29 '20 at 03:00
  • Hmm... yes, I see what you are saying, but the function returning the `struct tm` would be sequenced before the dereference and `&info.st_atime` doesn't change. – David C. Rankin Nov 29 '20 at 03:08
  • 1
    @KamilCuk I changed the code as you indicated and showed it in the question. Now all the results look fine. I was just being stupid and not understanding what I was doing. Thanks. – Gary Nov 29 '20 at 03:09
  • 1
    Sorry to ask a question like this when the problem was my not understanding and being stupid. Should I delete it? – Gary Nov 29 '20 at 03:11
  • No worries and I believe do not delete it. The best in my opinion: revert your changes back to original code, as it showed the problem you were facing. – KamilCuk Nov 29 '20 at 03:14
  • No, it's up to you it's actually a fairly helpful question. @KamilCuk - was right, it isn't the multiple calls per-se that are UB, it is the multiple calls within the `printf()` parameter list that are indeterminately sequenced that causes the problem. (it just helped to finally see how you were generating the output) – David C. Rankin Nov 29 '20 at 03:15
  • 1
    @KamilCuk Okay, thanks. I never took the incorrect code out, but just added the changes in a block below. – Gary Nov 29 '20 at 03:16
  • That's how you are supposed to do it -- so you are fine. Never delete from your question -- otherwise answers and comments before your deletion will no longer make sense. – David C. Rankin Nov 29 '20 at 03:17
  • 1
    @DavidC.Rankin Thanks a lot for your help. I won't delete it then. I get away with things in JavaScript that one can't in C. I'm glad to learn it. – Gary Nov 29 '20 at 03:18

1 Answers1

2

You are basically doing:

static char buffer[20]; // static buffer internal for asctime
char *my_asctime(int a) { // asctime
    snprintf(buffer, 20, "%d", a); // asctime converts the input to some output
    return buffer; // and returns pointer to internal buffer
}
int main() {
    printf("%s %s %s %s\n", 
       my_asctime(1),
       my_asctime(2),
       my_asctime(3),
       my_asctime(4)
   );
    // **Any** of the outputs are valid:
    // 1 1 1 1
    // 2 2 2 2
    // 3 3 3 3
    // 4 4 4 4
}

All asctime (sane implementations) write to the same memory and return the same pointer. So when printf is executed it's just going to print the content of the same memory. Note that the order of evaluation of arguments to functions are unsequenced with each other, but order of evaluation of function calls is indeterminately sequenced. Any one of the possible result of asctime will be valid. Use asctime_r or call one after another in separate printfs.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111