25

the scenario is: I get datetime in format "YYYY-MM-DD HH:MM:SS" with libexif. To minimize the saving cost, I wanna convert the datetime to unix timestamp or alike which only cost 64bit or 32bit. Is there any explicit way with c?

liuliu
  • 721
  • 1
  • 8
  • 19

7 Answers7

38

You could try a combination of strptime and mktime

struct tm tm;
time_t epoch;
if ( strptime(timestamp, "%Y-%m-%d %H:%M:%S", &tm) != NULL )
  epoch = mktime(&tm);
else
  // badness
Kjetil Joergensen
  • 1,595
  • 11
  • 10
  • 4
    I knew there had to be something like strptime, but I couldn't find it. I use Microsoft 99% of the time, and they don't support it. – Mark Ransom Jun 16 '09 at 16:40
  • +1: I was thinking of strptime, but I couldn't remember the function name for it. – Powerlord Jun 16 '09 at 16:42
  • 3
    As a side note, if you're using Windows, see this question: http://stackoverflow.com/questions/321849/strptime-equivalent-on-windows – Powerlord Jun 16 '09 at 16:44
  • 1
    I got `Conditional jump or move depends on uninitialised value(s)` with valgrind, see same question here: https://stackoverflow.com/questions/9037631/valgrind-complaining-about-mktime-is-that-my-fault. Setting the structure `tm` all to zero helps. – Andre Kampling Jul 18 '17 at 07:47
  • 3
    I would suggest `timegm()` instead of `mktime()` to avoid problems with timezones. – Alexis Wilke Mar 31 '18 at 21:33
  • However, my version of strptime() may stop before the timezone, that is return NULL if you specify "%Y-%m-%dT%H:%M:%S%z" (RFC 3339), if seconds contain a fractional part. I'll ty and file a bug... – Ale May 26 '23 at 11:12
5

Convert each part of the date/time into an integer to populate a struct tm, then convert that into a time_t using mktime.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • `mktime()` is often the wrong function, `timegm()` is more likely to be the correct one. It won't take the timezone in account. – Alexis Wilke Mar 31 '18 at 21:32
1

Here is a wired solution in c/pseudo code I just hacked together. Good luck!

char * runner = NULL;
char *string_orig = "YYYY-MM-DD HH:MM:SS";
time_t time = 0;
struct tm tmp;

use strstr(string_orig, "-") and atoi foreach

  tmp->tm_year .. 
  tmp->tm_mon  .. 
  tmp->tm_mday ..  
  tmp->tm_hour  .. 
  tmp->tm_min  .. 
  tmp->tm_sec .. 

with *runner as help

time = mktime(&tm)
merkuro
  • 6,161
  • 2
  • 27
  • 29
1

What about sscanf?

struct tm tmVar;
char *strVar = "YYYY-MM-DD HH:MM:SS";
time_t timeVar;
if(sscanf(strVar, "%d-%d-%d %d:%d:%d", &tm.tm_year, /* the other fields */)==6)
    timeVar = mktime(&tmVar);
else
    // bad format
0

Here's a ready snippet when strptime is not available:

#include <stddef.h>
#include <time.h> 
#include <stdio.h> 

time_t string_to_seconds(const char *timestamp_str)
{
    struct tm tm;
    time_t seconds;
    int r;

    if (timestamp_str == NULL) {
        printf("null argument\n");
        return (time_t)-1;
    }
    r = sscanf(timestamp_str, "%d-%d-%d %d:%d:%d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
    if (r != 6) {
        printf("expected %d numbers scanned in %s\n", r, timestamp_str);
        return (time_t)-1;
    }

    tm.tm_year -= 1900;
    tm.tm_mon -= 1;
    tm.tm_isdst = 0;
    seconds = mktime(&tm);
    if (seconds == (time_t)-1) {
        printf("reading time from %s failed\n", timestamp_str);
    }

    return seconds;
}

Adjust youself a string in sscanf to what you need. To ignore time zone and convert always as GMT/UTC substract a timezone (or _timezone) from seconds (timezone global is defined in time.h. DST is already ignored by zeroing tm_isdst field of tm.

tutejszy
  • 602
  • 7
  • 22
0

Linux supports the getdate() function, which I think is more practical than calling strptime() directly. This is because the getdate() function automatically checks many formats for you. It is an equivalent to calling strptime() with various formats until the function works or all formats were tested.

// setenv() should be called only once
setenv("DATEMSK", "/usr/share/myprog/datemsk.fmt", 0);

// convert a date
struct tm * t1(getdate("2018-03-31 14:35:46"));
if(t1 == nullptr) ...handle error...
time_t date1(timegm(t1));

// convert another date
struct tm * t2(getdate("03/31/2018 14:35:46"));
if(t2 == nullptr) ...handle error...
time_t date2(timegm(t2));

Note: timegm() is similar to mktime() except that it ignores the locale and uses UTC. In most cases that's the right way to convert your dates.

The datemsk.fmt file would include at least these two formats to support the above dates:

%Y-%b-%d %H:%M:%S
%b/%d/%Y %H:%M:%S

The number of supported formats is not limited, although you may not want to have too many. It's going to be rather slow if you have too many formats. You could also dynamically manage your formats and call strptime() in a loop.

Linux also offers a getdate_r() function which is thread safe.

Man Page: http://pubs.opengroup.org/onlinepubs/7908799/xsh/getdate.html

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
0

for anyone using gcc, following code is working for me:

#define YEAR_OFFSET 1900

struct tm* dt = (struct tm*)malloc(sizeof(struct tm));
char *strVar = "2022-08-13 23:09:47"; //! "YYYY-MM-DD HH:MM:SS"
time_t timeVar;
if(sscanf(strVar, "%d-%hhd-%hhd %hhd:%hhd:%hhd", &dt->tm_year, &dt->tm_mon, &dt->tm_mday, &dt->tm_hour, &dt->tm_min, &dt->tm_sec)==6)
{
    dt->tm_year -= YEAR_OFFSET;
    dt->tm_mon -= 1;
    timeVar = mktime(&tmVar);
}
else
    ;// bad format
free(dt);