[libc-commits] [libc] [libc] create TimeReader to look at a struct tm (PR #126138)
Michael Jones via libc-commits
libc-commits at lists.llvm.org
Thu Feb 6 13:51:49 PST 2025
https://github.com/michaelrj-google created https://github.com/llvm/llvm-project/pull/126138
In the process of adding strftime (#122556) I wrote this utility class
to simplify reading from a struct tm. It provides helper functions that
return basically everything needed by strftime. It's not tested
directly, but it is thoroughly exercised by the strftime tests.
>From bedf3a00ea13e807d4a67d28bdac853eb21ee3ef Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Thu, 6 Feb 2025 13:47:27 -0800
Subject: [PATCH] [libc] create TimeReader to look at a struct tm
In the process of adding strftime (#122556) I wrote this utility class
to simplify reading from a struct tm. It provides helper functions that
return basically everything needed by strftime. It's not tested
directly, but it is thoroughly exercised by the strftime tests.
---
libc/include/llvm-libc-types/struct_tm.h | 1 +
libc/src/time/CMakeLists.txt | 2 +
libc/src/time/mktime.cpp | 94 +--------
libc/src/time/time_constants.h | 17 +-
libc/src/time/time_utils.cpp | 92 ++++++++
libc/src/time/time_utils.h | 256 +++++++++++++++++++++++
6 files changed, 368 insertions(+), 94 deletions(-)
diff --git a/libc/include/llvm-libc-types/struct_tm.h b/libc/include/llvm-libc-types/struct_tm.h
index 9fef7c5718ea4a5..2ec74ecac0293b6 100644
--- a/libc/include/llvm-libc-types/struct_tm.h
+++ b/libc/include/llvm-libc-types/struct_tm.h
@@ -19,6 +19,7 @@ struct tm {
int tm_wday; // days since Sunday
int tm_yday; // days since January
int tm_isdst; // Daylight Saving Time flag
+ // TODO: add tm_gmtoff and tm_zone? (posix extensions)
};
#endif // LLVM_LIBC_TYPES_STRUCT_TM_H
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index ef9bfe57bc4ec29..dd28aa67280b7f3 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -22,6 +22,8 @@ add_object_library(
DEPENDS
libc.include.time
libc.src.__support.CPP.limits
+ libc.src.__support.CPP.string_view
+ libc.src.__support.CPP.optional
libc.src.errno.errno
.time_constants
libc.hdr.types.time_t
diff --git a/libc/src/time/mktime.cpp b/libc/src/time/mktime.cpp
index 3874cad02facbd6..fc05ff2930434f9 100644
--- a/libc/src/time/mktime.cpp
+++ b/libc/src/time/mktime.cpp
@@ -14,100 +14,8 @@
namespace LIBC_NAMESPACE_DECL {
-// Returns number of years from (1, year).
-static constexpr int64_t get_num_of_leap_years_before(int64_t year) {
- return (year / 4) - (year / 100) + (year / 400);
-}
-
-// Returns True if year is a leap year.
-static constexpr bool is_leap_year(const int64_t year) {
- return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
-}
-
LLVM_LIBC_FUNCTION(time_t, mktime, (struct tm * tm_out)) {
- // Unlike most C Library functions, mktime doesn't just die on bad input.
- // TODO(rtenneti); Handle leap seconds.
- int64_t tm_year_from_base = tm_out->tm_year + time_constants::TIME_YEAR_BASE;
-
- // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038.
- if (sizeof(time_t) == 4 &&
- tm_year_from_base >= time_constants::END_OF32_BIT_EPOCH_YEAR) {
- if (tm_year_from_base > time_constants::END_OF32_BIT_EPOCH_YEAR)
- return time_utils::out_of_range();
- if (tm_out->tm_mon > 0)
- return time_utils::out_of_range();
- if (tm_out->tm_mday > 19)
- return time_utils::out_of_range();
- else if (tm_out->tm_mday == 19) {
- if (tm_out->tm_hour > 3)
- return time_utils::out_of_range();
- else if (tm_out->tm_hour == 3) {
- if (tm_out->tm_min > 14)
- return time_utils::out_of_range();
- else if (tm_out->tm_min == 14) {
- if (tm_out->tm_sec > 7)
- return time_utils::out_of_range();
- }
- }
- }
- }
-
- // Years are ints. A 32-bit year will fit into a 64-bit time_t.
- // A 64-bit year will not.
- static_assert(
- sizeof(int) == 4,
- "ILP64 is unimplemented. This implementation requires 32-bit integers.");
-
- // Calculate number of months and years from tm_mon.
- int64_t month = tm_out->tm_mon;
- if (month < 0 || month >= time_constants::MONTHS_PER_YEAR - 1) {
- int64_t years = month / 12;
- month %= 12;
- if (month < 0) {
- years--;
- month += 12;
- }
- tm_year_from_base += years;
- }
- bool tm_year_is_leap = is_leap_year(tm_year_from_base);
-
- // Calculate total number of days based on the month and the day (tm_mday).
- int64_t total_days = tm_out->tm_mday - 1;
- for (int64_t i = 0; i < month; ++i)
- total_days += time_constants::NON_LEAP_YEAR_DAYS_IN_MONTH[i];
- // Add one day if it is a leap year and the month is after February.
- if (tm_year_is_leap && month > 1)
- total_days++;
-
- // Calculate total numbers of days based on the year.
- total_days += (tm_year_from_base - time_constants::EPOCH_YEAR) *
- time_constants::DAYS_PER_NON_LEAP_YEAR;
- if (tm_year_from_base >= time_constants::EPOCH_YEAR) {
- total_days += get_num_of_leap_years_before(tm_year_from_base - 1) -
- get_num_of_leap_years_before(time_constants::EPOCH_YEAR);
- } else if (tm_year_from_base >= 1) {
- total_days -= get_num_of_leap_years_before(time_constants::EPOCH_YEAR) -
- get_num_of_leap_years_before(tm_year_from_base - 1);
- } else {
- // Calculate number of leap years until 0th year.
- total_days -= get_num_of_leap_years_before(time_constants::EPOCH_YEAR) -
- get_num_of_leap_years_before(0);
- if (tm_year_from_base <= 0) {
- total_days -= 1; // Subtract 1 for 0th year.
- // Calculate number of leap years until -1 year
- if (tm_year_from_base < 0) {
- total_days -= get_num_of_leap_years_before(-tm_year_from_base) -
- get_num_of_leap_years_before(1);
- }
- }
- }
-
- // TODO: https://github.com/llvm/llvm-project/issues/121962
- // Need to handle timezone and update of tm_isdst.
- int64_t seconds = tm_out->tm_sec +
- tm_out->tm_min * time_constants::SECONDS_PER_MIN +
- tm_out->tm_hour * time_constants::SECONDS_PER_HOUR +
- total_days * time_constants::SECONDS_PER_DAY;
+ int64_t seconds = time_utils::mktime_internal(tm_out);
// Update the tm structure's year, month, day, etc. from seconds.
if (time_utils::update_from_seconds(seconds, tm_out) < 0)
diff --git a/libc/src/time/time_constants.h b/libc/src/time/time_constants.h
index 3e25f741745ab53..ab17862fdd957b9 100644
--- a/libc/src/time/time_constants.h
+++ b/libc/src/time/time_constants.h
@@ -18,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL {
namespace time_constants {
enum Month : int {
- JANUARY,
+ JANUARY = 0,
FEBRUARY,
MARCH,
APRIL,
@@ -32,6 +32,16 @@ enum Month : int {
DECEMBER
};
+enum WeekDay : int {
+ SUNDAY = 0,
+ MONDAY,
+ TUESDAY,
+ WEDNESDAY,
+ THURSDAY,
+ FRIDAY,
+ SATURDAY
+};
+
constexpr int SECONDS_PER_MIN = 60;
constexpr int MINUTES_PER_HOUR = 60;
constexpr int HOURS_PER_DAY = 24;
@@ -40,6 +50,9 @@ constexpr int MONTHS_PER_YEAR = 12;
constexpr int DAYS_PER_NON_LEAP_YEAR = 365;
constexpr int DAYS_PER_LEAP_YEAR = 366;
+constexpr int LAST_DAY_OF_NON_LEAP_YEAR = DAYS_PER_NON_LEAP_YEAR - 1;
+constexpr int LAST_DAY_OF_LEAP_YEAR = DAYS_PER_LEAP_YEAR - 1;
+
constexpr int SECONDS_PER_HOUR = SECONDS_PER_MIN * MINUTES_PER_HOUR;
constexpr int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
constexpr int NUMBER_OF_SECONDS_IN_LEAP_YEAR =
@@ -49,6 +62,8 @@ constexpr int TIME_YEAR_BASE = 1900;
constexpr int EPOCH_YEAR = 1970;
constexpr int EPOCH_WEEK_DAY = 4;
+constexpr int ISO_FIRST_DAY_OF_YEAR = 3; // the 4th day of the year, 0-indexed.
+
// For asctime the behavior is undefined if struct tm's tm_wday or tm_mon are
// not within the normal ranges as defined in <time.h>, or if struct tm's
// tm_year exceeds {INT_MAX}-1990, or if the below asctime_internal algorithm
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index abc93b8cb961ed7..585cae0fc33ce6a 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -15,6 +15,98 @@
namespace LIBC_NAMESPACE_DECL {
namespace time_utils {
+// TODO: clean this up in a followup patch
+int64_t mktime_internal(const struct tm *tm_out) {
+ // Unlike most C Library functions, mktime doesn't just die on bad input.
+ // TODO(rtenneti); Handle leap seconds.
+ int64_t tm_year_from_base = tm_out->tm_year + time_constants::TIME_YEAR_BASE;
+
+ // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038.
+ if (sizeof(time_t) == 4 &&
+ tm_year_from_base >= time_constants::END_OF32_BIT_EPOCH_YEAR) {
+ if (tm_year_from_base > time_constants::END_OF32_BIT_EPOCH_YEAR)
+ return time_utils::out_of_range();
+ if (tm_out->tm_mon > 0)
+ return time_utils::out_of_range();
+ if (tm_out->tm_mday > 19)
+ return time_utils::out_of_range();
+ else if (tm_out->tm_mday == 19) {
+ if (tm_out->tm_hour > 3)
+ return time_utils::out_of_range();
+ else if (tm_out->tm_hour == 3) {
+ if (tm_out->tm_min > 14)
+ return time_utils::out_of_range();
+ else if (tm_out->tm_min == 14) {
+ if (tm_out->tm_sec > 7)
+ return time_utils::out_of_range();
+ }
+ }
+ }
+ }
+
+ // Years are ints. A 32-bit year will fit into a 64-bit time_t.
+ // A 64-bit year will not.
+ static_assert(
+ sizeof(int) == 4,
+ "ILP64 is unimplemented. This implementation requires 32-bit integers.");
+
+ // Calculate number of months and years from tm_mon.
+ int64_t month = tm_out->tm_mon;
+ if (month < 0 || month >= time_constants::MONTHS_PER_YEAR - 1) {
+ int64_t years = month / 12;
+ month %= 12;
+ if (month < 0) {
+ years--;
+ month += 12;
+ }
+ tm_year_from_base += years;
+ }
+ bool tm_year_is_leap = time_utils::is_leap_year(tm_year_from_base);
+
+ // Calculate total number of days based on the month and the day (tm_mday).
+ int64_t total_days = tm_out->tm_mday - 1;
+ for (int64_t i = 0; i < month; ++i)
+ total_days += time_constants::NON_LEAP_YEAR_DAYS_IN_MONTH[i];
+ // Add one day if it is a leap year and the month is after February.
+ if (tm_year_is_leap && month > 1)
+ total_days++;
+
+ // Calculate total numbers of days based on the year.
+ total_days += (tm_year_from_base - time_constants::EPOCH_YEAR) *
+ time_constants::DAYS_PER_NON_LEAP_YEAR;
+ if (tm_year_from_base >= time_constants::EPOCH_YEAR) {
+ total_days +=
+ time_utils::get_num_of_leap_years_before(tm_year_from_base - 1) -
+ time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR);
+ } else if (tm_year_from_base >= 1) {
+ total_days -=
+ time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR) -
+ time_utils::get_num_of_leap_years_before(tm_year_from_base - 1);
+ } else {
+ // Calculate number of leap years until 0th year.
+ total_days -=
+ time_utils::get_num_of_leap_years_before(time_constants::EPOCH_YEAR) -
+ time_utils::get_num_of_leap_years_before(0);
+ if (tm_year_from_base <= 0) {
+ total_days -= 1; // Subtract 1 for 0th year.
+ // Calculate number of leap years until -1 year
+ if (tm_year_from_base < 0) {
+ total_days -=
+ time_utils::get_num_of_leap_years_before(-tm_year_from_base) -
+ time_utils::get_num_of_leap_years_before(1);
+ }
+ }
+ }
+
+ // TODO: https://github.com/llvm/llvm-project/issues/121962
+ // Need to handle timezone and update of tm_isdst.
+ int64_t seconds = tm_out->tm_sec +
+ tm_out->tm_min * time_constants::SECONDS_PER_MIN +
+ tm_out->tm_hour * time_constants::SECONDS_PER_HOUR +
+ total_days * time_constants::SECONDS_PER_DAY;
+ return seconds;
+}
+
static int64_t computeRemainingYears(int64_t daysPerYears,
int64_t quotientYears,
int64_t *remainingDays) {
diff --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index 5e0a692d4db0486..f8521b0e8ec33cd 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -12,6 +12,8 @@
#include "hdr/types/size_t.h"
#include "hdr/types/struct_tm.h"
#include "hdr/types/time_t.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/CPP/string_view.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/errno/libc_errno.h"
@@ -22,6 +24,10 @@
namespace LIBC_NAMESPACE_DECL {
namespace time_utils {
+// calculates the seconds from the epoch for tm_in. Does not update the struct,
+// you must call update_from_seconds for that.
+int64_t mktime_internal(const struct tm *tm_out);
+
// Update the "tm" structure's year, month, etc. members from seconds.
// "total_seconds" is the number of seconds since January 1st, 1970.
extern int64_t update_from_seconds(int64_t total_seconds, struct tm *tm);
@@ -61,6 +67,7 @@ LIBC_INLINE char *asctime(const struct tm *timeptr, char *buffer,
}
// TODO(michaelr): move this to use the strftime machinery
+ // equivalent to strftime(buffer, bufferLength, "%a %b %T %Y\n", timeptr)
int written_size = __builtin_snprintf(
buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
time_constants::WEEK_DAY_NAMES[timeptr->tm_wday].data(),
@@ -94,6 +101,255 @@ LIBC_INLINE struct tm *localtime(const time_t *t_ptr) {
return time_utils::gmtime_internal(t_ptr, &result);
}
+// Returns number of years from (1, year).
+LIBC_INLINE constexpr int64_t get_num_of_leap_years_before(int64_t year) {
+ return (year / 4) - (year / 100) + (year / 400);
+}
+
+// Returns True if year is a leap year.
+LIBC_INLINE constexpr bool is_leap_year(const int64_t year) {
+ return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
+}
+
+LIBC_INLINE constexpr int get_days_in_year(const int year) {
+ return is_leap_year(year) ? time_constants::DAYS_PER_LEAP_YEAR
+ : time_constants::DAYS_PER_NON_LEAP_YEAR;
+}
+
+// This is a helper class that takes a struct tm and lets you inspect its
+// values. Where relevant, results are bounds checked and returned as optionals.
+// This class does not, however, do data normalization except where necessary.
+// It will faithfully return a date of 9999-99-99, even though that makes no
+// sense.
+class TMReader final {
+ const tm *timeptr;
+
+public:
+ LIBC_INLINE constexpr TMReader(const tm *tmptr) : timeptr(tmptr) { ; }
+
+ // Strings
+ LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+ get_weekday_short_name() const {
+ if (timeptr->tm_wday >= 0 &&
+ timeptr->tm_wday < time_constants::DAYS_PER_WEEK)
+ return time_constants::WEEK_DAY_NAMES[timeptr->tm_wday];
+
+ return cpp::nullopt;
+ }
+
+ LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+ get_weekday_full_name() const {
+ if (timeptr->tm_wday >= 0 &&
+ timeptr->tm_wday < time_constants::DAYS_PER_WEEK)
+ return time_constants::WEEK_DAY_FULL_NAMES[timeptr->tm_wday];
+
+ return cpp::nullopt;
+ }
+
+ LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+ get_month_short_name() const {
+ if (timeptr->tm_mon >= 0 &&
+ timeptr->tm_mon < time_constants::MONTHS_PER_YEAR)
+ return time_constants::MONTH_NAMES[timeptr->tm_mon];
+
+ return cpp::nullopt;
+ }
+
+ LIBC_INLINE constexpr cpp::optional<cpp::string_view>
+ get_month_full_name() const {
+ if (timeptr->tm_mon >= 0 &&
+ timeptr->tm_mon < time_constants::MONTHS_PER_YEAR)
+ return time_constants::MONTH_FULL_NAMES[timeptr->tm_mon];
+
+ return cpp::nullopt;
+ }
+
+ LIBC_INLINE constexpr cpp::string_view get_am_pm() const {
+ if (timeptr->tm_hour < 12)
+ return "AM";
+ return "PM";
+ }
+
+ LIBC_INLINE constexpr cpp::string_view get_timezone_name() const {
+ // TODO: timezone support
+ return "UTC";
+ }
+
+ // Numbers
+ LIBC_INLINE constexpr int get_sec() const { return timeptr->tm_sec; }
+ LIBC_INLINE constexpr int get_min() const { return timeptr->tm_min; }
+ LIBC_INLINE constexpr int get_hour() const { return timeptr->tm_hour; }
+ LIBC_INLINE constexpr int get_mday() const { return timeptr->tm_mday; }
+ LIBC_INLINE constexpr int get_mon() const { return timeptr->tm_mon; }
+ LIBC_INLINE constexpr int get_yday() const { return timeptr->tm_yday; }
+ LIBC_INLINE constexpr int get_wday() const { return timeptr->tm_wday; }
+ LIBC_INLINE constexpr int get_isdst() const { return timeptr->tm_isdst; }
+
+ // returns the year, counting from 1900
+ LIBC_INLINE constexpr int get_year_raw() const { return timeptr->tm_year; }
+ // returns the year, counting from 0
+ LIBC_INLINE constexpr int get_year() const {
+ return timeptr->tm_year + time_constants::TIME_YEAR_BASE;
+ }
+
+ LIBC_INLINE constexpr int is_leap_year() const {
+ return time_utils::is_leap_year(get_year());
+ }
+
+ LIBC_INLINE constexpr int get_iso_wday() const {
+ // ISO uses a week that starts on Monday, but struct tm starts its week on
+ // Sunday. This function normalizes the weekday so that it always returns a
+ // value 0-6
+ const int NORMALIZED_WDAY =
+ timeptr->tm_wday % time_constants::DAYS_PER_WEEK;
+ return (NORMALIZED_WDAY + (time_constants::DAYS_PER_WEEK - 1)) % 7;
+ }
+
+ // returns the week of the current year, with weeks starting on start_day.
+ LIBC_INLINE constexpr int get_week(time_constants::WeekDay start_day) const {
+ // The most recent start_day. The rest of the days into the current week
+ // don't count, so ignore them.
+ // Also add 7 to handle start_day > tm_wday
+ const int start_of_cur_week =
+ timeptr->tm_yday -
+ ((timeptr->tm_wday + time_constants::DAYS_PER_WEEK - start_day) %
+ time_constants::DAYS_PER_WEEK);
+
+ // Add 1 since the first week may start with day 0
+ const int ceil_weeks_since_start =
+ ((start_of_cur_week + 1) + (time_constants::DAYS_PER_WEEK - 1)) /
+ time_constants::DAYS_PER_WEEK;
+ return ceil_weeks_since_start;
+ }
+
+ LIBC_INLINE constexpr int get_iso_week() const {
+ const time_constants::WeekDay start_day = time_constants::MONDAY;
+
+ // The most recent start_day. The rest of the days into the current week
+ // don't count, so ignore them.
+ // Also add 7 to handle start_day > tm_wday
+ const int start_of_cur_week =
+ timeptr->tm_yday -
+ ((timeptr->tm_wday + time_constants::DAYS_PER_WEEK - start_day) %
+ time_constants::DAYS_PER_WEEK);
+
+ // if the week starts in the previous year, and also if the 4th of this year
+ // is not in this week.
+ if (start_of_cur_week < -3) {
+ const int days_into_prev_year =
+ get_days_in_year(get_year() - 1) + start_of_cur_week;
+ // Each year has at least 52 weeks, but a year's last week will be 53 if
+ // its first week starts in the previous year and its last week ends
+ // in the next year. We know get_year() - 1 must extend into get_year(),
+ // so here we check if it also extended into get_year() - 2 and add 1 week
+ // if it does.
+ return 52 + ((days_into_prev_year % time_constants::DAYS_PER_WEEK) >
+ time_constants::ISO_FIRST_DAY_OF_YEAR
+ ? 1
+ : 0);
+ }
+
+ // subtract 1 to account for yday being 0 indexed
+ const int days_until_end_of_year =
+ get_days_in_year(get_year()) - start_of_cur_week - 1;
+
+ // if there are less than 3 days from the start of this week to the end of
+ // the year, then there must be 4 days in this week in the next year, which
+ // means that this week is the first week of that year.
+ if (days_until_end_of_year < 3)
+ return 1;
+
+ // else just calculate the current week like normal.
+ const int ceil_weeks_since_start =
+ ((start_of_cur_week + 1) + (time_constants::DAYS_PER_WEEK - 1)) /
+ time_constants::DAYS_PER_WEEK;
+
+ // add 1 if this year's first week starts in the previous year.
+ return ceil_weeks_since_start +
+ (((start_of_cur_week + time_constants::DAYS_PER_WEEK) %
+ time_constants::DAYS_PER_WEEK) >
+ time_constants::ISO_FIRST_DAY_OF_YEAR
+ ? 1
+ : 0);
+ }
+
+ LIBC_INLINE constexpr int get_iso_year() const {
+ const int BASE_YEAR = get_year();
+ // The ISO year is the same as a standard year for all dates after the start
+ // of the first week and before the last week. Since the first ISO week of a
+ // year starts on the 4th, anything after that is in this year.
+ if (timeptr->tm_yday >= time_constants::ISO_FIRST_DAY_OF_YEAR &&
+ timeptr->tm_yday < time_constants::DAYS_PER_NON_LEAP_YEAR -
+ time_constants::DAYS_PER_WEEK)
+ return BASE_YEAR;
+
+ const int ISO_WDAY = get_iso_wday();
+ // The first week of the ISO year is defined as the week containing the
+ // 4th day of January.
+
+ // first week
+ if (timeptr->tm_yday < time_constants::ISO_FIRST_DAY_OF_YEAR) {
+ /*
+ If jan 4 is in this week, then we're in BASE_YEAR, else we're in the
+ previous year. The formula's been rearranged so here's the derivation:
+
+ +--------+-- days until jan 4
+ | |
+ wday + (4 - yday) < 7
+ | |
+ +---------------+-- weekday of jan 4
+
+ rearranged to get all the constants on one side:
+
+ wday - yday < 7 - 4
+ */
+ return (ISO_WDAY - timeptr->tm_yday <
+ time_constants::DAYS_PER_WEEK -
+ time_constants::ISO_FIRST_DAY_OF_YEAR)
+ ? BASE_YEAR
+ : BASE_YEAR - 1;
+ }
+
+ // last week
+ const int DAYS_LEFT_IN_YEAR =
+ get_days_in_year(get_year()) - timeptr->tm_yday;
+ /*
+ Similar to above, we're checking if jan 4 (of next year) is in this week. If
+ it is, this is in the next year. Note that this also handles the case of
+ yday > days in year gracefully.
+
+ +------------------+-- days until jan 4 (of next year)
+ | |
+ wday + (4 + remaining days) < 7
+ | |
+ +-------------------------+-- weekday of jan 4
+
+ rearranging we get:
+
+ wday + remaining days < 7 - 4
+ */
+
+ return (ISO_WDAY + DAYS_LEFT_IN_YEAR <
+ time_constants::DAYS_PER_WEEK -
+ time_constants::ISO_FIRST_DAY_OF_YEAR)
+ ? BASE_YEAR + 1
+ : BASE_YEAR;
+ }
+
+ LIBC_INLINE constexpr time_t get_epoch() const {
+ return mktime_internal(timeptr);
+ }
+
+ // returns the timezone offset in microwave time:
+ // return (hours * 100) + minutes;
+ // This means that a shift of -4:30 is returned as -430, simplifying
+ // conversion.
+ LIBC_INLINE constexpr int get_timezone_offset() const {
+ // TODO: timezone support
+ return 0;
+ }
+};
+
} // namespace time_utils
} // namespace LIBC_NAMESPACE_DECL
More information about the libc-commits
mailing list