#include "time.h"
#include <type_traits>
#include <panda/from_chars.h>
namespace panda { namespace time { namespace format {
template <std::size_t N>
struct tag : std::integral_constant<std::size_t, N>{
static const auto length = N;
inline static char* applyN (char* in, int value) {
char* result = in + N;
char* tail = result - 1;
for (std::size_t i = 0; i < N; ++i) {
char digit = value % 10;
value /= 10;
*tail = digit + '0';
--tail;
}
return result;
}
inline static char* applyN_spad (char* in, int value) {
char* result = in + N;
char* tail = result - 1;
for (std::size_t i = 0; i < N - 1; ++i) {
char digit = value % 10;
value /= 10;
*tail = digit + '0';
--tail;
}
char digit = value % 10;
value /= 10;
if (digit) *tail = digit + '0';
else *tail = ' ';
--tail;
return result;
}
};
template <char C>
struct tag_char : tag<1> {
inline static char* apply (char* in, const datetime&, uint32_t) {
*in = C;
return ++in;
}
};
struct tag_century : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.year/100); }};
struct tag_year : tag<4> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.year); }};
struct tag_yr : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.year % 100); }};
struct tag_month : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.mon + 1); }};
struct tag_day : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.mday); }};
struct tag_day_spad : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN_spad(in, dt.mday); }};
struct tag_hour : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.hour); }};
struct tag_hour_spad : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN_spad(in, dt.hour); }};
struct tag_min : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.min); }};
struct tag_sec : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.sec); }};
struct tag_yday : tag<3> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.yday + 1); }};
struct tag_c_wday : tag<1> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.wday); }};
struct tag_ewday : tag<1> { inline static char* apply (char* in, const datetime& dt, uint32_t) { return applyN(in, dt.wday ? dt.wday : 7); }};
struct tag_hour12 : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto h = dt.hour % 12;
if (h == 0) h = 12;
return applyN(in, h);
}};
struct tag_hour12_spad : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto h = dt.hour % 12;
if (h == 0) h = 12;
return applyN_spad(in, h);
}};
struct tag_AMPM : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
*in++ = dt.hour < 12 ? 'A' : 'P';
*in++ = 'M';
return in;
}};
struct tag_ampm : tag<2> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
*in++ = dt.hour < 12 ? 'a' : 'p';
*in++ = 'm';
return in;
}};
struct tag_mksec : tag<7> { inline static char* apply (char* in, const datetime&, uint32_t value) {
if (!value) return in;
*in++ = '.';
char* result = nullptr;
char* tail = in + 6;
for (std::size_t i = 0; i < 6; ++i) {
--tail;
char digit = value % 10;
value /= 10;
if (!result) {
if (!digit) continue;
result = tail + 1;
}
*tail = digit + '0';
}
/* happens when mksec is garbage. gargabe in => garbage out, don't care, just do not crash */
if (!result) { return in; }
return result;
}};
struct tag_tzoff : tag<6> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto off = dt.gmtoff;
if (off >= 0) *in++ = '+';
else {
*in++ = '-';
off = -off;
}
auto hour = off / 3600;
auto min = (off % 3600) / 60;
if (!min) return tag<2>::applyN(in, hour);
in = tag<2>::applyN(in, hour);
*in++ = ':';
return tag<2>::applyN(in, min);
}};
struct tag_tzoff_void : tag<5> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto off = dt.gmtoff;
if (off >= 0) *in++ = '+';
else {
*in++ = '-';
off = -off;
}
return tag<2>::applyN(tag<2>::applyN(in, off / 3600), (off % 3600) / 60);
}};
struct tag_tzabbr : tag<7> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto src = dt.zone;
while (char c = *src++) *in++ = c;
return in;
}};
struct tag_tz1123 : tag<5> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
if (!dt.gmtoff) {
*in++ = 'G';
*in++ = 'M';
*in++ = 'T';
return in;
}
auto off = dt.gmtoff;
if (off >= 0) *in++ = '+';
else {
*in++ = '-';
off = -off;
}
return tag<2>::applyN(tag<2>::applyN(in, off / 3600), (off % 3600) / 60);
}};
struct tag_month_short : tag<3> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto name = MONTH_NAMES[dt.mon];
*in++ = name[0];
*in++ = name[1];
*in++ = name[2];
return in;
}};
struct tag_month_long : tag<9> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto name = MONTH_NAMES[dt.mon];
auto size = name.length();
memcpy(in, name.data(), size);
in += size;
return in;
}};
struct tag_wday_short : tag<3> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto name = WDAY_NAMES[dt.wday];
*in++ = name[0];
*in++ = name[1];
*in++ = name[2];
return in;
}};
struct tag_wday_long : tag<9> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto name = WDAY_NAMES[dt.wday];
auto size = name.length();
memcpy(in, name.data(), size);
in += size;
return in;
}};
// max epoch has 17 digits, but in past it has additional '-'
struct tag_epoch : tag<18> { inline static char* apply (char* in, const datetime& dt, uint32_t) {
auto gmtoff = dt.gmtoff;
// we can use the lightest version timegmll() because it is guaranteed that <dt> is already normalized
auto epoch = timegmll(&dt) - gmtoff;
auto res = to_chars(in, in + length, epoch);
assert(!res.ec);
return res.ptr;
}};
template <typename...Ts> struct size_of_t;
template <typename T> struct size_of_t<T> { static const auto length = T::length; };
template <typename T, typename...Ts> struct size_of_t<T, Ts...> {
static const auto length = (size_of_t<T>::length + size_of_t<Ts...>::length);
};
template <typename... Tags> struct Composer;
template <typename T1> struct Composer<T1> {
static char* fn (char* in, const datetime& dt, uint32_t mksec) {
return T1::apply(in, dt, mksec);
}
};
template <typename T, typename...Tags> struct Composer<T, Tags...> {
static char* fn (char* in, const datetime& dt, uint32_t mksec) {
return Composer<Tags...>::fn(Composer<T>::fn(in, dt, mksec), dt, mksec);
}
};
template <typename ...Args>
struct exp_t {
static constexpr const auto length = size_of_t<Args...>::length;
inline static char* apply (char* in, const datetime& dt, uint32_t mksec) {
return Composer<Args...>::fn(in,dt, mksec);
}
};
using ansi_c_t = exp_t<tag_wday_short, tag_char<' '>, tag_month_short, tag_char<' '>, tag_day_spad, tag_char<' '>, tag_hour, tag_char<':'>, tag_min, tag_char<':'>, tag_sec, tag_char<' '>, tag_year>;
using ymd_t = exp_t<tag_year, tag_char<'-'>, tag_month, tag_char<'-'>, tag_day>;
using mdy_s_t = exp_t<tag_month, tag_char<'/'>, tag_day, tag_char<'/'>, tag_year>;
using hms_t = exp_t<tag_hour, tag_char<':'>, tag_min, tag_char<':'>, tag_sec>;
using hms12_t = exp_t<tag_hour12, tag_char<':'>, tag_min, tag_char<':'>, tag_sec, tag_char<' '>, tag_AMPM>;
using hm_t = exp_t<tag_hour, tag_char<':'>, tag_min>;
}}}