-1

How to convert from string like “2021-11-15 12:10" in UTC to Unix timestamp?

Then add two hours to timestamp (like timestamp += 60*60*2).

Then convert resulting timestamp to string in the same format in UTC?

Using <chrono>, <ctime>, or library, doesn’t really matter.

bolt
  • 387
  • 2
  • 6
  • 16
  • What you have done so far, post it here. – Abhishek Dutt Nov 07 '21 at 04:35
  • Is it an invariant that the resultant string always indicate a time 2 hours past the original string? If so, then it is best to not interpret the strings as local time, but rather UTC. Otherwise daylight saving time transitions can introduce errors. Or a mobile device changing time zones between parse and format could introduce errors. On the other hand, those "errors" may be exactly what you want to happen. It is good to have a clear idea of what you want to happen in corner cases like this. – Howard Hinnant Nov 07 '21 at 15:25
  • Possible duplicate: https://stackoverflow.com/q/17681439/10871073 – Adrian Mole Nov 07 '21 at 17:05

2 Answers2

3

You could use the I/O manipulators std::get_time and std::put_time.

Example:

#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>

int main() {
    std::istringstream in("2021-11-15 12:10"); // put the date in an istringstream
    
    std::tm t{};
    t.tm_isdst = -1; // let std::mktime try to figure out if DST is in effect

    in >> std::get_time(&t, "%Y-%m-%d %H:%M"); // extract it into a std::tm
    
    std::time_t timestamp = std::mktime(&t);   // get epoch

    timestamp += 2 * 60 *60;                   // add 2 hours

    std::tm utc = *std::gmtime(&timestamp);    // make a UTC std::tm
    std::tm lt = *std::localtime(&timestamp);  // make a localtime std::tm
    
    // print the results:
    std::cout << std::put_time(&utc, "%Y-%m-%d %H:%M") << '\n'
              << std::put_time(&lt, "%Y-%m-%d %H:%M") << '\n';
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • It returns random dates. Check here - https://replit.com/@biggerevil/PreviousGiantState#main.cpp – bolt Nov 07 '21 at 05:04
  • 1
    @bolt Oups - I forgot to initialize `t`. I fixed that now. – Ted Lyngmo Nov 07 '21 at 05:10
  • Now it works! So the mistake was because of {} after t? Didn’t expect this to work this way, been struggling for several hours with that. Big thanks to you! – bolt Nov 07 '21 at 05:17
  • 1
    Yeah, without `{}` the `tm` will be filled with "random" data. You're welcome! – Ted Lyngmo Nov 07 '21 at 05:18
  • 1
    I am concerned `std::tm t{};` will zero the `.tm_isdst` member and `std::mktime(&t);` when it should use ` `tm.tm_isdst = -1;`. In your time zone, it might be standard time, on 2021-11-15, but is others, it is not. Using -1 allows `mktime()` to figure it out. – chux - Reinstate Monica Nov 07 '21 at 06:08
  • 1
    @chux-ReinstateMonica Yeah, I agree. Added. – Ted Lyngmo Nov 07 '21 at 06:33
  • Alterative:, as no need for `std::time_t timestamp ... timestamp += 2 * 60 *60;`. Simply: `std::tm t{}; t.tm_isdst = -1; get_time(&t, "%Y-%m-%d %H:%M"); t.t.tm_hour += 2; mktime(&t); put_time(&lt, "%Y-%m-%d %H:%M");` – chux - Reinstate Monica Nov 07 '21 at 17:05
  • @chux-ReinstateMonica I think that's risky. I don't think `tm_hour`s over 23 are guaranteed to be supported (but I could be wrong). – Ted Lyngmo Nov 07 '21 at 17:15
  • 1
    @TedLyngmo Its OK "The mktime function converts the broken-down time, expressed as local time ... The original values of the tm_wday and tm_yday components of the structure are ignored, and the original values of the other components are not restricted to the ranges indicated above. ... and the other components are set to represent the specified calendar time, but with their values forced to the ranges indicated above. " C17dr § 7.27.2.3 2 – chux - Reinstate Monica Nov 07 '21 at 22:43
  • @chux-ReinstateMonica Aha, great! – Ted Lyngmo Nov 08 '21 at 05:29
1

With the recent edit that the input string represents a UTC date/time, the problem becomes both simpler and more complex.

  • Simpler in that there are no complications with time zones and daylight saving and politicians.
  • More complex in that there is no portable C or C++ solution (prior to C++20), though there are platform-dependent solutions.

This answer proposes a portable solution that is new in C++20. However if you don't have C++20, there exists a free, open-source, header-only solution that previews this part of C++20 which works with C++11/14/17 and on all platforms.

#include <chrono>
#include <fmt>
#include <iostream>
#include <sstream>

std::string
test(std::string ts)
{
    using namespace std::chrono;

    std::istringstream in{std::move(ts)};
    sys_seconds tp;
    in >> parse("%F %H:%M", tp);
    return std::format("{:%F %H:%M}", tp + 2h);
}

int
main()
{
    std::cout << test("2021-11-15 12:10") << '\n';
}

The above is the C++20 solution. The type sys_seconds is a Unix timestamp. It counts seconds since 1970-01-01 00:00:00 UTC, excluding leap seconds. If you want the number of seconds out of it, that is tp.time_since_epoch().count().

One simply parses that, adds two hours to it, and formats it. The local time zone is never considered, and so there are no corner cases. This simply always works.

For example if the local time zone is "America/New_York", and the input string is "2021-11-07 00:10", then the output string is "2021-11-07 02:10". However if this input string is used with the currently accepted answer, then the output string is an hour less: "2021-11-07 01:10".

To use the free, open-source, header-only solution:

  1. Replace #include <fmt> with #include "date/date.h"
  2. Add using namespace date;
  3. Change the last line to return format("%F %H:%M", tp + 2h);
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577