HowardHinnant / date

A date and time library based on the C++11/14/17 <chrono> header
Other
3.08k stars 670 forks source link

Does the library support to convert a std::string to epoch without using std::istringstream #731

Open Jack012a opened 2 years ago

Jack012a commented 2 years ago
https://stackoverflow.com/questions/4137748/c-converting-a-time-string-to-seconds-from-the-epoch

#include "tz.h"
#include <iostream>
#include <sstream>

int
main()
{
    std::istringstream is("2010-11-04T23:23:01Z");
    is.exceptions(std::ios::failbit);
    date::sys_seconds tp;
    date::parse(is, "%FT%TZ", tp);
    std::cout << "seconds from epoch is " << tp.time_since_epoch().count() << "s\n";
}

Question> Does the library offer an interface where I can use std::string directly without passing it through a std::istringstream

Thank you

HowardHinnant commented 2 years ago

No, not directly. However there are lots of existing ways to parse int. Here is an example of using C++17's from_chars to parse the ints from a string and then turn the ints into a sys_seconds:

#include "date/date.h"
#include <charconv>
#include <iostream>
#include <string>
#include <system_error>

int
main()
{
    std::string is("2010-11-04T23:23:01Z");
    //              01234567890123456789
    int y, m, d, h, M, s;
    auto r = std::from_chars(&is[0], &is[4], y);
    if (static_cast<bool>(r.ec))
        throw std::runtime_error("error parsing year");
    if (*r.ptr++ != '-')
        throw std::runtime_error("error parsing first '-'");
    r = std::from_chars(r.ptr, &is[7], m);
    if (static_cast<bool>(r.ec))
        throw std::runtime_error("error parsing month");
    if (*r.ptr++ != '-')
        throw std::runtime_error("error parsing second '-'");
    r = std::from_chars(r.ptr, &is[10], d);
    if (static_cast<bool>(r.ec))
        throw std::runtime_error("error parsing day");
    if (*r.ptr++ != 'T')
        throw std::runtime_error("error parsing 'T'");
    r = std::from_chars(r.ptr, &is[13], h);
    if (static_cast<bool>(r.ec))
        throw std::runtime_error("error parsing hour");
    if (*r.ptr++ != ':')
        throw std::runtime_error("error parsing first ':'");
    r = std::from_chars(r.ptr, &is[16], M);
    if (static_cast<bool>(r.ec))
        throw std::runtime_error("error parsing minute");
    if (*r.ptr++ != ':')
        throw std::runtime_error("error parsing second ':'");
    r = std::from_chars(r.ptr, &is[19], s);
    if (static_cast<bool>(r.ec))
        throw std::runtime_error("error parsing second");
    if (*r.ptr++ != 'Z')
        throw std::runtime_error("error parsing 'Z'");
    auto tp = date::sys_days{date::year{y}/m/d} + std::chrono::hours{h} +
                       std::chrono::minutes{M} + std::chrono::seconds{s};
    std::cout << "seconds from epoch is " << tp.time_since_epoch().count() << "s\n";
}

The output is:

seconds from epoch is 1288912981s
Jack012a commented 2 years ago

https://howardhinnant.github.io/date/date.html#to_stream_formatting

%z The offset from UTC in the ISO 8601 format. For example -0430 refers to 4 hours 30 minutes behind UTC. If the offset is zero, +0000 is used. The modified commands %Ez and %Oz insert a : between the hours and minutes: -04:30. If the offset information is not available, failbit will be set.

This is how I currently use the function to convert time string to epoch seconds.

std::chrono::system_clock::time_point tp;
std::istringstream is("2022-04-10T03:10:02.551112+00:00");
is >> date::parse("%FT%T%Oz", tp);
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(
                         tp.time_since_epoch())
                         .count();

Question> Is there a difference between %Ez and %Oz? I tested the output and didn't see any difference.

Thank you again for this awesome date library!

HowardHinnant commented 2 years ago

There is no difference between %Ez and %Oz.

Thanks for the feedback!