41

Is there a good equivalent implementation of strptime() available for Windows? Unfortunately, this POSIX function does not appear to be available.

Open Group description of strptime - summary: it converts a text string such as "MM-DD-YYYY HH:MM:SS" into a tm struct, the opposite of strftime().

An̲̳̳drew
  • 13,375
  • 13
  • 47
  • 46

7 Answers7

36

If you don't want to port any code or condemn your project to boost, you can do this:

  1. parse the date using sscanf
  2. then copy the integers into a struct tm (subtract 1 from month and 1900 from year -- months are 0-11 and years start in 1900)
  3. finally, use mktime to get a UTC epoch integer

Just remember to set the isdst member of the struct tm to -1, or else you'll have daylight savings issues.

svick
  • 236,525
  • 50
  • 385
  • 514
amwinter
  • 3,121
  • 2
  • 27
  • 25
  • 8
    Note, that `mktime` works with dates in range about 1970 ~ 2038, but you can use [`_mktime64`](http://msdn.microsoft.com/en-us/library/d1y53h2a%28v=vs.80%29.aspx) which works with dates in range 1970 ~ 3000 :) – LihO Dec 07 '12 at 17:15
  • 1
    Sometimes it makes sense to fill `isdst` with its current value, you can get it with `localtime(&current_time)->tm_isdst;`, where `current_time` is current time in `time_t` format, as returned by `time(&current_time)`. – user Oct 19 '14 at 08:46
  • @amwinter how about tm_wday and tm_yday ? – aderchox Dec 11 '19 at 16:58
  • 1
    @aderchox simply use mktime() to normalize the time and you will get a correct tm_wday and tm_yday. – Sam Sep 26 '20 at 09:57
  • Ad 2. Not needed if sscan into &t.tm_day directly Remember to zero init the struct e.g.: struct tm my_time{0}; – vSzemkel Sep 29 '20 at 08:12
  • 1
    Avoid setting `tm_isdst = -1;` I wrote about that at length here: https://stackoverflow.com/questions/8558919/mktime-and-tm-isdst – Rich Jahn Oct 04 '21 at 17:46
29

Assuming you are using Visual Studio 2015 or above, you can use this as a drop-in replacement for strptime:

#include <time.h>
#include <iomanip>
#include <sstream>

extern "C" char* strptime(const char* s,
                          const char* f,
                          struct tm* tm) {
  // Isn't the C++ standard lib nice? std::get_time is defined such that its
  // format parameters are the exact same as strptime. Of course, we have to
  // create a string stream first, and imbue it with the current C locale, and
  // we also have to make sure we return the right things if it fails, or
  // if it succeeds, but this is still far simpler an implementation than any
  // of the versions in any of the C standard libraries.
  std::istringstream input(s);
  input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
  input >> std::get_time(tm, f);
  if (input.fail()) {
    return nullptr;
  }
  return (char*)(s + input.tellg());
}

Just be aware that for cross platform applications, std::get_time wasn't implemented until GCC 5.1, so switching to calling std::get_time directly may not be an option.

Orvid King
  • 1,188
  • 10
  • 16
  • 3
    Thanks for being copy-pastable. – chtenb Nov 23 '16 at 16:18
  • 1
    but std::get_time is broken on VS2015 [here](http://stackoverflow.com/questions/35041344/trying-to-use-stdget-time-to-parse-yymmdd-and-failing#comment69466398_35041344) – dashesy Dec 13 '16 at 21:58
  • It isn't broken; what they were trying to do doesn't work with GCC or Clang either, and, although I've not specifically tested it, it likely doesn't work with strptime either. – Orvid King Dec 31 '16 at 01:57
  • +1 but to get it to work I needed to add the following line after the `get_time()` line: `if(input.eof()) return (char *)(s + strlen(s));` – jez May 23 '21 at 21:49
  • There's a difference in behavior between these two (GCC11 and end-of-2021 glibc and STL): This C++ versions rejects `strptime("2018-11-41", "%Y-%m-%d", x)`, returning a `nullptr`, while the C version succeeds, leaving the trailing `"1"` unparsed. – Jan Kundrát Dec 10 '21 at 19:36
  • As i was using this function a lot i noticed that `std::locale(setlocale(LC_ALL, nullptr))` was slowing down the function by a lot, so you may want to take it outside the function in a variable et reuse the value each time instead of re-instanciating it at every call. – Tomtix Oct 22 '22 at 23:22
16

An open-source version (BSD license) of strptime() can be found here: http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/time/strptime.c?rev=HEAD

You'll need to add the following declaration to use it:

char *strptime(const char * __restrict, const char * __restrict, struct tm * __restrict);
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 1
    Visual Studio 2008 complains that "fatal error C1083: Cannot open include file: 'sys/cdefs.h': No such file or directory" – Ozair Kafray Nov 01 '18 at 10:44
  • 4
    How is that the accepted answer. It uses a bunch headers which are not included in windows. –  Sep 09 '19 at 14:28
  • This solution does not work on Windows (and on Linux or macOS it isn't needed!) – headbanger Dec 11 '19 at 12:28
15

This does the job:

#include "stdafx.h"
#include "boost/date_time/posix_time/posix_time.hpp"
using namespace boost::posix_time;

int _tmain(int argc, _TCHAR* argv[])
{
    std::string ts("2002-01-20 23:59:59.000");
    ptime t(time_from_string(ts));
    tm pt_tm = to_tm( t );

Notice, however, that the input string is YYYY-MM-DD

ravenspoint
  • 19,093
  • 6
  • 57
  • 103
0

This is a copy-and-paste-capable C example of the answer @amwinter posted, although I did not use sscanf() - the *scanf() family of functions is IMO too perverse to do robust parsing with:

(headers and error checking omitted to keep the example short enough to prevent a vertical scroll bar from getting created)

    // format will be YYYYmmddHHMMSSZ
    const char *notAfter = getNotAfterStringFromX509Cert( x509 );
    struct tm notAfterTm = { 0 };

#ifdef _WIN32
    char buffer[ 8 ];

    memset( buffer, 0, sizeof( buffer ) );
    strncpy( buffer, notAfter, 4 );
    notAfterTm.tm_year = strtol( buffer, NULL, 10 ) - 1900;

    memset( buffer, 0, sizeof( buffer ) );
    strncpy( buffer, notAfter + 4, 2 );
    notAfterTm.tm_mon = strtol( buffer, NULL, 10 ) - 1;

    memset( buffer, 0, sizeof( buffer ) );
    strncpy( buffer, notAfter + 6, 2 );
    notAfterTm.tm_mday = strtol( buffer, NULL, 10 );

    memset( buffer, 0, sizeof( buffer ) );
    strncpy( buffer, notAfter + 8, 2 );
    notAfterTm.tm_hour = strtol( buffer, NULL, 10 );

    memset( buffer, 0, sizeof( buffer ) );
    strncpy( buffer, notAfter + 10, 2 );
    notAfterTm.tm_min = strtol( buffer, NULL, 10 );

    memset( buffer, 0, sizeof( buffer ) );
    strncpy( buffer, notAfter + 12, 2 );
    notAfterTm.tm_sec = strtol( buffer, NULL, 10 );

    time_t result = mktime( &notAfterTm );

This is a really simple case, where the input string is in a format known exactly, so it's extremely easy to parse.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
-1

One alternative is to use GetSystemTime and send the time information to a function that parses it according to your format using vsnprintf_s. In the example below there is one function that creates a time string with milli second precision. It then sends the string to a function that formats it according to the desired format:

#include <string>
#include <cstdio>
#include <cstdarg>
#include <atlstr.h> 

std::string FormatToISO8601 (const std::string FmtS, ...) {
   CStringA BufferString;
   try {
       va_list VaList;
       va_start (VaList, FmtS);
       BufferString.FormatV (FmtS.c_str(), VaList);
   } catch (...) {}
   return std::string (BufferString);
}

void CreateISO8601String () {
   SYSTEMTIME st;
   GetSystemTime(&st);
   std::string MyISO8601String = FormatToISO8601 ("%4u-%02u-%02uT%02u:%02u:%02u.%03u", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
}
Pr0t0c0l78
  • 121
  • 6
  • 1
    You've got things backwards. The question is asking for a way to convert a **string representation** into a `struct tm`, while you have presented a (poor) implementation of [strftime](https://msdn.microsoft.com/en-us/library/fe06s4ak.aspx). – IInspectable Jun 16 '15 at 14:36
-1

There is a version of strptime() for windows available at https://github.com/p-j-miller/date-time . The same location includes a matching strftime() function and a comprehensive test program. This also works under Linux if you need to create code that works on both OS's.