@@ -75,7 +75,7 @@ char* ctime_r(const time_t* clock, char* buf) {
75
75
namespace {
76
76
constexpr int STRUCT_TM_BASE_YEAR = 1900 ;
77
77
constexpr int UNIX_TIME_BASE_YEAR = 1970 ;
78
- constexpr long SECONDS_PER_DAY = (24L * 60L * 60L );
78
+ constexpr ui64 SECONDS_PER_DAY = (24L * 60L * 60L );
79
79
80
80
constexpr bool IsLeapYear (int year) {
81
81
if (year % 4 != 0 ) {
@@ -94,7 +94,29 @@ namespace {
94
94
return IsLeapYear (year) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR;
95
95
}
96
96
97
- constexpr ui64 FOUR_CENTURIES = (400 * 365 + 100 - 3 );
97
+ constexpr ui32 FOUR_CENTURY_YEARS = 400 ;
98
+
99
+ constexpr ui32 LeapYearCount (ui32 years) {
100
+ return years / 4 - years / 100 + years / 400 ;
101
+ }
102
+
103
+ constexpr ui32 FOUR_CENTURY_DAYS = FOUR_CENTURY_YEARS * DAYS_IN_YEAR + LeapYearCount(FOUR_CENTURY_YEARS);
104
+
105
+ constexpr int FindYearWithin4Centuries (ui32& dayno) {
106
+ Y_ASSERT (dayno < FOUR_CENTURY_DAYS);
107
+ ui32 years = dayno / DAYS_IN_YEAR;
108
+
109
+ const ui32 diff = years * DAYS_IN_YEAR + LeapYearCount (years);
110
+
111
+ if (diff <= dayno) {
112
+ dayno -= diff;
113
+ } else {
114
+ dayno -= diff - YearSize (static_cast <int >(years));
115
+ --years;
116
+ }
117
+
118
+ return static_cast <int >(years);
119
+ }
98
120
99
121
constexpr ui16 MONTH_TO_DAYS[12 ] = {
100
122
0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 };
@@ -103,73 +125,73 @@ namespace {
103
125
0 , 31 , 60 , 91 , 121 , 152 , 182 , 213 , 244 , 274 , 305 , 335 };
104
126
105
127
struct TMonth32LUT {
106
- char LastMonthDay32[12 ];
107
- char FirstMonthDay32[12 ];
128
+ ui8 LastMonthDay32[12 ];
129
+ ui8 FirstMonthDay32[12 ];
108
130
};
109
131
110
- constexpr TMonth32LUT MONTH_32_LUT[2 ] = {
111
- {
112
- // common year
113
- .LastMonthDay32 = {31 , 27 , 26 , 24 , 23 , 21 , 20 , 19 , 17 , 16 , 14 , 13 },
114
- .FirstMonthDay32 = {0 , 1 , 5 , 6 , 8 , 9 , 11 , 12 , 13 , 15 , 16 , 18 },
115
- },
116
- {
117
- // leap year
118
- .LastMonthDay32 = {31 , 28 , 27 , 25 , 24 , 22 , 21 , 20 , 18 , 17 , 15 , 14 },
119
- .FirstMonthDay32 = {0 , 1 , 4 , 5 , 7 , 8 , 10 , 11 , 12 , 14 , 15 , 17 },
120
- },
132
+ constexpr TMonth32LUT COMMON_YEAR = {
133
+ .LastMonthDay32 = {31 , 27 , 26 , 24 , 23 , 21 , 20 , 19 , 17 , 16 , 14 , 13 },
134
+ .FirstMonthDay32 = {0 , 1 , 5 , 6 , 8 , 9 , 11 , 12 , 13 , 15 , 16 , 18 },
121
135
};
122
136
123
- constexpr int DayOfYearToMonth (ui64& yearDay, bool leapYear) {
124
- Y_ASSERT (yearDay < 366 );
125
- int approxMonth = yearDay / 32 ;
126
- int approxMDay = yearDay % 32 ;
127
- int dayThreshold = MONTH_32_LUT[leapYear].LastMonthDay32 [approxMonth];
128
- int currentMonthMDayOffset = MONTH_32_LUT[leapYear].FirstMonthDay32 [approxMonth];
129
- bool nextMonth = (approxMDay >= dayThreshold);
130
- int dayCorrection = nextMonth ? -dayThreshold : currentMonthMDayOffset;
131
- int day = approxMDay + dayCorrection;
132
- int month = approxMonth + nextMonth;
133
- yearDay = day;
137
+ constexpr int DayOfYearToMonth (ui32& yearDay, const bool leapYear) {
138
+ Y_ASSERT (yearDay < DAYS_IN_YEAR + leapYear);
139
+ if (leapYear) {
140
+ if (yearDay > 59 ) {
141
+ --yearDay;
142
+ } else if (yearDay == 59 ) {
143
+ // February, 29th
144
+ yearDay = 28 ;
145
+ return 1 ;
146
+ }
147
+ }
148
+ const int approxMonth = static_cast <int >(yearDay / 32 );
149
+ const int approxMDay = static_cast <int >(yearDay % 32 );
150
+ const int dayThreshold = COMMON_YEAR.LastMonthDay32 [approxMonth];
151
+ const int currentMonthMDayOffset = COMMON_YEAR.FirstMonthDay32 [approxMonth];
152
+ const bool nextMonth = (approxMDay >= dayThreshold);
153
+ const int dayCorrection = nextMonth ? -dayThreshold : currentMonthMDayOffset;
154
+ yearDay = approxMDay + dayCorrection;
155
+ const int month = approxMonth + nextMonth;
134
156
return month;
135
157
}
136
158
137
159
class TDayNoToYearLookupTable {
138
160
static constexpr int TableSize = 128 ;
139
- // lookup table for years in [1970, 1970 + 128 = 2098 ] range
161
+ // lookup table for years in [StartYear, StartYear + TableSize ] range
140
162
ui16 DaysSinceEpoch[TableSize] = {};
141
163
142
164
public:
165
+ static constexpr int StartYear = 1970 ;
166
+ static constexpr int StartDays = (StartYear - UNIX_TIME_BASE_YEAR) * DAYS_IN_YEAR + LeapYearCount(StartYear - 1 ) - LeapYearCount(UNIX_TIME_BASE_YEAR - 1 );
167
+ static constexpr i64 MinTimestamp = StartDays * static_cast <i64 >(SECONDS_PER_DAY);
168
+ static constexpr i64 MaxTimestamp = MinTimestamp + static_cast <i64 >(TableSize) * DAYS_IN_LEAP_YEAR * SECONDS_PER_DAY - 1 ;
143
169
constexpr TDayNoToYearLookupTable () {
144
170
ui16 daysAccumulated = 0 ;
145
171
146
- for (int year = UNIX_TIME_BASE_YEAR ; year < UNIX_TIME_BASE_YEAR + TableSize; ++year) {
172
+ for (int year = StartYear ; year < StartYear + TableSize; ++year) {
147
173
daysAccumulated += YearSize (year);
148
- DaysSinceEpoch[year - UNIX_TIME_BASE_YEAR ] = daysAccumulated;
174
+ DaysSinceEpoch[year - StartYear ] = daysAccumulated;
149
175
}
150
176
}
151
177
152
178
// lookup year by days since epoch, decrement day counter to the corresponding amount of days.
153
179
// The method returns the last year in the table, if year is too big
154
- int FindYear (ui64& days) const {
155
- if (days >= DaysSinceEpoch[TableSize - 1 ]) {
156
- days -= DaysSinceEpoch[TableSize - 1 ];
157
- return TableSize + UNIX_TIME_BASE_YEAR;
158
- }
159
- const ui64 yearIndex = days / DAYS_IN_LEAP_YEAR;
180
+ int FindYear (ui32& days) const {
181
+ const ui32 yearIndex = days / DAYS_IN_LEAP_YEAR;
160
182
161
183
// we can miss by at most 1 year
162
184
Y_ASSERT (yearIndex < TableSize);
163
185
if (const auto diff = DaysSinceEpoch[yearIndex]; diff <= days) {
164
186
days -= diff;
165
- return static_cast <int >(yearIndex + UNIX_TIME_BASE_YEAR + 1 );
187
+ return static_cast <int >(yearIndex + StartYear + 1 );
166
188
}
167
189
168
190
if (yearIndex > 0 ) {
169
191
days -= DaysSinceEpoch[yearIndex - 1 ];
170
192
}
171
193
172
- return static_cast <int >(yearIndex + UNIX_TIME_BASE_YEAR );
194
+ return static_cast <int >(yearIndex + StartYear );
173
195
}
174
196
};
175
197
@@ -209,55 +231,72 @@ time_t TimeGM(const struct tm* t) {
209
231
210
232
struct tm * GmTimeR (const time_t * timer, struct tm * tmbuf) {
211
233
i64 time = static_cast <i64 >(*timer);
234
+ tm* resut = tmbuf;
235
+ int dayClock;
236
+ ui32 daysRemaining;
237
+ bool isLeapYear;
238
+
239
+ if (time >= TDayNoToYearLookupTable::MinTimestamp && time <= TDayNoToYearLookupTable::MaxTimestamp)
240
+ {
241
+ dayClock = static_cast <int >(time % SECONDS_PER_DAY);
242
+ daysRemaining = time / SECONDS_PER_DAY;
243
+ tmbuf->tm_wday = static_cast <int >((daysRemaining + 4 ) % 7 ); // Day 0 was a thursday
244
+ daysRemaining -= TDayNoToYearLookupTable::StartDays;
245
+ const int year = DAYS_TO_YEAR_LOOKUP.FindYear (daysRemaining);
246
+ isLeapYear = IsLeapYear (year);
247
+ tmbuf->tm_year = year - STRUCT_TM_BASE_YEAR;
248
+ } else {
249
+ i64 year = UNIX_TIME_BASE_YEAR;
212
250
213
- ui64 dayclock, dayno;
214
- int year = UNIX_TIME_BASE_YEAR;
215
-
216
- if (Y_UNLIKELY (time < 0 )) {
217
- ui64 shift = (ui64)(-time - 1 ) / (FOUR_CENTURIES * SECONDS_PER_DAY) + 1 ;
218
- time += shift * (FOUR_CENTURIES * SECONDS_PER_DAY);
219
- year -= shift * 400 ;
220
- }
251
+ if (Y_UNLIKELY (time < 0 )) {
252
+ const ui64 shift = (ui64)(-time - 1 ) / (static_cast <ui64>(FOUR_CENTURY_DAYS) * SECONDS_PER_DAY) + 1 ;
253
+ time += static_cast <i64 >(shift * FOUR_CENTURY_DAYS * SECONDS_PER_DAY);
254
+ year -= static_cast <i64 >(shift * FOUR_CENTURY_YEARS);
255
+ }
221
256
222
- dayclock = (ui64)time % SECONDS_PER_DAY;
223
- dayno = (ui64)time / SECONDS_PER_DAY;
257
+ dayClock = static_cast <int >(time % SECONDS_PER_DAY);
258
+ ui64 dayNo = (ui64)time / SECONDS_PER_DAY;
259
+ tmbuf->tm_wday = (dayNo + 4 ) % 7 ; // Day 0 was a thursday
224
260
225
- if (Y_UNLIKELY (dayno >= FOUR_CENTURIES)) {
226
- year += 400 * (dayno / FOUR_CENTURIES);
227
- dayno = dayno % FOUR_CENTURIES;
228
- }
261
+ if (int shiftYears = (year - 1 ) % FOUR_CENTURY_YEARS; shiftYears != 0 ) {
262
+ if (shiftYears < 0 ) {
263
+ shiftYears += FOUR_CENTURY_YEARS;
264
+ }
265
+ year -= shiftYears;
266
+ dayNo += shiftYears * DAYS_IN_YEAR + LeapYearCount (shiftYears);
267
+ }
229
268
230
- tmbuf-> tm_sec = dayclock % 60 ;
231
- tmbuf-> tm_min = (dayclock % 3600 ) / 60 ;
232
- tmbuf-> tm_hour = dayclock / 3600 ;
233
- tmbuf-> tm_wday = (dayno + 4 ) % 7 ; // Day 0 was a thursday
269
+ if ( Y_UNLIKELY (dayNo >= FOUR_CENTURY_DAYS)) {
270
+ year += FOUR_CENTURY_YEARS * (dayNo / FOUR_CENTURY_DAYS) ;
271
+ dayNo = dayNo % FOUR_CENTURY_DAYS ;
272
+ }
234
273
235
- if (Y_LIKELY (year == UNIX_TIME_BASE_YEAR)) {
236
- year = DAYS_TO_YEAR_LOOKUP.FindYear (dayno);
237
- }
274
+ daysRemaining = dayNo;
275
+ const int yearDiff = FindYearWithin4Centuries (daysRemaining);
276
+ year += yearDiff;
277
+ isLeapYear = IsLeapYear (yearDiff + 1 );
278
+ tmbuf->tm_year = static_cast <int >(year - STRUCT_TM_BASE_YEAR);
238
279
239
- bool isLeapYear = IsLeapYear (year);
240
- for (;;) {
241
- const ui16 yearSize = isLeapYear ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR;
242
- if (dayno < yearSize) {
243
- break ;
280
+ // check year overflow
281
+ if (Y_UNLIKELY (year - STRUCT_TM_BASE_YEAR != tmbuf->tm_year )) {
282
+ resut = nullptr ;
244
283
}
245
- dayno -= yearSize;
246
- ++year;
247
- isLeapYear = IsLeapYear (year);
248
284
}
249
285
250
- tmbuf->tm_year = year - STRUCT_TM_BASE_YEAR;
251
- tmbuf->tm_yday = dayno;
252
- tmbuf->tm_mon = DayOfYearToMonth (dayno, isLeapYear);
253
- tmbuf->tm_mday = dayno + 1 ;
286
+ tmbuf->tm_sec = dayClock % 60 ;
287
+ tmbuf->tm_min = (dayClock % 3600 ) / 60 ;
288
+ tmbuf->tm_hour = dayClock / 3600 ;
289
+
290
+ tmbuf->tm_yday = static_cast <int >(daysRemaining);
291
+ tmbuf->tm_mon = DayOfYearToMonth (daysRemaining, isLeapYear);
292
+ tmbuf->tm_mday = static_cast <int >(daysRemaining + 1 );
254
293
tmbuf->tm_isdst = 0 ;
255
294
#ifndef _win_
256
295
tmbuf->tm_gmtoff = 0 ;
257
296
tmbuf->tm_zone = (char *)" UTC" ;
258
297
#endif
259
298
260
- return tmbuf ;
299
+ return resut ;
261
300
}
262
301
263
302
TString CTimeR (const time_t * timer) {
0 commit comments