1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/utils/port/Clocks.h"
8 
9 #include "td/utils/port/platform.h"
10 
11 #include <chrono>
12 #include <ctime>
13 
14 #if TD_PORT_POSIX
15 #include <time.h>
16 #endif
17 
18 namespace td {
19 
monotonic()20 double Clocks::monotonic() {
21 #if TD_PORT_POSIX
22   // use system specific functions, because std::chrono::steady_clock is steady only under Windows
23 
24 #ifdef CLOCK_BOOTTIME
25   {
26     static bool skip = [] {
27       struct timespec spec;
28       return clock_gettime(CLOCK_BOOTTIME, &spec) != 0;
29     }();
30     struct timespec spec;
31     if (!skip && clock_gettime(CLOCK_BOOTTIME, &spec) == 0) {
32       return static_cast<double>(spec.tv_nsec) * 1e-9 + static_cast<double>(spec.tv_sec);
33     }
34   }
35 #endif
36 #ifdef CLOCK_MONOTONIC_RAW
37   {
38     static bool skip = [] {
39       struct timespec spec;
40       return clock_gettime(CLOCK_MONOTONIC_RAW, &spec) != 0;
41     }();
42     struct timespec spec;
43     if (!skip && clock_gettime(CLOCK_MONOTONIC_RAW, &spec) == 0) {
44       return static_cast<double>(spec.tv_nsec) * 1e-9 + static_cast<double>(spec.tv_sec);
45     }
46   }
47 #endif
48 
49 #endif
50 
51   auto duration = std::chrono::steady_clock::now().time_since_epoch();
52   return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
53 }
54 
system()55 double Clocks::system() {
56   auto duration = std::chrono::system_clock::now().time_since_epoch();
57   return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
58 }
59 
tz_offset()60 int Clocks::tz_offset() {
61   // not thread-safe on POSIX, so calculate the offset only once
62   static int offset = [] {
63     auto now = std::time(nullptr);
64 
65     auto time_ptr = std::localtime(&now);
66     if (time_ptr == nullptr) {
67       return 0;
68     }
69     auto local_time = *time_ptr;
70 
71     time_ptr = std::gmtime(&now);
72     if (time_ptr == nullptr) {
73       return 0;
74     }
75     auto utc_time = *time_ptr;
76 
77     int minute_offset = local_time.tm_min - utc_time.tm_min;
78     int hour_offset = local_time.tm_hour - utc_time.tm_hour;
79     int day_offset = local_time.tm_mday - utc_time.tm_mday;
80     if (day_offset >= 20) {
81       day_offset = -1;
82     } else if (day_offset <= -20) {
83       day_offset = 1;
84     }
85     int sec_offset = day_offset * 86400 + hour_offset * 3600 + minute_offset * 60;
86     if (sec_offset >= 15 * 3600 || sec_offset <= -15 * 3600) {
87       return 0;
88     }
89     return sec_offset / 900 * 900;  // round to 900 just in case
90   }();
91   return offset;
92 }
93 
94 namespace detail {
95 int init_tz_offset_private = Clocks::tz_offset();
96 }  // namespace detail
97 
98 }  // namespace td
99