1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 04:47:34 +00:00

LibC: Implement tzset with time zone awareness in accordance with POSIX

This commit is contained in:
Timothy Flynn 2022-01-24 14:42:51 -05:00 committed by Linus Groh
parent 6095500263
commit b1ea585149
2 changed files with 100 additions and 5 deletions

View file

@ -10,6 +10,25 @@
const auto expected_epoch = "Thu Jan 1 00:00:00 1970\n"sv;
class TimeZoneGuard {
public:
TimeZoneGuard()
: m_tz(getenv("TZ"))
{
}
~TimeZoneGuard()
{
if (m_tz)
setenv("TZ", m_tz, 1);
else
unsetenv("TZ");
}
private:
char const* m_tz { nullptr };
};
TEST_CASE(asctime)
{
time_t epoch = 0;
@ -41,3 +60,48 @@ TEST_CASE(ctime_r)
EXPECT_EQ(expected_epoch, StringView(result));
}
TEST_CASE(tzset)
{
TimeZoneGuard guard;
auto set_tz = [](char const* tz) {
setenv("TZ", tz, 1);
tzset();
};
set_tz("UTC");
EXPECT_EQ(timezone, 0);
EXPECT_EQ(altzone, 0);
EXPECT_EQ(daylight, 0);
EXPECT_EQ(tzname[0], "UTC"sv);
EXPECT_EQ(tzname[1], "UTC"sv);
set_tz("America/New_York");
EXPECT_EQ(timezone, 5 * 60 * 60);
EXPECT_EQ(altzone, 4 * 60 * 60);
EXPECT_EQ(daylight, 1);
EXPECT_EQ(tzname[0], "EST"sv);
EXPECT_EQ(tzname[1], "EDT"sv);
set_tz("America/Phoenix");
EXPECT_EQ(timezone, 7 * 60 * 60);
EXPECT_EQ(altzone, 7 * 60 * 60);
EXPECT_EQ(daylight, 0);
EXPECT_EQ(tzname[0], "MST"sv);
EXPECT_EQ(tzname[1], "MST"sv);
set_tz("America/Asuncion");
EXPECT_EQ(timezone, 4 * 60 * 60);
EXPECT_EQ(altzone, 3 * 60 * 60);
EXPECT_EQ(daylight, 1);
EXPECT_EQ(tzname[0], "-04"sv);
EXPECT_EQ(tzname[1], "-03"sv);
set_tz("CET");
EXPECT_EQ(timezone, -1 * 60 * 60);
EXPECT_EQ(altzone, -2 * 60 * 60);
EXPECT_EQ(daylight, 1);
EXPECT_EQ(tzname[0], "CET"sv);
EXPECT_EQ(tzname[1], "CEST"sv);
}

View file

@ -11,7 +11,9 @@
#include <LibTimeZone/TimeZone.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/times.h>
@ -358,15 +360,44 @@ long altzone;
char* tzname[2];
int daylight;
static char __tzname_standard[TZNAME_MAX];
static char __tzname_daylight[TZNAME_MAX];
constexpr const char* __utc = "UTC";
void tzset()
{
// FIXME: Here we pretend we are in UTC+0.
timezone = 0;
daylight = 0;
tzname[0] = const_cast<char*>(__utc);
tzname[1] = const_cast<char*>(__utc);
// FIXME: Actually parse the TZ environment variable, described here:
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08
StringView time_zone;
if (char* tz = getenv("TZ"); tz != nullptr)
time_zone = tz;
else
time_zone = TimeZone::current_time_zone();
auto set_default_values = []() {
timezone = 0;
altzone = 0;
daylight = 0;
tzname[0] = const_cast<char*>(__utc);
tzname[1] = const_cast<char*>(__utc);
};
if (auto offsets = TimeZone::get_named_time_zone_offsets(time_zone, AK::Time::now_realtime()); offsets.has_value()) {
if (!offsets->at(0).name.copy_characters_to_buffer(__tzname_standard, TZNAME_MAX))
return set_default_values();
if (!offsets->at(1).name.copy_characters_to_buffer(__tzname_daylight, TZNAME_MAX))
return set_default_values();
// timezone and altzone are seconds west of UTC, i.e. the offsets are negated.
timezone = -offsets->at(0).seconds;
altzone = -offsets->at(1).seconds;
daylight = timezone != altzone;
tzname[0] = __tzname_standard;
tzname[1] = __tzname_daylight;
} else {
set_default_values();
}
}
clock_t clock()