#pragma once
namespace panda { namespace time {
void localtime (ptime_t, dt*);
void anytime (ptime_t, dt*, const tz*);
#ifdef __GNUC__
inline void ianytime (ptime_t epoch, dt* result, const tz* zone) __attribute__((always_inline));
inline void ilocaltime (ptime_t epoch, dt* result) __attribute__((always_inline));
#endif
inline ptime_t _calc_rule_epoch (int, const dt*, dt);
#define __PTIME_TRANS_BINFIND(VAR, FIELD) \
int index = -1; \
int low = 0; \
int high = zone->trans_cnt; \
while (high - low > 1) { \
int mid = (high+low)/2; \
if (zone->trans[mid].FIELD > VAR) high = mid; \
else if (zone->trans[mid].FIELD < VAR) low = mid; \
else { index = mid; break; } \
} \
if (index < 0) index = high - 1;
#define _PTIME_LT_LEAPSEC_CORR(source) \
if (epoch < source.leap_end) result->sec = 60 + epoch - source.start;
inline void ianytime (ptime_t epoch, dt* result, const tz* zone) {
if (epoch < zone->ltrans.start) {
__PTIME_TRANS_BINFIND(epoch, start);
igmtime(epoch + zone->trans[index].offset, result);
result->gmtoff = zone->trans[index].gmt_offset;
result->n_zone = zone->trans[index].n_abbrev;
result->isdst = zone->trans[index].isdst;
_PTIME_LT_LEAPSEC_CORR(zone->trans[index]);
}
else if (!zone->future.hasdst) { // future with no DST
igmtime(epoch + zone->future.outer.offset, result);
result->n_zone = zone->future.outer.n_abbrev;
result->gmtoff = zone->future.outer.gmt_offset;
result->isdst = zone->future.outer.isdst; // some zones stay in dst in future (when no POSIX string and last trans is in dst)
_PTIME_LT_LEAPSEC_CORR(zone->ltrans);
}
else {
igmtime(epoch + zone->future.outer.offset, result);
int is_leap = is_leap_year(result->year);
if ((epoch >= _calc_rule_epoch(is_leap, result, zone->future.outer.end) - zone->future.outer.offset) &&
(epoch < _calc_rule_epoch(is_leap, result, zone->future.inner.end) - zone->future.inner.offset)) {
igmtime(epoch + zone->future.inner.offset, result);
result->isdst = zone->future.inner.isdst;
result->n_zone = zone->future.inner.n_abbrev;
result->gmtoff = zone->future.inner.gmt_offset;
} else {
result->isdst = zone->future.outer.isdst;
result->n_zone = zone->future.outer.n_abbrev;
result->gmtoff = zone->future.outer.gmt_offset;
}
_PTIME_LT_LEAPSEC_CORR(zone->ltrans);
}
}
inline void ilocaltime (ptime_t epoch, dt* result) {
ianytime(epoch, result, tzlocal());
}
inline ptime_t _calc_rule_epoch (int is_leap, const dt* curdate, dt border) {
border.mday = (border.wday + curdate->yday - MON2YDAY[is_leap][border.mon] - curdate->wday + 378) % 7 + 7*border.yday - 6;
if (border.mday > DAYS_IN_MONTH[is_leap][border.mon]) border.mday -= 7;
border.year = curdate->year;
return itimegmll(&border);
}
};};