diff --git a/Base/etc/timezone b/Base/etc/timezone new file mode 100644 index 0000000000..e2e7775815 --- /dev/null +++ b/Base/etc/timezone @@ -0,0 +1 @@ +UTC diff --git a/Userland/Libraries/LibTimeZone/TimeZone.cpp b/Userland/Libraries/LibTimeZone/TimeZone.cpp index f9953c53b2..8629ca7380 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.cpp +++ b/Userland/Libraries/LibTimeZone/TimeZone.cpp @@ -4,8 +4,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include -#include +#include namespace TimeZone { @@ -20,15 +22,77 @@ enum class TimeZone : u16 { }; #endif -StringView current_time_zone() -{ - static bool initialized_time_zone = false; - if (!initialized_time_zone) { - initialized_time_zone = true; - tzset(); +class TimeZoneFile { +public: + TimeZoneFile(char const* mode) + : m_file(fopen("/etc/timezone", mode)) + { + if (m_file) + flockfile(m_file); } - return canonicalize_time_zone(tzname[0]).value_or("UTC"sv); + ~TimeZoneFile() + { + if (m_file) { + funlockfile(m_file); + fclose(m_file); + } + } + + ErrorOr read_time_zone() + { + if (!m_file) + return Error::from_string_literal("Could not open /etc/timezone"); + + Array buffer; + size_t bytes = fread(buffer.data(), 1, buffer.size(), m_file); + + if (bytes == 0) + return Error::from_string_literal("Could not read time zone from /etc/timezone"); + + return String(buffer.span().slice(0, bytes)).trim_whitespace(); + } + + ErrorOr write_time_zone(StringView time_zone) + { + if (!m_file) + return Error::from_string_literal("Could not open /etc/timezone"); + + auto bytes = fwrite(time_zone.characters_without_null_termination(), 1, time_zone.length(), m_file); + if (bytes != time_zone.length()) + return Error::from_string_literal("Could not write new time zone to /etc/timezone"); + + return {}; + } + +private: + FILE* m_file { nullptr }; +}; + +StringView current_time_zone() +{ + TimeZoneFile time_zone_file("r"); + + // FIXME: Propagate the error to existing callers. + if (auto time_zone = time_zone_file.read_time_zone(); !time_zone.is_error()) + return canonicalize_time_zone(time_zone.value()).value_or("UTC"sv); + + return "UTC"sv; +} + +ErrorOr change_time_zone([[maybe_unused]] StringView time_zone) +{ +#ifdef __serenity__ + TimeZoneFile time_zone_file("w"); + + if (auto new_time_zone = canonicalize_time_zone(time_zone); new_time_zone.has_value()) + return time_zone_file.write_time_zone(*new_time_zone); + + return Error::from_string_literal("Provided time zone is not supported"); +#else + // Do not even attempt to change the time zone of someone's host machine. + return {}; +#endif } Span __attribute__((weak)) all_time_zones() diff --git a/Userland/Libraries/LibTimeZone/TimeZone.h b/Userland/Libraries/LibTimeZone/TimeZone.h index 493f89eb4d..fb4d58177a 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.h +++ b/Userland/Libraries/LibTimeZone/TimeZone.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -25,6 +26,7 @@ struct Offset { }; StringView current_time_zone(); +ErrorOr change_time_zone(StringView time_zone); Span all_time_zones(); Optional time_zone_from_string(StringView time_zone);