forked from HowardHinnant/date
Move formatting and parsing
Moved formatting and parsing of sys_time and local_time from tz.h to date.h in order to make this functionality available to a wider audience. Existing code does not need to change. But future code can #include "date.h" instead of "tz.h" and need not compile tz.cpp nor link to curl. Formatting zoned_time remains in tz.h.
This commit is contained in:
598
date.h
598
date.h
@ -3919,6 +3919,604 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const local_time<Duration>& ut
|
||||
return os << sys_time<Duration>{ut.time_since_epoch()};
|
||||
}
|
||||
|
||||
// format
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
std::basic_string<CharT, Traits>
|
||||
format(const std::locale& loc, std::basic_string<CharT, Traits> fmt,
|
||||
local_time<Duration> tp, const std::string* abbrev = nullptr,
|
||||
const std::chrono::seconds* offset_sec = nullptr)
|
||||
{
|
||||
// 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 on +/-hhmm format
|
||||
// %Ez, %Oz replace with offset from zone on +/-hh:mm format
|
||||
// %Z replace with abbreviation from zone
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
auto command = false;
|
||||
auto modified = false;
|
||||
for (std::size_t i = 0; i < fmt.size(); ++i)
|
||||
{
|
||||
switch (fmt[i])
|
||||
{
|
||||
case '%':
|
||||
command = true;
|
||||
modified = false;
|
||||
break;
|
||||
case 'O':
|
||||
case 'E':
|
||||
modified = true;
|
||||
break;
|
||||
case 'S':
|
||||
case 'T':
|
||||
if (command && !modified && ratio_less<typename Duration::period, ratio<1>>::value)
|
||||
{
|
||||
basic_ostringstream<CharT, Traits> os;
|
||||
os.imbue(loc);
|
||||
os << make_time(tp - floor<seconds>(tp));
|
||||
auto s = os.str();
|
||||
s.erase(0, 8);
|
||||
fmt.insert(i+1, s);
|
||||
i += s.size() - 1;
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
case 'z':
|
||||
if (command)
|
||||
{
|
||||
if (offset_sec == nullptr)
|
||||
throw std::runtime_error("Can not format local_time with %z");
|
||||
else
|
||||
{
|
||||
auto offset = duration_cast<minutes>(*offset_sec);
|
||||
basic_ostringstream<CharT, Traits> os;
|
||||
if (offset >= minutes{0})
|
||||
os << '+';
|
||||
os << make_time(offset);
|
||||
auto s = os.str();
|
||||
if (!modified)
|
||||
s.erase(s.find(':'), 1);
|
||||
fmt.replace(i - 1 - modified, 2 + modified, s);
|
||||
i += s.size() - 1;
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
case 'Z':
|
||||
if (command && !modified)
|
||||
{
|
||||
if (abbrev == nullptr)
|
||||
throw std::runtime_error("Can not format local_time with %Z");
|
||||
else
|
||||
{
|
||||
fmt.replace(i - 1, 2,
|
||||
std::basic_string<CharT, Traits>(abbrev->begin(), abbrev->end()));
|
||||
i += abbrev->size() - 1;
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
default:
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto& f = use_facet<time_put<CharT>>(loc);
|
||||
basic_ostringstream<CharT, Traits> os;
|
||||
auto tt = system_clock::to_time_t(sys_time<Duration>{tp.time_since_epoch()});
|
||||
std::tm tm{};
|
||||
#ifndef _MSC_VER
|
||||
gmtime_r(&tt, &tm);
|
||||
#else
|
||||
gmtime_s(&tm, &tt);
|
||||
#endif
|
||||
f.put(os, os, os.fill(), &tm, fmt.data(), fmt.data() + fmt.size());
|
||||
return os.str();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
format(const std::locale& loc, std::basic_string<CharT, Traits> fmt,
|
||||
local_time<Duration> tp)
|
||||
{
|
||||
return detail::format(loc, std::move(fmt), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
format(std::basic_string<CharT, Traits> fmt, local_time<Duration> tp)
|
||||
{
|
||||
return detail::format(std::locale{}, std::move(fmt), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
format(const std::locale& loc, std::basic_string<CharT, Traits> fmt,
|
||||
sys_time<Duration> tp)
|
||||
{
|
||||
const std::string abbrev("UTC");
|
||||
CONSTDATA std::chrono::seconds offset{0};
|
||||
return detail::format(loc, std::move(fmt),
|
||||
local_time<Duration>{tp.time_since_epoch()}, &abbrev, &offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
format(std::basic_string<CharT, Traits> fmt, sys_time<Duration> tp)
|
||||
{
|
||||
const std::string abbrev("UTC");
|
||||
CONSTDATA std::chrono::seconds offset{0};
|
||||
return detail::format(std::move(fmt), local_time<Duration>{tp.time_since_epoch()},
|
||||
&abbrev, &offset);
|
||||
}
|
||||
|
||||
// const CharT* formats
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
format(const std::locale& loc, const CharT* fmt, local_time<Duration> tp)
|
||||
{
|
||||
return detail::format(loc, std::basic_string<CharT>(fmt), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
format(const CharT* fmt, local_time<Duration> tp)
|
||||
{
|
||||
return detail::format(std::locale{}, std::basic_string<CharT>(fmt), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
format(const std::locale& loc, const CharT* fmt, sys_time<Duration> tp)
|
||||
{
|
||||
const std::string abbrev("UTC");
|
||||
CONSTDATA std::chrono::seconds offset{0};
|
||||
return detail::format(loc, std::basic_string<CharT>(fmt),
|
||||
local_time<Duration>{tp.time_since_epoch()},
|
||||
&abbrev, &offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
format(const CharT* fmt, sys_time<Duration> tp)
|
||||
{
|
||||
const std::string abbrev("UTC");
|
||||
CONSTDATA std::chrono::seconds offset{0};
|
||||
return detail::format(std::locale{}, std::basic_string<CharT>(fmt),
|
||||
local_time<Duration>{tp.time_since_epoch()},
|
||||
&abbrev, &offset);
|
||||
}
|
||||
|
||||
// parse
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
typename basic_istream<CharT, Traits>::sentry ok{is};
|
||||
if (ok)
|
||||
{
|
||||
auto& f = use_facet<time_get<CharT>>(is.getloc());
|
||||
ios_base::iostate err = ios_base::goodbit;
|
||||
std::tm tm{};
|
||||
Duration subseconds{};
|
||||
std::basic_string<CharT, Traits> temp_abbrev;
|
||||
minutes temp_offset{};
|
||||
|
||||
auto b = format.data();
|
||||
auto i = b;
|
||||
auto e = b + format.size();
|
||||
auto command = false;
|
||||
auto modified = false;
|
||||
for (; i < e; ++i)
|
||||
{
|
||||
switch (*i)
|
||||
{
|
||||
case '%':
|
||||
command = true;
|
||||
modified = false;
|
||||
break;
|
||||
case 'O':
|
||||
case 'E':
|
||||
modified = true;
|
||||
break;
|
||||
case 'T':
|
||||
case 'S':
|
||||
if (command && !modified)
|
||||
{
|
||||
f.get(is, 0, is, err, &tm, b, i-1);
|
||||
b = i+1;
|
||||
if (*i == 'T')
|
||||
{
|
||||
const CharT hm[] = {'%', 'H', ':', '%', 'M', ':'};
|
||||
f.get(is, 0, is, err, &tm, hm, hm+6);
|
||||
}
|
||||
if (ratio_less<typename Duration::period, ratio<1>>::value)
|
||||
{
|
||||
auto decimal_point = Traits::to_int_type(
|
||||
use_facet<numpunct<CharT>>(is.getloc()).decimal_point());
|
||||
string buf;
|
||||
while (true)
|
||||
{
|
||||
auto k = is.peek();
|
||||
if (Traits::eq_int_type(k, Traits::eof()))
|
||||
break;
|
||||
if (Traits::eq_int_type(k, decimal_point))
|
||||
{
|
||||
buf += '.';
|
||||
decimal_point = Traits::eof();
|
||||
is.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto c = static_cast<char>(Traits::to_char_type(k));
|
||||
if (isdigit(c))
|
||||
{
|
||||
buf += c;
|
||||
is.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!buf.empty())
|
||||
subseconds = round<Duration>(duration<double>{stod(buf)});
|
||||
else
|
||||
err |= ios_base::failbit;
|
||||
}
|
||||
else
|
||||
{
|
||||
const CharT hm[] = {'%', 'S'};
|
||||
f.get(is, 0, is, err, &tm, hm, hm+2);
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
case 'z':
|
||||
if (command)
|
||||
{
|
||||
f.get(is, 0, is, err, &tm, b, i-1-modified);
|
||||
b = i+1;
|
||||
if ((err & ios_base::failbit) == 0)
|
||||
{
|
||||
CharT sign{};
|
||||
is >> sign;
|
||||
if (!is.fail() && (sign == '+' || sign == '-'))
|
||||
{
|
||||
char h1, h0, m1, m0;
|
||||
char colon = ':';
|
||||
h1 = static_cast<char>(is.get());
|
||||
h0 = static_cast<char>(is.get());
|
||||
if (modified)
|
||||
{
|
||||
if (h0 == ':')
|
||||
{
|
||||
colon = h0;
|
||||
h0 = h1;
|
||||
h1 = '0';
|
||||
}
|
||||
else
|
||||
colon = 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)
|
||||
&& colon == ':')
|
||||
{
|
||||
temp_offset = 10*hours{h1 - '0'} + hours{h0 - '0'} +
|
||||
10*minutes{m1 - '0'} + minutes{m0 - '0'};
|
||||
if (sign == '-')
|
||||
temp_offset = -temp_offset;
|
||||
}
|
||||
else
|
||||
err |= ios_base::failbit;
|
||||
}
|
||||
else
|
||||
err |= ios_base::failbit;
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
case 'Z':
|
||||
if (command && !modified)
|
||||
{
|
||||
f.get(is, 0, is, err, &tm, b, i-1);
|
||||
b = i+1;
|
||||
if ((err & ios_base::failbit) == 0)
|
||||
{
|
||||
is >> temp_abbrev;
|
||||
if (is.fail())
|
||||
err |= ios_base::failbit;
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
default:
|
||||
command = false;
|
||||
modified = false;
|
||||
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)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
auto tt = _mkgmtime(&tm);
|
||||
#else
|
||||
auto tt = timegm(&tm);
|
||||
#endif
|
||||
tp = floor<Duration>(system_clock::from_time_t(tt) + subseconds);
|
||||
abbrev = std::move(temp_abbrev);
|
||||
offset = temp_offset;
|
||||
}
|
||||
}
|
||||
is.setstate(err);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp)
|
||||
{
|
||||
std::basic_string<CharT, Traits> abbrev;
|
||||
std::chrono::minutes offset{};
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
std::chrono::minutes offset{};
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::chrono::minutes& offset)
|
||||
{
|
||||
std::basic_string<CharT, Traits> abbrev;
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset)
|
||||
{
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
std::basic_string<CharT, Traits> abbrev;
|
||||
std::chrono::minutes offset{};
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
std::chrono::minutes offset{};
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp,
|
||||
std::chrono::minutes& offset)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
std::basic_string<CharT, Traits> abbrev;
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp,
|
||||
std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
// const CharT* formats
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp,
|
||||
std::chrono::minutes& offset)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp,
|
||||
std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp, std::chrono::minutes& offset)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev,
|
||||
std::chrono::minutes& offset)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp, std::chrono::minutes& offset,
|
||||
std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev, offset);
|
||||
}
|
||||
|
||||
} // namespace date
|
||||
|
||||
#endif // DATE_H
|
||||
|
589
tz.h
589
tz.h
@ -1336,127 +1336,6 @@ to_gps_time(tai_time<Duration> t)
|
||||
|
||||
// format
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
std::basic_string<CharT, Traits>
|
||||
format(const std::locale& loc, std::basic_string<CharT, Traits> fmt,
|
||||
local_time<Duration> tp, const sys_info* info = nullptr)
|
||||
{
|
||||
// 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 on +/-hhmm format
|
||||
// %Ez, %Oz replace with offset from zone on +/-hh:mm format
|
||||
// %Z replace with abbreviation from zone
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
auto command = false;
|
||||
auto modified = false;
|
||||
for (std::size_t i = 0; i < fmt.size(); ++i)
|
||||
{
|
||||
switch (fmt[i])
|
||||
{
|
||||
case '%':
|
||||
command = true;
|
||||
modified = false;
|
||||
break;
|
||||
case 'O':
|
||||
case 'E':
|
||||
modified = true;
|
||||
break;
|
||||
case 'S':
|
||||
case 'T':
|
||||
if (command && !modified && ratio_less<typename Duration::period, ratio<1>>::value)
|
||||
{
|
||||
basic_ostringstream<CharT, Traits> os;
|
||||
os.imbue(loc);
|
||||
os << make_time(tp - floor<seconds>(tp));
|
||||
auto s = os.str();
|
||||
s.erase(0, 8);
|
||||
fmt.insert(i+1, s);
|
||||
i += s.size() - 1;
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
case 'z':
|
||||
if (command)
|
||||
{
|
||||
if (info == nullptr)
|
||||
throw std::runtime_error("Can not format local_time with %z");
|
||||
else
|
||||
{
|
||||
auto offset = duration_cast<minutes>(info->offset);
|
||||
basic_ostringstream<CharT, Traits> os;
|
||||
if (offset >= minutes{0})
|
||||
os << '+';
|
||||
os << make_time(offset);
|
||||
auto s = os.str();
|
||||
if (!modified)
|
||||
s.erase(s.find(':'), 1);
|
||||
fmt.replace(i - 1 - modified, 2 + modified, s);
|
||||
i += s.size() - 1;
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
case 'Z':
|
||||
if (command && !modified)
|
||||
{
|
||||
if (info == nullptr)
|
||||
throw std::runtime_error("Can not format local_time with %Z");
|
||||
else
|
||||
{
|
||||
fmt.replace(i - 1, 2, std::basic_string<CharT, Traits>
|
||||
(info->abbrev.begin(), info->abbrev.end()));
|
||||
i += info->abbrev.size() - 1;
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
default:
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto& f = use_facet<time_put<CharT>>(loc);
|
||||
basic_ostringstream<CharT, Traits> os;
|
||||
auto tt = system_clock::to_time_t(sys_time<Duration>{tp.time_since_epoch()});
|
||||
std::tm tm{};
|
||||
#ifndef _MSC_VER
|
||||
gmtime_r(&tt, &tm);
|
||||
#else
|
||||
gmtime_s(&tm, &tt);
|
||||
#endif
|
||||
f.put(os, os, os.fill(), &tm, fmt.data(), fmt.data() + fmt.size());
|
||||
return os.str();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
format(const std::locale& loc, std::basic_string<CharT, Traits> fmt,
|
||||
local_time<Duration> tp)
|
||||
{
|
||||
return detail::format(loc, std::move(fmt), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
format(std::basic_string<CharT, Traits> fmt, local_time<Duration> tp)
|
||||
{
|
||||
return detail::format(std::locale{}, std::move(fmt), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
@ -1464,7 +1343,8 @@ format(const std::locale& loc, std::basic_string<CharT, Traits> fmt,
|
||||
const zoned_time<Duration>& tp)
|
||||
{
|
||||
auto const info = tp.get_info();
|
||||
return detail::format(loc, std::move(fmt), tp.get_local_time(), &info);
|
||||
return detail::format(loc, std::move(fmt), tp.get_local_time(),
|
||||
&info.abbrev, &info.offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
@ -1473,44 +1353,12 @@ std::basic_string<CharT, Traits>
|
||||
format(std::basic_string<CharT, Traits> fmt, const zoned_time<Duration>& tp)
|
||||
{
|
||||
auto const info = tp.get_info();
|
||||
return detail::format(std::locale{}, std::move(fmt), tp.get_local_time(), &info);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
format(const std::locale& loc, std::basic_string<CharT, Traits> fmt,
|
||||
sys_time<Duration> tp)
|
||||
{
|
||||
return format(loc, std::move(fmt), make_zoned(locate_zone("UTC"), tp));
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT, Traits>
|
||||
format(std::basic_string<CharT, Traits> fmt, sys_time<Duration> tp)
|
||||
{
|
||||
return format(std::move(fmt), make_zoned(locate_zone("UTC"), tp));
|
||||
return detail::format(std::locale{}, std::move(fmt), tp.get_local_time(),
|
||||
&info.abbrev, &info.offset);
|
||||
}
|
||||
|
||||
// const CharT* formats
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
format(const std::locale& loc, const CharT* fmt, local_time<Duration> tp)
|
||||
{
|
||||
return detail::format(loc, std::basic_string<CharT>(fmt), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
format(const CharT* fmt, local_time<Duration> tp)
|
||||
{
|
||||
return detail::format(std::locale{}, std::basic_string<CharT>(fmt), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
@ -1518,7 +1366,7 @@ format(const std::locale& loc, const CharT* fmt, const zoned_time<Duration>& tp)
|
||||
{
|
||||
auto const info = tp.get_info();
|
||||
return detail::format(loc, std::basic_string<CharT>(fmt), tp.get_local_time(),
|
||||
&info);
|
||||
&info.abbrev, &info.offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
@ -1528,432 +1376,7 @@ format(const CharT* fmt, const zoned_time<Duration>& tp)
|
||||
{
|
||||
auto const info = tp.get_info();
|
||||
return detail::format(std::locale{}, std::basic_string<CharT>(fmt),
|
||||
tp.get_local_time(), &info);
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
format(const std::locale& loc, const CharT* fmt, sys_time<Duration> tp)
|
||||
{
|
||||
return format(loc, fmt, make_zoned(locate_zone("UTC"), tp));
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
format(const CharT* fmt, sys_time<Duration> tp)
|
||||
{
|
||||
return format(fmt, make_zoned(locate_zone("UTC"), tp));
|
||||
}
|
||||
|
||||
// parse
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
typename basic_istream<CharT, Traits>::sentry ok{is};
|
||||
if (ok)
|
||||
{
|
||||
auto& f = use_facet<time_get<CharT>>(is.getloc());
|
||||
ios_base::iostate err = ios_base::goodbit;
|
||||
std::tm tm{};
|
||||
Duration subseconds{};
|
||||
std::basic_string<CharT, Traits> temp_abbrev;
|
||||
minutes temp_offset{};
|
||||
|
||||
auto b = format.data();
|
||||
auto i = b;
|
||||
auto e = b + format.size();
|
||||
auto command = false;
|
||||
auto modified = false;
|
||||
for (; i < e; ++i)
|
||||
{
|
||||
switch (*i)
|
||||
{
|
||||
case '%':
|
||||
command = true;
|
||||
modified = false;
|
||||
break;
|
||||
case 'O':
|
||||
case 'E':
|
||||
modified = true;
|
||||
break;
|
||||
case 'T':
|
||||
case 'S':
|
||||
if (command && !modified)
|
||||
{
|
||||
f.get(is, 0, is, err, &tm, b, i-1);
|
||||
b = i+1;
|
||||
if (*i == 'T')
|
||||
{
|
||||
const CharT hm[] = {'%', 'H', ':', '%', 'M', ':'};
|
||||
f.get(is, 0, is, err, &tm, hm, hm+6);
|
||||
}
|
||||
if (ratio_less<typename Duration::period, ratio<1>>::value)
|
||||
{
|
||||
auto decimal_point = Traits::to_int_type(
|
||||
use_facet<numpunct<CharT>>(is.getloc()).decimal_point());
|
||||
string buf;
|
||||
while (true)
|
||||
{
|
||||
auto k = is.peek();
|
||||
if (Traits::eq_int_type(k, Traits::eof()))
|
||||
break;
|
||||
if (Traits::eq_int_type(k, decimal_point))
|
||||
{
|
||||
buf += '.';
|
||||
decimal_point = Traits::eof();
|
||||
is.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto c = static_cast<char>(Traits::to_char_type(k));
|
||||
if (isdigit(c))
|
||||
{
|
||||
buf += c;
|
||||
is.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!buf.empty())
|
||||
subseconds = round<Duration>(duration<double>{stod(buf)});
|
||||
else
|
||||
err |= ios_base::failbit;
|
||||
}
|
||||
else
|
||||
{
|
||||
const CharT hm[] = {'%', 'S'};
|
||||
f.get(is, 0, is, err, &tm, hm, hm+2);
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
case 'z':
|
||||
if (command)
|
||||
{
|
||||
f.get(is, 0, is, err, &tm, b, i-1-modified);
|
||||
b = i+1;
|
||||
if ((err & ios_base::failbit) == 0)
|
||||
{
|
||||
CharT sign{};
|
||||
is >> sign;
|
||||
if (!is.fail() && (sign == '+' || sign == '-'))
|
||||
{
|
||||
char h1, h0, m1, m0;
|
||||
char colon = ':';
|
||||
h1 = static_cast<char>(is.get());
|
||||
h0 = static_cast<char>(is.get());
|
||||
if (modified)
|
||||
{
|
||||
if (h0 == ':')
|
||||
{
|
||||
colon = h0;
|
||||
h0 = h1;
|
||||
h1 = '0';
|
||||
}
|
||||
else
|
||||
colon = 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)
|
||||
&& colon == ':')
|
||||
{
|
||||
temp_offset = 10*hours{h1 - '0'} + hours{h0 - '0'} +
|
||||
10*minutes{m1 - '0'} + minutes{m0 - '0'};
|
||||
if (sign == '-')
|
||||
temp_offset = -temp_offset;
|
||||
}
|
||||
else
|
||||
err |= ios_base::failbit;
|
||||
}
|
||||
else
|
||||
err |= ios_base::failbit;
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
case 'Z':
|
||||
if (command && !modified)
|
||||
{
|
||||
f.get(is, 0, is, err, &tm, b, i-1);
|
||||
b = i+1;
|
||||
if ((err & ios_base::failbit) == 0)
|
||||
{
|
||||
is >> temp_abbrev;
|
||||
if (is.fail())
|
||||
err |= ios_base::failbit;
|
||||
}
|
||||
}
|
||||
command = false;
|
||||
modified = false;
|
||||
break;
|
||||
default:
|
||||
command = false;
|
||||
modified = false;
|
||||
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)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
auto tt = _mkgmtime(&tm);
|
||||
#else
|
||||
auto tt = timegm(&tm);
|
||||
#endif
|
||||
tp = floor<Duration>(system_clock::from_time_t(tt) + subseconds);
|
||||
abbrev = std::move(temp_abbrev);
|
||||
offset = temp_offset;
|
||||
}
|
||||
}
|
||||
is.setstate(err);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp)
|
||||
{
|
||||
std::basic_string<CharT, Traits> abbrev;
|
||||
std::chrono::minutes offset{};
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
std::chrono::minutes offset{};
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::chrono::minutes& offset)
|
||||
{
|
||||
std::basic_string<CharT, Traits> abbrev;
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset)
|
||||
{
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp,
|
||||
std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
detail::parse(is, format, tp, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = floor<Duration>(tp - offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
std::basic_string<CharT, Traits> abbrev;
|
||||
std::chrono::minutes offset{};
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
std::chrono::minutes offset{};
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp,
|
||||
std::chrono::minutes& offset)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
std::basic_string<CharT, Traits> abbrev;
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is,
|
||||
const std::basic_string<CharT, Traits>& format, local_time<Duration>& tp,
|
||||
std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
sys_time<Duration> st;
|
||||
detail::parse(is, format, st, abbrev, offset);
|
||||
if (!is.fail())
|
||||
tp = local_time<Duration>{st.time_since_epoch()};
|
||||
}
|
||||
|
||||
// const CharT* formats
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp,
|
||||
std::chrono::minutes& offset)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp,
|
||||
std::basic_string<CharT, Traits>& abbrev, std::chrono::minutes& offset)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format, sys_time<Duration>& tp,
|
||||
std::chrono::minutes& offset, std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp, std::chrono::minutes& offset)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp, std::basic_string<CharT, Traits>& abbrev,
|
||||
std::chrono::minutes& offset)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev, offset);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Duration>
|
||||
inline
|
||||
void
|
||||
parse(std::basic_istream<CharT, Traits>& is, const CharT* format,
|
||||
local_time<Duration>& tp, std::chrono::minutes& offset,
|
||||
std::basic_string<CharT, Traits>& abbrev)
|
||||
{
|
||||
parse(is, std::basic_string<CharT, Traits>(format), tp, abbrev, offset);
|
||||
tp.get_local_time(), &info.abbrev, &info.offset);
|
||||
}
|
||||
|
||||
} // namespace date
|
||||
|
Reference in New Issue
Block a user