How to use the millisecond in strptime(3)

113 Views Asked by At

I'd like to format the 2023/10/04T11:41:03.553Z with strptime(3) %Y/%m/%dT%H:%M:%S.???Z.

What is the input field descriptor of fraction of seconds ?

Although I read through the document of strptime(3), I couldn't find any input field descriptor which corresponds to "millisecond".

2

There are 2 best solutions below

6
chux - Reinstate Monica On

Function strptime() in not part of any Standard C library. It is well defined in *nix systems. It commonly uses a pointer to struct tm.

struct tm is defined to have at least 9 members. It may have more. There is no specified member corresponding to time units finer than seconds.

Sometimes struct tm has a member for milliseconds, microseconds, etc. Yet that is implementation dependent.

Referenced strptime() simply does not have a specifier for sub-seconds. Code needs to handle such optional members in some other fashion.


struct tm woes

Without "%Z", strptime() forms the struct tm without adjusting .tm_isdst. In general, strptime() does not certainly assign all struct tm members.

Initialize struct tm prior to the strptime().

struct tm time_stamp = { 0 };

Sample validation:

// Return error flag
bool testit(struct tm *t, int *ms, const char *date) {
  memset(t, 0, sizeof *t);

  const char *s = strptime(date, "%Y/%m/%dT%H:%M:%S", &t);
  if (s == NULL) {
    return true;
  }

  int n = 0;
  sscanf(s, ".%3dZ%n", ms, &n);
  return (n < 5 || s[5] != 0);
}

For the pedantic: detect rump milliseconds as exactly as 3 digits.

unsigned char *uc = (unsigned char *)s; 
if (uc[0] != '.' || !isdigit(uc[1]) || !isdigit(uc[2])
    || !isdigit(uc[3]) || uc[4] != 'Z' || uc[5]) {
  return true;
}
uc[4] = 0;
*ms = atoi(s + 1);
return false;
3
chenzhongpu On

A workaround way is to design your own struct tm which contains a member for milliseconds:

struct tm2 {
  struct tm tm;
  long ms;
};

Then you have to parse the milliseconds part manually (I haved updated the code according to @Fe2O3's comment):

  const char *date = "2023/10/04T11:41:03.553Z";
  struct tm2 tm2;

  char *peroid = strptime(date, "%Y/%m/%dT%H:%M:%S", &tm2.tm);
  if (peroid == NULL) {
    printf("error format!\n");
  } else if (*peroid == '.') {
    char *endstr;
    tm2.ms = strtol(peroid + 1, &endstr, 10);
    if (*endstr != 'Z') {
      printf("error format!\n");
    }
  } else {
    printf("error format!\n");
  }

To make the code more robust, you can adjust error checking code.