Browse Source

Merge pull request #106759 from aaronp64/time_year_calc

Update `time.cpp` year/unix time conversions to be constant time
Thaddeus Crews 3 months ago
parent
commit
f196a7128c
1 changed files with 32 additions and 18 deletions
  1. 32 18
      core/os/time.cpp

+ 32 - 18
core/os/time.cpp

@@ -37,6 +37,33 @@
 #define IS_LEAP_YEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
 #define YEAR_SIZE(year) (IS_LEAP_YEAR(year) ? 366 : 365)
 
+static constexpr int64_t total_leap_days(int64_t p_year) {
+	if (p_year > 0) {
+		--p_year;
+		return 1 + (p_year / 4 - p_year / 100 + p_year / 400);
+	}
+
+	return p_year / 4 - p_year / 100 + p_year / 400;
+}
+
+static constexpr int64_t year_to_days(int64_t p_year) {
+	return p_year * 365 + total_leap_days(p_year);
+}
+
+static constexpr int64_t days_to_year(int64_t p_days) {
+	int64_t year = 400 * p_days / year_to_days(400);
+	if (year < 0) {
+		--year;
+	}
+	if (year_to_days(year) > p_days) {
+		--year;
+	}
+	if (year_to_days(year + 1) <= p_days) {
+		++year;
+	}
+	return year;
+}
+
 #define YEAR_KEY "year"
 #define MONTH_KEY "month"
 #define DAY_KEY "day"
@@ -72,16 +99,10 @@ static const uint8_t MONTH_DAYS_TABLE[2][12] = {
 	int64_t day_number = Math::floor(p_unix_time_val / (double)SECONDS_PER_DAY);            \
 	{                                                                                       \
 		int64_t day_number_copy = day_number;                                               \
-		year = UNIX_EPOCH_YEAR_AD;                                                          \
+		day_number_copy += year_to_days(UNIX_EPOCH_YEAR_AD);                                \
+		year = days_to_year(day_number_copy);                                               \
+		day_number_copy -= year_to_days(year);                                              \
 		uint8_t month_zero_index = 0;                                                       \
-		while (day_number_copy >= YEAR_SIZE(year)) {                                        \
-			day_number_copy -= YEAR_SIZE(year);                                             \
-			year++;                                                                         \
-		}                                                                                   \
-		while (day_number_copy < 0) {                                                       \
-			year--;                                                                         \
-			day_number_copy += YEAR_SIZE(year);                                             \
-		}                                                                                   \
 		/* After the above, day_number now represents the day of the year (0-index). */     \
 		while (day_number_copy >= MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month_zero_index]) { \
 			day_number_copy -= MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month_zero_index];      \
@@ -116,15 +137,8 @@ static const uint8_t MONTH_DAYS_TABLE[2][12] = {
 		day_number += MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][i];                      \
 	}                                                                               \
 	/* Add the days in the years to day_number. */                                  \
-	if (year >= UNIX_EPOCH_YEAR_AD) {                                               \
-		for (int64_t iyear = UNIX_EPOCH_YEAR_AD; iyear < year; iyear++) {           \
-			day_number += YEAR_SIZE(iyear);                                         \
-		}                                                                           \
-	} else {                                                                        \
-		for (int64_t iyear = UNIX_EPOCH_YEAR_AD - 1; iyear >= year; iyear--) {      \
-			day_number -= YEAR_SIZE(iyear);                                         \
-		}                                                                           \
-	}
+	day_number += year_to_days(year);                                               \
+	day_number -= year_to_days(UNIX_EPOCH_YEAR_AD);
 
 #define PARSE_ISO8601_STRING(ret)                                                             \
 	int64_t year = UNIX_EPOCH_YEAR_AD;                                                        \