systime.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #include "systime.h"
  2. #include <util/system/yassert.h>
  3. #include <util/system/defaults.h>
  4. #ifdef _win_
  5. namespace {
  6. // Number of 100 nanosecond units from 1/1/1601 to 1/1/1970
  7. constexpr ui64 NUMBER_OF_100_NANO_BETWEEN_1601_1970 =
  8. ULL(116444736000000000);
  9. constexpr ui64 NUMBER_OF_100_NANO_IN_SECOND = ULL(10000000);
  10. union TFTUnion {
  11. ui64 FTScalar;
  12. FILETIME FTStruct;
  13. };
  14. } // namespace
  15. void FileTimeToTimeval(const FILETIME* ft, timeval* tv) {
  16. Y_ASSERT(ft);
  17. Y_ASSERT(tv);
  18. TFTUnion ntTime;
  19. ntTime.FTStruct = *ft;
  20. ntTime.FTScalar -= NUMBER_OF_100_NANO_BETWEEN_1601_1970;
  21. tv->tv_sec =
  22. static_cast<long>(ntTime.FTScalar / NUMBER_OF_100_NANO_IN_SECOND);
  23. tv->tv_usec = static_cast<long>(
  24. (ntTime.FTScalar % NUMBER_OF_100_NANO_IN_SECOND) / LL(10));
  25. }
  26. void FileTimeToTimespec(const FILETIME& ft, struct timespec* ts) {
  27. Y_ASSERT(ts);
  28. TFTUnion ntTime;
  29. ntTime.FTStruct = ft;
  30. ntTime.FTScalar -= NUMBER_OF_100_NANO_BETWEEN_1601_1970;
  31. ts->tv_sec =
  32. static_cast<time_t>(ntTime.FTScalar / NUMBER_OF_100_NANO_IN_SECOND);
  33. ts->tv_nsec = static_cast<long>(
  34. (ntTime.FTScalar % NUMBER_OF_100_NANO_IN_SECOND) * LL(100));
  35. }
  36. int gettimeofday(timeval* tp, void*) {
  37. FILETIME ft;
  38. GetSystemTimeAsFileTime(&ft);
  39. FileTimeToTimeval(&ft, tp);
  40. return 0;
  41. }
  42. tm* localtime_r(const time_t* clock, tm* result) {
  43. tzset();
  44. tm* res = localtime(clock);
  45. if (res) {
  46. memcpy(result, res, sizeof(tm));
  47. return result;
  48. }
  49. return 0;
  50. }
  51. tm* gmtime_r(const time_t* clock, tm* result) {
  52. return gmtime_s(result, clock) == 0 ? result : 0;
  53. }
  54. char* ctime_r(const time_t* clock, char* buf) {
  55. char* res = ctime(clock);
  56. if (res) {
  57. memcpy(buf, res, 26);
  58. return buf;
  59. }
  60. return 0;
  61. }
  62. #endif /* _win_ */
  63. namespace {
  64. constexpr int STRUCT_TM_BASE_YEAR = 1900;
  65. constexpr int UNIX_TIME_BASE_YEAR = 1970;
  66. constexpr ui64 SECONDS_PER_DAY = (24L * 60L * 60L);
  67. constexpr bool IsLeapYear(int year) {
  68. if (year % 4 != 0) {
  69. return false;
  70. }
  71. if (year % 100 != 0) {
  72. return true;
  73. }
  74. return year % 400 == 0;
  75. }
  76. constexpr ui16 DAYS_IN_YEAR = 365;
  77. constexpr ui16 DAYS_IN_LEAP_YEAR = 366;
  78. constexpr ui16 YearSize(int year) {
  79. return IsLeapYear(year) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR;
  80. }
  81. constexpr ui32 FOUR_CENTURY_YEARS = 400;
  82. constexpr ui32 LeapYearCount(ui32 years) {
  83. return years / 4 - years / 100 + years / 400;
  84. }
  85. constexpr ui32 FOUR_CENTURY_DAYS = FOUR_CENTURY_YEARS * DAYS_IN_YEAR + LeapYearCount(FOUR_CENTURY_YEARS);
  86. constexpr int FindYearWithin4Centuries(ui32& dayno) {
  87. Y_ASSERT(dayno < FOUR_CENTURY_DAYS);
  88. ui32 years = dayno / DAYS_IN_YEAR;
  89. const ui32 diff = years * DAYS_IN_YEAR + LeapYearCount(years);
  90. if (diff <= dayno) {
  91. dayno -= diff;
  92. } else {
  93. dayno -= diff - YearSize(static_cast<int>(years));
  94. --years;
  95. }
  96. return static_cast<int>(years);
  97. }
  98. constexpr ui16 MONTH_TO_DAYS[12] = {
  99. 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  100. constexpr ui16 MONTH_TO_DAYS_LEAP[12] = {
  101. 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
  102. struct TMonth32LUT {
  103. ui8 LastMonthDay32[12];
  104. ui8 FirstMonthDay32[12];
  105. };
  106. constexpr TMonth32LUT COMMON_YEAR = {
  107. .LastMonthDay32 = {31, 27, 26, 24, 23, 21, 20, 19, 17, 16, 14, 13},
  108. .FirstMonthDay32 = {0, 1, 5, 6, 8, 9, 11, 12, 13, 15, 16, 18},
  109. };
  110. constexpr int DayOfYearToMonth(ui32& yearDay, const bool leapYear) {
  111. Y_ASSERT(yearDay < DAYS_IN_YEAR + leapYear);
  112. if (leapYear) {
  113. if (yearDay > 59) {
  114. --yearDay;
  115. } else if (yearDay == 59) {
  116. // February, 29th
  117. yearDay = 28;
  118. return 1;
  119. }
  120. }
  121. const int approxMonth = static_cast<int>(yearDay / 32);
  122. const int approxMDay = static_cast<int>(yearDay % 32);
  123. const int dayThreshold = COMMON_YEAR.LastMonthDay32[approxMonth];
  124. const int currentMonthMDayOffset = COMMON_YEAR.FirstMonthDay32[approxMonth];
  125. const bool nextMonth = (approxMDay >= dayThreshold);
  126. const int dayCorrection = nextMonth ? -dayThreshold : currentMonthMDayOffset;
  127. yearDay = approxMDay + dayCorrection;
  128. const int month = approxMonth + nextMonth;
  129. return month;
  130. }
  131. class TDayNoToYearLookupTable {
  132. static constexpr int TableSize = 128;
  133. // lookup table for years in [StartYear, StartYear + TableSize] range
  134. ui16 DaysSinceEpoch[TableSize] = {};
  135. public:
  136. static constexpr int StartYear = 1970;
  137. static constexpr int StartDays = (StartYear - UNIX_TIME_BASE_YEAR) * DAYS_IN_YEAR + LeapYearCount(StartYear - 1) - LeapYearCount(UNIX_TIME_BASE_YEAR - 1);
  138. static constexpr i64 MinTimestamp = StartDays * static_cast<i64>(SECONDS_PER_DAY);
  139. static constexpr i64 MaxTimestamp = MinTimestamp + static_cast<i64>(TableSize) * DAYS_IN_LEAP_YEAR * SECONDS_PER_DAY - 1;
  140. constexpr TDayNoToYearLookupTable() {
  141. ui16 daysAccumulated = 0;
  142. for (int year = StartYear; year < StartYear + TableSize; ++year) {
  143. daysAccumulated += YearSize(year);
  144. DaysSinceEpoch[year - StartYear] = daysAccumulated;
  145. }
  146. }
  147. // lookup year by days since epoch, decrement day counter to the corresponding amount of days.
  148. // The method returns the last year in the table, if year is too big
  149. int FindYear(ui32& days) const {
  150. const ui32 yearIndex = days / DAYS_IN_LEAP_YEAR;
  151. // we can miss by at most 1 year
  152. Y_ASSERT(yearIndex < TableSize);
  153. if (const auto diff = DaysSinceEpoch[yearIndex]; diff <= days) {
  154. days -= diff;
  155. return static_cast<int>(yearIndex + StartYear + 1);
  156. }
  157. if (yearIndex > 0) {
  158. days -= DaysSinceEpoch[yearIndex - 1];
  159. }
  160. return static_cast<int>(yearIndex + StartYear);
  161. }
  162. };
  163. constexpr TDayNoToYearLookupTable DAYS_TO_YEAR_LOOKUP;
  164. } // namespace
  165. //! Inverse of gmtime: converts struct tm to time_t, assuming the data
  166. //! in tm is UTC rather than local timezone. This implementation
  167. //! returns the number of seconds since 1970-01-01, converted to time_t.
  168. //! @note this code adopted from
  169. //! http://osdir.com/ml/web.wget.patches/2005-07/msg00010.html
  170. //! Subject: A more robust timegm - msg#00010
  171. time_t TimeGM(const struct tm* t) {
  172. // Only handles years after 1970
  173. if (Y_UNLIKELY(t->tm_year < 70)) {
  174. return (time_t)-1;
  175. }
  176. int days = 365 * (t->tm_year - 70);
  177. // Take into account the leap days between 1970 and YEAR-1
  178. days += (t->tm_year - 1 - 68) / 4 - ((t->tm_year - 1) / 100) + ((t->tm_year - 1 + 300) / 400);
  179. if (Y_UNLIKELY(t->tm_mon < 0 || t->tm_mon >= 12)) {
  180. return (time_t)-1;
  181. }
  182. if (IsLeapYear(1900 + t->tm_year)) {
  183. days += MONTH_TO_DAYS_LEAP[t->tm_mon];
  184. } else {
  185. days += MONTH_TO_DAYS[t->tm_mon];
  186. }
  187. days += t->tm_mday - 1;
  188. unsigned long secs = days * 86400ul + t->tm_hour * 3600 + t->tm_min * 60 + t->tm_sec;
  189. return (time_t)secs;
  190. }
  191. struct tm* GmTimeR(const time_t* timer, struct tm* tmbuf) {
  192. i64 time = static_cast<i64>(*timer);
  193. tm* resut = tmbuf;
  194. int dayClock;
  195. ui32 daysRemaining;
  196. bool isLeapYear;
  197. if (time >= TDayNoToYearLookupTable::MinTimestamp && time <= TDayNoToYearLookupTable::MaxTimestamp)
  198. {
  199. dayClock = static_cast<int>(time % SECONDS_PER_DAY);
  200. daysRemaining = time / SECONDS_PER_DAY;
  201. tmbuf->tm_wday = static_cast<int>((daysRemaining + 4) % 7); // Day 0 was a thursday
  202. daysRemaining -= TDayNoToYearLookupTable::StartDays;
  203. const int year = DAYS_TO_YEAR_LOOKUP.FindYear(daysRemaining);
  204. isLeapYear = IsLeapYear(year);
  205. tmbuf->tm_year = year - STRUCT_TM_BASE_YEAR;
  206. } else {
  207. i64 year = UNIX_TIME_BASE_YEAR;
  208. if (Y_UNLIKELY(time < 0)) {
  209. const ui64 shift = (ui64)(-time - 1) / (static_cast<ui64>(FOUR_CENTURY_DAYS) * SECONDS_PER_DAY) + 1;
  210. time += static_cast<i64>(shift * FOUR_CENTURY_DAYS * SECONDS_PER_DAY);
  211. year -= static_cast<i64>(shift * FOUR_CENTURY_YEARS);
  212. }
  213. dayClock = static_cast<int>(time % SECONDS_PER_DAY);
  214. ui64 dayNo = (ui64)time / SECONDS_PER_DAY;
  215. tmbuf->tm_wday = (dayNo + 4) % 7; // Day 0 was a thursday
  216. if (int shiftYears = (year - 1) % FOUR_CENTURY_YEARS; shiftYears != 0) {
  217. if (shiftYears < 0) {
  218. shiftYears += FOUR_CENTURY_YEARS;
  219. }
  220. year -= shiftYears;
  221. dayNo += shiftYears * DAYS_IN_YEAR + LeapYearCount(shiftYears);
  222. }
  223. if (Y_UNLIKELY(dayNo >= FOUR_CENTURY_DAYS)) {
  224. year += FOUR_CENTURY_YEARS * (dayNo / FOUR_CENTURY_DAYS);
  225. dayNo = dayNo % FOUR_CENTURY_DAYS;
  226. }
  227. daysRemaining = dayNo;
  228. const int yearDiff = FindYearWithin4Centuries(daysRemaining);
  229. year += yearDiff;
  230. isLeapYear = IsLeapYear(yearDiff + 1);
  231. tmbuf->tm_year = static_cast<int>(year - STRUCT_TM_BASE_YEAR);
  232. // check year overflow
  233. if (Y_UNLIKELY(year - STRUCT_TM_BASE_YEAR != tmbuf->tm_year)) {
  234. resut = nullptr;
  235. }
  236. }
  237. tmbuf->tm_sec = dayClock % 60;
  238. tmbuf->tm_min = (dayClock % 3600) / 60;
  239. tmbuf->tm_hour = dayClock / 3600;
  240. tmbuf->tm_yday = static_cast<int>(daysRemaining);
  241. tmbuf->tm_mon = DayOfYearToMonth(daysRemaining, isLeapYear);
  242. tmbuf->tm_mday = static_cast<int>(daysRemaining + 1);
  243. tmbuf->tm_isdst = 0;
  244. #ifndef _win_
  245. tmbuf->tm_gmtoff = 0;
  246. tmbuf->tm_zone = (char*)"UTC";
  247. #endif
  248. return resut;
  249. }
  250. TString CTimeR(const time_t* timer) {
  251. char sTime[32];
  252. sTime[0] = 0;
  253. ctime_r(timer, &sTime[0]);
  254. return sTime;
  255. }