diff --git a/Userland/Libraries/LibC/time.cpp b/Userland/Libraries/LibC/time.cpp index c1df69c6a5..a1591c8051 100644 --- a/Userland/Libraries/LibC/time.cpp +++ b/Userland/Libraries/LibC/time.cpp @@ -112,13 +112,18 @@ static bool is_valid_time(time_t timestamp) return (timestamp >= smallest_possible_time) && (timestamp <= biggest_possible_time); } -static struct tm* time_to_tm(struct tm* tm, time_t t) +static struct tm* time_to_tm(struct tm* tm, time_t t, StringView time_zone) { if (!is_valid_time(t)) { errno = EOVERFLOW; return nullptr; } + if (auto offset = TimeZone::get_time_zone_offset(time_zone, AK::Time::from_seconds(t)); offset.has_value()) { + tm->tm_isdst = offset->in_dst == TimeZone::InDST::Yes; + t += offset->seconds; + } + int year = 1970; for (; t >= days_in_year(year) * __seconds_per_day; ++year) t -= days_in_year(year) * __seconds_per_day; @@ -146,7 +151,7 @@ static struct tm* time_to_tm(struct tm* tm, time_t t) return tm; } -static time_t tm_to_time(struct tm* tm, long timezone_adjust_seconds) +static time_t tm_to_time(struct tm* tm, StringView time_zone) { // "The original values of the tm_wday and tm_yday components of the structure are ignored, // and the original values of the other components are not restricted to the ranges described in . @@ -156,8 +161,6 @@ static time_t tm_to_time(struct tm* tm, long timezone_adjust_seconds) // but with their values forced to the ranges indicated in the entry; // the final value of tm_mday shall not be set until tm_mon and tm_year are determined." - // FIXME: Handle tm_isdst eventually. - tm->tm_year += tm->tm_mon / 12; tm->tm_mon %= 12; if (tm->tm_mon < 0) { @@ -167,7 +170,17 @@ static time_t tm_to_time(struct tm* tm, long timezone_adjust_seconds) tm->tm_yday = day_of_year(1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday); time_t days_since_epoch = years_to_days_since_epoch(1900 + tm->tm_year) + tm->tm_yday; - auto timestamp = ((days_since_epoch * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec + timezone_adjust_seconds; + auto timestamp = ((days_since_epoch * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec; + + if (tm->tm_isdst < 0) { + if (auto offset = TimeZone::get_time_zone_offset(time_zone, AK::Time::from_seconds(timestamp)); offset.has_value()) + timestamp -= offset->seconds; + } else { + auto index = tm->tm_isdst == 0 ? 0 : 1; + + if (auto offsets = TimeZone::get_named_time_zone_offsets(time_zone, AK::Time::from_seconds(timestamp)); offsets.has_value()) + timestamp -= offsets->at(index).seconds; + } if (!is_valid_time(timestamp)) { errno = EOVERFLOW; @@ -180,7 +193,7 @@ static time_t tm_to_time(struct tm* tm, long timezone_adjust_seconds) time_t mktime(struct tm* tm) { tzset(); - return tm_to_time(tm, daylight ? altzone : timezone); + return tm_to_time(tm, __tzname); } struct tm* localtime(time_t const* t) @@ -196,12 +209,12 @@ struct tm* localtime_r(time_t const* t, struct tm* tm) if (!t) return nullptr; - return time_to_tm(tm, *t - (daylight ? altzone : timezone)); + return time_to_tm(tm, *t, __tzname); } time_t timegm(struct tm* tm) { - return tm_to_time(tm, 0); + return tm_to_time(tm, { __utc, __builtin_strlen(__utc) }); } struct tm* gmtime(time_t const* t) @@ -214,7 +227,7 @@ struct tm* gmtime_r(time_t const* t, struct tm* tm) { if (!t) return nullptr; - return time_to_tm(tm, *t); + return time_to_tm(tm, *t, { __utc, __builtin_strlen(__utc) }); } char* asctime(const struct tm* tm) diff --git a/Userland/Libraries/LibCore/DateTime.cpp b/Userland/Libraries/LibCore/DateTime.cpp index bf09253f77..350e076658 100644 --- a/Userland/Libraries/LibCore/DateTime.cpp +++ b/Userland/Libraries/LibCore/DateTime.cpp @@ -92,13 +92,13 @@ String DateTime::to_string(StringView format) const int const format_len = format.length(); auto format_time_zone_offset = [&](bool with_separator) { -#if defined(__serenity__) - auto offset_seconds = daylight ? -altzone : -timezone; -#elif !defined(__FreeBSD__) - auto offset_seconds = -timezone; -#else - auto offset_seconds = 0; -#endif + struct tm gmt_tm; + gmtime_r(&m_timestamp, &gmt_tm); + + gmt_tm.tm_isdst = -1; + auto gmt_timestamp = mktime(&gmt_tm); + + auto offset_seconds = static_cast(difftime(m_timestamp, gmt_timestamp)); StringView offset_sign; if (offset_seconds >= 0) { @@ -251,11 +251,7 @@ String DateTime::to_string(StringView format) const format_time_zone_offset(true); break; case 'Z': { -#ifndef __FreeBSD__ - auto const* timezone_name = tzname[daylight]; -#else - auto const* timezone_name = tzname[0]; -#endif + auto const* timezone_name = tzname[tm.tm_isdst == 0 ? 0 : 1]; builder.append({ timezone_name, strlen(timezone_name) }); break; }