2

I have followed some good answers to similar questions like this one.

Yet my code seems to give output one hour later after converting string to time_point and back to string.

The code that gives the wrong answer:

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

using date_time = std::chrono::system_clock::time_point;

std::string dateTimeToString(date_time time) {
    std::time_t now_c = std::chrono::system_clock::to_time_t(time);
    auto tm = std::localtime(&now_c);
    char buffer[32];
    std::strftime(buffer, 32, "%Y-%m-%d %H:%M:%S", tm);
    return std::string(buffer);
}

date_time stringToDateTime(const std::string &s) {
    std::tm timeDate = {};
    std::istringstream ss(s);
    ss >> std::get_time(&timeDate, "%Y-%m-%d %H:%M:%S");
    return std::chrono::system_clock::from_time_t(mktime(&timeDate));
}

int main() {
    std::string birthday = "2000-06-05 20:20:00";
    std::cout << "Two birthday dates: \n" << birthday << " \nsecond one:\n" << dateTimeToString(stringToDateTime(birthday))
              << "\n******************\n";
    return 0;
}

And the output:

Two birthday dates: 2000-06-05 20:20:00 second one: 2000-06-05 21:20:00


I have thought that this has something to do with timezones, but I am unable to solve this problem. What is wrong with my code?

Spideyyyy
  • 123
  • 4

2 Answers2

1

You can use boost library for time management.

Look at boost::date_time::parse_date(const std::string& s, int order_spec = ymd_order_iso):

//! Generic function to parse a delimited date (eg: 2002-02-10)
/*! Accepted formats are: "2003-02-10" or " 2003-Feb-10" or
 * "2003-Feburary-10"
 * The order in which the Month, Day, & Year appear in the argument
 * string can be accomodated by passing in the appropriate ymd_order_spec
 */

And at boost::date_time::parse_delimited_time_duration(const std::string& s):

//! Creates a time_duration object from a delimited string
/*! Expected format for string is "[-]h[h][:mm][:ss][.fff]".
 * If the number of fractional digits provided is greater than the 
 * precision of the time duration type then the extra digits are 
 * truncated.
 *
 * A negative duration will be created if the first character in
 * string is a '-', all other '-' will be treated as delimiters.
 * Accepted delimiters are "-:,.". 
 */

Boost made a wrapper function to those both methods, that can parse a date-time string:

template<class time_type>
inline time_type parse_delimited_time(const std::string& s, char sep) {
    typedef typename time_type::time_duration_type time_duration;
    typedef typename time_type::date_type date_type;

    //split date/time on a unique delimiter char such as ' ' or 'T'
    std::string date_string, tod_string;
    split(s, sep, date_string, tod_string);
    //call parse_date with first string
    date_type d = parse_date<date_type>(date_string);
    //call parse_time_duration with remaining string
    time_duration td = parse_delimited_time_duration<time_duration>(tod_string);
    //construct a time
    return time_type(d, td);

}

If you want a different date format parser, I made a slightly different implementation:

posix_time::ptime parse_dmy_time(const std::string &s, char sep) {
    typedef typename posix_time::ptime::time_duration_type time_duration;
    typedef typename posix_time::ptime::date_type date_type;

    //split date/time on a unique delimiter char such as ' ' or 'T'
    std::string date_string, tod_string;
    split(s, sep, date_string, tod_string);
    //call parse_date with first string
    auto d = parse_date<date_type>(date_string, ymd_order_dmy);
    //call parse_time_duration with remaining string
    auto td = parse_delimited_time_duration<time_duration>(tod_string);
    //construct a time
    return {d, td};
}
Coral Kashri
  • 3,436
  • 2
  • 10
  • 22
  • Since this is a group project, I would prefer not to add more external libraries if not necessary as to avoid complications. – Spideyyyy Feb 14 '20 at 17:40
  • @Spideyyyy `boost` is very recommended library, and it known as complications reducer relatively to the STL, and its objects are very easy to use. I would really consider to use this library, especially in big projects. This library also comes with extra data structures you might need in the future. You can read about boost advantages: https://stackoverflow.com/questions/125580/what-are-the-advantages-of-using-the-c-boost-libraries – Coral Kashri Feb 14 '20 at 21:02
1

Not sure it helps but works for me. Add the UTC offset and TZ info (%z and %Z) in both steps:

std::string dateTimeToString(date_time time) {
    std::time_t now_c = std::chrono::system_clock::to_time_t(time);
    auto tm = std::localtime(&now_c);
    char buffer[32];
    std::strftime(buffer, 32, "%Y-%m-%d %H:%M:%S%z %Z", tm);
    return std::string(buffer);
}

date_time stringToDateTime(const std::string& s) {
    std::tm timeDate = {};
    std::istringstream ss(s);
    ss >> std::get_time(&timeDate, "%Y-%m-%d %H:%M:%S%z %Z");
    return std::chrono::system_clock::from_time_t(mktime(&timeDate));
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • @Spideyyyy "_Do you live in UTC?_" - No, the [UTC offset](https://en.wikipedia.org/wiki/UTC_offset) is the difference in hours and minutes from Coordinated Universal Time (UTC) for a particular place and date. I just noticed that `get_time` does not parse `%z` and `%Z` though, so I need to look closer at that part later. – Ted Lyngmo Feb 17 '20 at 06:55