15

What is the state of the art way to get date and time as string in c++11?

I know about std::put_time, but the reference says I shall use it only in streams.

There is std::chrono::system_clock which provides to_time_t returning the time as time_t and lacking the date, doesn't it?

I could use a stringstream like bames53: Outputting Date and Time in C++ using std::chrono but that seems to be a workaround.

Community
  • 1
  • 1
kiigass
  • 427
  • 1
  • 4
  • 16

5 Answers5

18

Using Howard Hinnant's free, open-source header-only datetime library, you can get the current UTC time as a std::string in a single line of code:

std::string s = date::format("%F %T", std::chrono::system_clock::now());

I just ran this, and the string contains:

2017-03-30 17:05:13.400455

That's right, it even gives you the full precision. If you don't like that format, all of the strftime formatting flags are available. If you want your local time, there is a timezone library also available, though it is not header only.

std::string s = date::format("%F %T %Z", date::make_zoned(date::current_zone(),
                                         std::chrono::system_clock::now()));

Output:

2017-03-30 13:05:13.400455 EDT

Update for C++20

This can now be done with the std::lib headers <chrono> and <format>:

#include <chrono>
#include <format>
#include <string>

int
main()
{
    std::string s1 = std::format("{:%F %T}", std::chrono::system_clock::now());
    std::string s2 = std::format("{:%F %T %Z}",
        std::chrono::zoned_time{std::chrono::current_zone(),
                                std::chrono::system_clock::now()});
}

Demo.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 2
    Yes, date.h is 100% thread safe. It does not use the underlying thread-unsafe C API, nor does it rely on thread-unsafe practices such as setting environment variables. The time zone library is also thread safe, including the initialization of the timezone library upon first use (which depends on C++11 thread safe function local statics). – Howard Hinnant Sep 20 '17 at 11:24
  • 1
    The only part that relies on user-supplied synchronization is the rarely used feature of re-loading the timezone database after first use (which one might use after the application has been running for months). And this characteristic is fully documented: https://howardhinnant.github.io/date/tz.html#database – Howard Hinnant Sep 20 '17 at 11:24
  • 1
    Thank you for this great library Howard. I was able to replace 7 lines using the unsafe and clunky strftime() into 1 line! Please propose to the Standard Committee (if you haven't already). – Ricky65 Nov 18 '17 at 15:46
  • Thanks @Ricky65! You can find the current standardization process of this library near the bottom of the README here: https://github.com/HowardHinnant/date – Howard Hinnant Nov 18 '17 at 16:15
  • Thanks for the link. Hopefully it will make it into C++20. Marvelous! Thanks for proposing. – Ricky65 Nov 19 '17 at 13:00
  • It uses the C API because std::chrono is heavily reliant on the C standard library in most implementations that matter. std::chrono is just a thin wrapper on top of the C standard library by the way. – Something Something Jul 31 '23 at 01:18
  • The C API won't do sub-second precision. `std::chrono` will. The C API has two time zones: UTC and local. `std::chrono` has those two plus all the [IANA time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). The C API doesn't specify whether or not leap seconds are counted, and in practice never does (tracks [Unix Time](https://en.wikipedia.org/wiki/Unix_time)). `std::chrono` will let you compute both in Unix Time and in true UTC with leap seconds. `std::chrono` makes it trivial to compute the 3rd Thu of May this year. Not so in the C API. Etc., etc. – Howard Hinnant Jul 31 '23 at 01:28
7

Simpler:

string CurrentDate()
{
    std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());

    char buf[100] = {0};
    std::strftime(buf, sizeof(buf), "%Y-%m-%d", std::localtime(&now));
    return buf;
}

Adjust the format as appropriate for the time too.

Note that I suspect this will not work well for multithreaded code since std::localtime() returns a pointer to an internal struct.

Timmmm
  • 88,195
  • 71
  • 364
  • 509
6

Firstly, std::time_t indeed captures both date and time, since it generally represents seconds from January 1st, 1970.

There is no great support for handling dates in C++11. You still have to depend on boost if you don't wish to do it, mostly, manually. Here's how to do it manually.

You can use it—in a thread-safe way—together with any std::chrono::*clock, such as std::system_clock, like this:

std::string get_date_string(std::chrono::time_point t) {
  auto as_time_t = std::chrono::system_clock::to_time_t(t);
  struct tm tm;
  if (::gmtime_r(&as_time_t, &tm))
    if (std::strftime(some_buffer, sizeof(some_buffer), "%F", &tm))
      return std::string{some_buffer};
  throw std::runtime_error("Failed to get current date as string");
}

Somewhere else, you can issue:

get_date_string(std::system_clock::now());

The relatively good thing about this solution is that, at the API level, you're still using modern, portable C++ concepts such as std::chrono::time_point, and of course, std::string.

Yam Marcovic
  • 7,953
  • 1
  • 28
  • 38
  • 2
    Nice. You might want to use `std::chrono::system_clock::time_point` for the function argument. The buffer `some_buffer` is char array (e.g. `char some_buffer[64];`) that must be declared inside the function and must not be static (otherwise the whole thread safety is lost). You could reuse the `std::string` internal buffer for that (resize, then contract) and avoid copying. – the swine Jul 17 '18 at 00:53
4

timestamp like this;

#include <iostream>
#include <chrono>
#include <ctime>

int main() {

    std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
    std::time_t start_time = std::chrono::system_clock::to_time_t(now);
    char timedisplay[100];
    struct tm buf;
    errno_t err = localtime_s(&buf, &start_time);
    if (std::strftime(timedisplay, sizeof(timedisplay), "%H:%M:%S", &buf)) {
        std::cout << timedisplay << '\n';
    }
}

Date in a similar manner.

Angus Comber
  • 9,316
  • 14
  • 59
  • 107
3

You can use the code snippet given below as it will serve your purpose. Here use time.h header file for required localtime() function and then using the strftime() function with required parameters will give the output and it returns it as a string.

#include <iostream>
#include <string>
#include <time.h>
std::string current_date();
std::string current_time();
int main(){
    std::cout<<"Current date => "<<current_date()<<"\n";
    std::cout<<"Current time => "<<current_time()<<"\n";
}
std::string current_date(){
    time_t now = time(NULL);
    struct tm tstruct;
    char buf[40];
    tstruct = *localtime(&now);
    //format: day DD-MM-YYYY
    strftime(buf, sizeof(buf), "%A %d/%m/%Y", &tstruct);
    return buf;
}
std::string current_time(){
    time_t now = time(NULL);
    struct tm tstruct;
    char buf[40];
    tstruct = *localtime(&now);
    //format: HH:mm:ss
    strftime(buf, sizeof(buf), "%X", &tstruct);
    return buf;
}
Abhishek Rathore
  • 1,016
  • 1
  • 11
  • 13
  • Since `std::string` is returned, temporal `std::string buf` is a better option. That would remove an extra overhead for constructing string from a char buffer. In provided example `return buf` == `std::string(buf)`, which invoke `memcpy` to copy 'buf' into newly created return sting. After all: `std::string buf; buf.resize(40); ... strftime((char*)buf.data(), 40, "%A %d/%m/%Y", &tstruct);` – user3124812 Jun 21 '19 at 05:20