mirror of
https://github.com/HowardHinnant/date.git
synced 2024-12-27 16:41:04 +08:00
Add format and parse functionality.
This commit is contained in:
parent
d3be73c664
commit
e8f95dddb7
8
date.h
8
date.h
@ -29,6 +29,7 @@
|
|||||||
# include <cmath>
|
# include <cmath>
|
||||||
#endif
|
#endif
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <locale>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <ratio>
|
#include <ratio>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -787,12 +788,14 @@ class save_stream
|
|||||||
std::ostream& os_;
|
std::ostream& os_;
|
||||||
char fill_;
|
char fill_;
|
||||||
std::ios::fmtflags flags_;
|
std::ios::fmtflags flags_;
|
||||||
|
std::locale loc_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~save_stream()
|
~save_stream()
|
||||||
{
|
{
|
||||||
os_.fill(fill_);
|
os_.fill(fill_);
|
||||||
os_.flags(flags_);
|
os_.flags(flags_);
|
||||||
|
os_.imbue(loc_);
|
||||||
}
|
}
|
||||||
|
|
||||||
save_stream(const save_stream&) = delete;
|
save_stream(const save_stream&) = delete;
|
||||||
@ -802,6 +805,7 @@ public:
|
|||||||
: os_(os)
|
: os_(os)
|
||||||
, fill_(os.fill())
|
, fill_(os.fill())
|
||||||
, flags_(os.flags())
|
, flags_(os.flags())
|
||||||
|
, loc_(os.getloc())
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3564,7 +3568,9 @@ public:
|
|||||||
os.width(2);
|
os.width(2);
|
||||||
os << std::abs(t.m_.count()) << ':';
|
os << std::abs(t.m_.count()) << ':';
|
||||||
os.width(2);
|
os.width(2);
|
||||||
os << std::abs(t.s_.count()) << '.';
|
os << std::abs(t.s_.count())
|
||||||
|
<< use_facet<numpunct<char>>(os.getloc()).decimal_point();
|
||||||
|
os.imbue(locale{});
|
||||||
#if __cplusplus >= 201402
|
#if __cplusplus >= 201402
|
||||||
CONSTDATA auto cl10 = ceil_log10(Period::den);
|
CONSTDATA auto cl10 = ceil_log10(Period::den);
|
||||||
using scale = std::ratio_multiply<Period, std::ratio<pow10(cl10)>>;
|
using scale = std::ratio_multiply<Period, std::ratio<pow10(cl10)>>;
|
||||||
|
2
tz.cpp
2
tz.cpp
@ -84,7 +84,7 @@ namespace date
|
|||||||
#if _WIN32 // TODO: sensible default for all platforms.
|
#if _WIN32 // TODO: sensible default for all platforms.
|
||||||
static std::string install{ "c:\\tzdata" };
|
static std::string install{ "c:\\tzdata" };
|
||||||
#else
|
#else
|
||||||
static std::string install{ "/Users/howardhinnant/Downloads/tzdata2016a" };
|
static std::string install{ "/Users/howardhinnant/Downloads/tzdata" };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const std::vector<std::string> files =
|
static const std::vector<std::string> files =
|
||||||
|
229
tz.h
229
tz.h
@ -57,6 +57,7 @@ Technically any OS may use the mapping process but currently only Windows does u
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
|
#include <locale>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <ratio>
|
#include <ratio>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -686,6 +687,234 @@ utc_clock::utc_to_sys(std::chrono::time_point<utc_clock, Duration> t)
|
|||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// format
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <class Duration>
|
||||||
|
std::string
|
||||||
|
format(const std::locale& loc, std::string format,
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, Duration> tp,
|
||||||
|
const Zone* zone)
|
||||||
|
{
|
||||||
|
// Handle these specially
|
||||||
|
// %S append fractional seconds if tp has precision finer than seconds
|
||||||
|
// %T append fractional seconds if tp has precision finer than seconds
|
||||||
|
// %z replace with offset from zone
|
||||||
|
// %Z replace with abbreviation from zone
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
for (auto i = 0; i < format.size(); ++i)
|
||||||
|
{
|
||||||
|
if (format[i] == '%' && i < format.size()-1)
|
||||||
|
{
|
||||||
|
switch (format[i+1])
|
||||||
|
{
|
||||||
|
case 'S':
|
||||||
|
case 'T':
|
||||||
|
if (ratio_less<typename Duration::period, ratio<1>>::value)
|
||||||
|
{
|
||||||
|
ostringstream os;
|
||||||
|
os.imbue(loc);
|
||||||
|
os << make_time(tp - floor<seconds>(tp));
|
||||||
|
auto s = os.str();
|
||||||
|
s.erase(0, 8);
|
||||||
|
format.insert(i+2, s);
|
||||||
|
i += 2 + s.size() - 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
if (zone == nullptr)
|
||||||
|
{
|
||||||
|
format.replace(i, 2, "+0000");
|
||||||
|
i += 5 - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto info = zone->get_info(tp, tz::local);
|
||||||
|
auto offset = duration_cast<minutes>(info.offset);
|
||||||
|
ostringstream os;
|
||||||
|
if (offset >= minutes{0})
|
||||||
|
os << '+';
|
||||||
|
os << make_time(offset);
|
||||||
|
auto s = os.str();
|
||||||
|
s.erase(s.find(':'), 1);
|
||||||
|
format.replace(i, 2, s);
|
||||||
|
i += s.size() - 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
if (zone == nullptr)
|
||||||
|
{
|
||||||
|
format.replace(i, 2, "UTC");
|
||||||
|
i += 3 - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto info = zone->get_info(tp, tz::local);
|
||||||
|
format.replace(i, 2, info.abbrev);
|
||||||
|
i += info.abbrev.size() - 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto& f = use_facet<time_put<char>>(loc);
|
||||||
|
ostringstream os;
|
||||||
|
auto tt = system_clock::to_time_t(tp);
|
||||||
|
std::tm tm{};
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
gmtime_r(&tt, &tm);
|
||||||
|
#else
|
||||||
|
gmtime_s(&tm, &tt);
|
||||||
|
#endif
|
||||||
|
f.put(os, os, os.fill(), &tm, format.data(), format.data() + format.size());
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <class Duration>
|
||||||
|
inline
|
||||||
|
std::string
|
||||||
|
format(const std::locale& loc, std::string format,
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, Duration> tp,
|
||||||
|
const Zone* zone = nullptr)
|
||||||
|
{
|
||||||
|
return detail::format(loc, std::move(format), tp, zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string
|
||||||
|
format(const std::locale& loc, std::string format, day_point tp,
|
||||||
|
const Zone* zone = nullptr)
|
||||||
|
{
|
||||||
|
return detail::format(loc, std::move(format), tp, zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Duration>
|
||||||
|
inline
|
||||||
|
std::string
|
||||||
|
format(std::string format,
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, Duration> tp,
|
||||||
|
const Zone* zone = nullptr)
|
||||||
|
{
|
||||||
|
return detail::format(std::locale{}, std::move(format), tp, zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string
|
||||||
|
format(std::string format, day_point tp, const Zone* zone = nullptr)
|
||||||
|
{
|
||||||
|
return detail::format(std::locale{}, std::move(format), tp, zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse
|
||||||
|
|
||||||
|
template <class Duration>
|
||||||
|
void
|
||||||
|
parse(std::istream& is, const std::string& format,
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, Duration>& tp)
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
istream::sentry ok{is};
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
auto& f = use_facet<time_get<char>>(is.getloc());
|
||||||
|
ios_base::iostate err = ios_base::goodbit;
|
||||||
|
std::tm tm{};
|
||||||
|
minutes offset{};
|
||||||
|
Duration subseconds{};
|
||||||
|
|
||||||
|
auto b = format.data();
|
||||||
|
auto i = b;
|
||||||
|
auto e = b + format.size();
|
||||||
|
for (; i < e; ++i)
|
||||||
|
{
|
||||||
|
if (*i == '%' && i < e-1)
|
||||||
|
{
|
||||||
|
switch (i[1])
|
||||||
|
{
|
||||||
|
case 'T':
|
||||||
|
case 'S':
|
||||||
|
f.get(is, 0, is, err, &tm, b, i);
|
||||||
|
++i;
|
||||||
|
b = i+1;
|
||||||
|
if (*i == 'T')
|
||||||
|
{
|
||||||
|
const char hm[] = "%H:%M:";
|
||||||
|
f.get(is, 0, is, err, &tm, hm, hm+6);
|
||||||
|
}
|
||||||
|
if (ratio_less<typename Duration::period, ratio<1>>::value)
|
||||||
|
{
|
||||||
|
double s;
|
||||||
|
is >> s;
|
||||||
|
if (!is.fail())
|
||||||
|
subseconds = duration_cast<Duration>(duration<double>{s});
|
||||||
|
else
|
||||||
|
err &= ios_base::failbit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char hm[] = "%S";
|
||||||
|
f.get(is, 0, is, err, &tm, hm, hm+2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
f.get(is, 0, is, err, &tm, b, i);
|
||||||
|
++i;
|
||||||
|
b = i+1;
|
||||||
|
if ((err & ios_base::failbit) == 0)
|
||||||
|
{
|
||||||
|
char sign{};
|
||||||
|
is >> sign;
|
||||||
|
if (!is.fail() && (sign == '+' || sign == '-'))
|
||||||
|
{
|
||||||
|
char h1, h0, m1, m0;
|
||||||
|
h1 = static_cast<char>(is.get());
|
||||||
|
h0 = static_cast<char>(is.get());
|
||||||
|
m1 = static_cast<char>(is.get());
|
||||||
|
m0 = static_cast<char>(is.get());
|
||||||
|
if (!is.fail() && std::isdigit(h1) && std::isdigit(h0)
|
||||||
|
&& std::isdigit(m1) && std::isdigit(m0))
|
||||||
|
{
|
||||||
|
offset = 10*hours{h1 - '0'} + hours{h0 - '0'} +
|
||||||
|
10*minutes{m1 - '0'} + minutes{m0 - '0'};
|
||||||
|
if (sign == '-')
|
||||||
|
offset = -offset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
err &= ios_base::failbit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
err &= ios_base::failbit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((err & ios_base::failbit) == 0)
|
||||||
|
{
|
||||||
|
if (b < e)
|
||||||
|
f.get(is, 0, is, err, &tm, b, e);
|
||||||
|
if ((err & ios_base::failbit) == 0)
|
||||||
|
{
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
auto tt = timegm(&tm);
|
||||||
|
#else
|
||||||
|
auto tt = _mkgmtime(&tm);
|
||||||
|
#endif
|
||||||
|
tp = floor<Duration>(system_clock::from_time_t(tt) +
|
||||||
|
subseconds - offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is.setstate(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace date
|
} // namespace date
|
||||||
|
|
||||||
#endif // TZ_H
|
#endif // TZ_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user