From c1323febc28c4d21b555b2449ec7473dc1c0d568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kleines=20Filmr=C3=B6llchen?= Date: Mon, 13 Mar 2023 22:06:22 +0100 Subject: [PATCH] AK: Introduce UnixDateTime This is a generic wrapper for a time instant relative to the unix epoch, and does not account for leap seconds. It should be used in place of Duration in most current cases. --- AK/Forward.h | 2 + AK/Time.cpp | 10 +++ AK/Time.h | 123 ++++++++++++++++++++++++++ Userland/Libraries/LibIPC/Decoder.cpp | 7 ++ Userland/Libraries/LibIPC/Decoder.h | 3 + Userland/Libraries/LibIPC/Encoder.cpp | 6 ++ Userland/Libraries/LibIPC/Encoder.h | 3 + 7 files changed, 154 insertions(+) diff --git a/AK/Forward.h b/AK/Forward.h index a11fe2c6f27..0b3f773a89a 100644 --- a/AK/Forward.h +++ b/AK/Forward.h @@ -46,6 +46,7 @@ class StringBuilder; class StringImpl; class StringView; class URL; +class UnixDateTime; class Utf16View; class Utf32CodePointIterator; class Utf32View; @@ -196,6 +197,7 @@ using AK::StringBuilder; using AK::StringImpl; using AK::StringView; using AK::Traits; +using AK::UnixDateTime; using AK::URL; using AK::Utf16View; using AK::Utf32CodePointIterator; diff --git a/AK/Time.cpp b/AK/Time.cpp index a1c5bd973e2..ae76cf81100 100644 --- a/AK/Time.cpp +++ b/AK/Time.cpp @@ -237,6 +237,16 @@ Duration Duration::now_monotonic_coarse() return now_time_from_clock(CLOCK_MONOTONIC_COARSE); } +UnixDateTime UnixDateTime::now() +{ + return UnixDateTime { now_time_from_clock(CLOCK_REALTIME) }; +} + +UnixDateTime UnixDateTime::now_coarse() +{ + return UnixDateTime { now_time_from_clock(CLOCK_REALTIME_COARSE) }; +} + #endif } diff --git a/AK/Time.h b/AK/Time.h index 7eebb43508c..9cb986f7a34 100644 --- a/AK/Time.h +++ b/AK/Time.h @@ -354,6 +354,128 @@ private: u32 m_nanoseconds { 0 }; // Always less than 1'000'000'000 }; +namespace Detail { + +// Common base class for all unaware time types. +// Naive, or unaware, in the time context means to make heavily simplifying assumptions about time. +// In the case of this class and its children, they are not timezone-aware and strictly ordered. +class UnawareTime { +public: + constexpr UnawareTime(UnawareTime const&) = default; + constexpr UnawareTime& operator=(UnawareTime const&) = default; + + [[nodiscard]] timespec to_timespec() const { return m_offset.to_timespec(); } + // Rounds towards -inf. + [[nodiscard]] timeval to_timeval() const { return m_offset.to_timeval(); } + + // We intentionally do not define a comparison operator here to avoid accidentally comparing incompatible time types. + +protected: + constexpr explicit UnawareTime(Duration offset) + : m_offset(offset) + { + } + + Duration m_offset {}; +}; + +} + +// Naive UNIX time, representing an offset from 1970-01-01 00:00:00Z, without accounting for UTC leap seconds. +// This class is mainly intended for interoperating with anything that expects a unix timestamp. +class UnixDateTime : public Detail::UnawareTime { +public: + constexpr UnixDateTime() + : Detail::UnawareTime(Duration::zero()) + { + } + + constexpr static UnixDateTime epoch() + { + return UnixDateTime {}; + } + + // Creates UNIX time from a unix timestamp. + // Note that the returned time is probably not equivalent to the same timestamp in UTC time, since UNIX time does not observe leap seconds. + [[nodiscard]] constexpr static UnixDateTime from_unix_time_parts(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond) + { + constexpr auto milliseconds_per_day = 86'400'000; + constexpr auto milliseconds_per_hour = 3'600'000; + constexpr auto milliseconds_per_minute = 60'000; + constexpr auto milliseconds_per_second = 1'000; + + i64 days = days_since_epoch(year, month, day); + i64 milliseconds_since_epoch = days * milliseconds_per_day; + + milliseconds_since_epoch += hour * milliseconds_per_hour; + milliseconds_since_epoch += minute * milliseconds_per_minute; + milliseconds_since_epoch += second * milliseconds_per_second; + milliseconds_since_epoch += millisecond; + + return from_milliseconds_since_epoch(milliseconds_since_epoch); + } + + [[nodiscard]] constexpr static UnixDateTime from_seconds_since_epoch(i64 seconds) + { + return UnixDateTime { Duration::from_seconds(seconds) }; + } + + [[nodiscard]] constexpr static UnixDateTime from_milliseconds_since_epoch(i64 milliseconds) + { + return UnixDateTime { Duration::from_milliseconds(milliseconds) }; + } + + [[nodiscard]] constexpr static UnixDateTime from_nanoseconds_since_epoch(i64 nanoseconds) + { + return UnixDateTime { Duration::from_nanoseconds(nanoseconds) }; + } + + [[nodiscard]] static UnixDateTime from_unix_timespec(struct timespec const& time) + { + return UnixDateTime { Duration::from_timespec(time) }; + } + + // Earliest and latest representable UNIX timestamps. + [[nodiscard]] constexpr static UnixDateTime earliest() { return UnixDateTime { Duration::min() }; } + [[nodiscard]] constexpr static UnixDateTime latest() { return UnixDateTime { Duration::max() }; } + + [[nodiscard]] constexpr Duration offset_to_epoch() const { return m_offset; } + // May return an epoch offset *after* what this UnixDateTime contains, because rounding to seconds occurs. + [[nodiscard]] i64 seconds_since_epoch() const { return m_offset.to_seconds(); } + [[nodiscard]] i64 milliseconds_since_epoch() const { return m_offset.to_milliseconds(); } + [[nodiscard]] i64 nanoseconds_since_epoch() const { return m_offset.to_nanoseconds(); } + // Never returns a point after this UnixDateTime, since fractional seconds are cut off. + [[nodiscard]] i64 truncated_seconds_since_epoch() const { return m_offset.to_truncated_seconds(); } + + // Offsetting a UNIX time by a duration yields another UNIX time. + constexpr UnixDateTime operator+(Duration const& other) const { return UnixDateTime { m_offset + other }; }; + constexpr UnixDateTime& operator+=(Duration const& other) + { + this->m_offset = this->m_offset + other; + return *this; + }; + constexpr UnixDateTime operator-(Duration const& other) const { return UnixDateTime { m_offset - other }; }; + // Subtracting two UNIX times yields their time difference. + constexpr Duration operator-(UnixDateTime const& other) const { return m_offset - other.m_offset; }; + +#ifndef KERNEL + [[nodiscard]] static UnixDateTime now(); + [[nodiscard]] static UnixDateTime now_coarse(); +#endif + + constexpr bool operator==(UnixDateTime const& other) const + { + return this->m_offset == other.m_offset; + } + constexpr int operator<=>(UnixDateTime const& other) const { return this->m_offset <=> other.m_offset; } + +private: + constexpr explicit UnixDateTime(Duration offset) + : Detail::UnawareTime(offset) + { + } +}; + template inline void timeval_sub(TimevalType const& a, TimevalType const& b, TimevalType& result) { @@ -451,5 +573,6 @@ using AK::timespec_to_timeval; using AK::timeval_add; using AK::timeval_sub; using AK::timeval_to_timespec; +using AK::UnixDateTime; using AK::years_to_days_since_epoch; #endif diff --git a/Userland/Libraries/LibIPC/Decoder.cpp b/Userland/Libraries/LibIPC/Decoder.cpp index 3a2f6347a48..69b418f96b3 100644 --- a/Userland/Libraries/LibIPC/Decoder.cpp +++ b/Userland/Libraries/LibIPC/Decoder.cpp @@ -76,6 +76,13 @@ ErrorOr decode(Decoder& decoder) return AK::Duration::from_nanoseconds(nanoseconds); } +template<> +ErrorOr decode(Decoder& decoder) +{ + auto nanoseconds = TRY(decoder.decode()); + return AK::UnixDateTime::from_nanoseconds_since_epoch(nanoseconds); +} + template<> ErrorOr decode(Decoder& decoder) { diff --git a/Userland/Libraries/LibIPC/Decoder.h b/Userland/Libraries/LibIPC/Decoder.h index 1422fec6f48..e342082f8f3 100644 --- a/Userland/Libraries/LibIPC/Decoder.h +++ b/Userland/Libraries/LibIPC/Decoder.h @@ -96,6 +96,9 @@ ErrorOr decode(Decoder&); template<> ErrorOr decode(Decoder&); +template<> +ErrorOr decode(Decoder&); + template<> ErrorOr decode(Decoder&); diff --git a/Userland/Libraries/LibIPC/Encoder.cpp b/Userland/Libraries/LibIPC/Encoder.cpp index c14c163bcb0..311c4cc67db 100644 --- a/Userland/Libraries/LibIPC/Encoder.cpp +++ b/Userland/Libraries/LibIPC/Encoder.cpp @@ -90,6 +90,12 @@ ErrorOr encode(Encoder& encoder, Duration const& value) return encoder.encode(value.to_nanoseconds()); } +template<> +ErrorOr encode(Encoder& encoder, UnixDateTime const& value) +{ + return encoder.encode(value.nanoseconds_since_epoch()); +} + template<> ErrorOr encode(Encoder& encoder, URL const& value) { diff --git a/Userland/Libraries/LibIPC/Encoder.h b/Userland/Libraries/LibIPC/Encoder.h index d753a7940c4..1588f124dd3 100644 --- a/Userland/Libraries/LibIPC/Encoder.h +++ b/Userland/Libraries/LibIPC/Encoder.h @@ -125,6 +125,9 @@ ErrorOr encode(Encoder&, JsonValue const&); template<> ErrorOr encode(Encoder&, Duration const&); +template<> +ErrorOr encode(Encoder&, UnixDateTime const&); + template<> ErrorOr encode(Encoder&, URL const&);