[libc-commits] [libc] [libc] create TimeReader to look at a struct tm (PR #126138)

Nick Desaulniers via libc-commits libc-commits at lists.llvm.org
Thu Feb 6 15:10:45 PST 2025


================
@@ -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 {
----------------
nickdesaulniers wrote:

Does adding `using time_constants;` in each of these methods help make the equations slightly more readable?

https://github.com/llvm/llvm-project/pull/126138


More information about the libc-commits mailing list