3#ifdef USE_TIME_TIMEZONE
12static ParsedTimezone global_tz_{};
22static uint32_t parse_uint(
const char *&p) {
24 while (std::isdigit(
static_cast<unsigned char>(*p))) {
25 value = value * 10 + (*p -
'0');
40 while (
days >= (diy = days_in_year(
year)) &&
year < 2200) {
52static int epoch_to_year(time_t epoch) {
53 int64_t
days = epoch / 86400;
54 if (epoch < 0 && epoch % 86400 != 0)
56 return days_to_year(
days);
82 int h = (
day + (13 * (
month + 1)) / 5 + k + k / 4 +
j / 4 - 2 *
j) % 7;
89 int64_t
days = epoch / 86400;
128 while (*p && *p !=
'>') {
139 const char *start = p;
140 while (*p && std::isalpha(
static_cast<unsigned char>(*p))) {
143 return (p - start) >= 3;
151 }
else if (*p ==
'+') {
155 int hours = parse_uint(p);
161 minutes = parse_uint(p);
164 seconds = parse_uint(p);
168 return sign * (hours * 3600 + minutes * 60 + seconds);
172static void parse_transition_time(
const char *&p, DSTRule &
rule) {
184 int remaining = julian_day;
189 if (remaining <=
dim) {
202 int remaining = day_of_year;
207 if (remaining < days_this_month) {
211 remaining -= days_this_month;
223 if (*p ==
'M' || *p ==
'm') {
246 }
else if (*p ==
'J' || *p ==
'j') {
255 }
else if (std::isdigit(
static_cast<unsigned char>(*p))) {
268 parse_transition_time(p,
rule);
287 for (
int y = 1970;
y <
year;
y++) {
288 days += days_in_year(
y);
303 int first_occurrence = 1 + days_until_first;
308 day = first_occurrence;
351 int year = internal::epoch_to_year(utc_epoch);
375 if (!tz_string || !*tz_string) {
379 const char *p = tz_string;
393 if (!*p || (!std::isdigit(
static_cast<unsigned char>(*p)) && *p !=
'+' && *p !=
'-')) {
410 if (!std::isalpha(
static_cast<unsigned char>(*p)) && *p !=
'<') {
419 if (*p && *p !=
',' && (std::isdigit(
static_cast<unsigned char>(*p)) || *p ==
'+' || *p ==
'-')) {
455 time_t local_epoch = utc_epoch - offset;
458 out_tm->tm_isdst = in_dst ? 1 : 0;
472extern "C" struct tm *
localtime_r(
const time_t *timer,
struct tm *result) {
473 if (timer ==
nullptr || result ==
nullptr) {
483 static struct tm localtime_buf;
time_t const DSTRule int32_t base_offset_seconds
int day_of_week(int year, int month, int day)
Calculate day of week for any date (0 = Sunday) Uses a simplified algorithm that works for years 1970...
int days_in_month(int year, int month)
Get the number of days in a month.
int32_t parse_offset(const char *&p)
Parse an offset in format [-]hh[:mm[:ss]].
time_t const DSTRule & rule
bool parse_dst_rule(const char *&p, DSTRule &rule)
Parse a DST rule in format Mm.w.d[/time], Jn[/time], or n[/time].
bool skip_tz_name(const char *&p)
Skip a timezone name (letters or <...> quoted format)
void julian_to_month_day(int julian_day, int &month, int &day)
Convert Julian day (J format, 1-365 not counting Feb 29) to month/day.
void day_of_year_to_month_day(int day_of_year, int year, int &month, int &day)
Convert day of year (plain format, 0-365 counting Feb 29) to month/day.
bool is_leap_year(int year)
Check if a year is a leap year.
time_t calculate_dst_transition(int year, const DSTRule &rule, int32_t base_offset_seconds)
Calculate the epoch timestamp for a DST transition in a given year.
void epoch_to_tm_utc(time_t epoch, struct tm *out_tm)
Convert epoch to year/month/day/hour/min/sec (UTC)
void set_global_tz(const ParsedTimezone &tz)
Set the global timezone used by epoch_to_local_tm() when called without a timezone.
bool is_in_dst(time_t utc_epoch, const ParsedTimezone &tz)
Check if a given UTC epoch falls within DST for the parsed timezone.
const ParsedTimezone & get_global_tz()
Get the global timezone.
ESPTime __attribute__((noinline)) RealTimeClock
bool parse_posix_tz(const char *tz_string, ParsedTimezone &result)
Parse a POSIX TZ string into a ParsedTimezone struct.
@ JULIAN_NO_LEAP
J format: Jn (day 1-365, Feb 29 not counted)
@ NONE
No DST rule (used to indicate no DST)
@ DAY_OF_YEAR
Plain number: n (day 0-365, Feb 29 counted in leap years)
@ MONTH_WEEK_DAY
M format: Mm.w.d (e.g., M3.2.0 = 2nd Sunday of March)
bool const ParsedTimezone & tz
bool epoch_to_local_tm(time_t utc_epoch, const ParsedTimezone &tz, struct tm *out_tm)
Convert a UTC epoch to local time using the parsed timezone.
struct tm * localtime_r(const time_t *timer, struct tm *result)
struct tm * localtime(const time_t *timer)
Rule for DST transition (packed for 32-bit: 12 bytes)
uint16_t day
Day of year (for JULIAN_NO_LEAP and DAY_OF_YEAR)
DSTRuleType type
Type of rule.
uint8_t week
Week 1-5, 5 = last (for MONTH_WEEK_DAY)
int32_t time_seconds
Seconds after midnight (default 7200 = 2:00 AM)
uint8_t day_of_week
Day 0-6, 0 = Sunday (for MONTH_WEEK_DAY)
uint8_t month
Month 1-12 (for MONTH_WEEK_DAY)
Parsed POSIX timezone information (packed for 32-bit: 32 bytes)
bool has_dst() const
Check if this timezone has DST rules.
DSTRule dst_end
When DST ends.
DSTRule dst_start
When DST starts.
int32_t dst_offset_seconds
DST offset from UTC in seconds.
int32_t std_offset_seconds
Standard time offset from UTC in seconds (positive = west)