/src/serenity/AK/Time.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, the SerenityOS developers. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/Checked.h> |
8 | | #include <AK/Time.h> |
9 | | |
10 | | // Make a reasonable guess as to which timespec/timeval definition to use. |
11 | | // It doesn't really matter, since both are identical. |
12 | | #ifdef KERNEL |
13 | | # include <Kernel/UnixTypes.h> |
14 | | #else |
15 | | # include <sys/time.h> |
16 | | # include <time.h> |
17 | | #endif |
18 | | |
19 | | namespace AK { |
20 | | |
21 | | int days_in_month(int year, unsigned month) |
22 | 0 | { |
23 | 0 | VERIFY(month >= 1 && month <= 12); |
24 | 0 | if (month == 2) |
25 | 0 | return is_leap_year(year) ? 29 : 28; |
26 | | |
27 | 0 | bool is_long_month = (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12); |
28 | 0 | return is_long_month ? 31 : 30; |
29 | 0 | } |
30 | | |
31 | | unsigned day_of_week(int year, unsigned month, int day) |
32 | 0 | { |
33 | 0 | VERIFY(month >= 1 && month <= 12); |
34 | 0 | constexpr Array seek_table = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 }; |
35 | 0 | if (month < 3) |
36 | 0 | --year; |
37 | |
|
38 | 0 | return (year + year / 4 - year / 100 + year / 400 + seek_table[month - 1] + day) % 7; |
39 | 0 | } |
40 | | |
41 | | Duration Duration::from_ticks(clock_t ticks, time_t ticks_per_second) |
42 | 0 | { |
43 | 0 | auto secs = ticks % ticks_per_second; |
44 | |
|
45 | 0 | i32 nsecs = 1'000'000'000 * (ticks - (ticks_per_second * secs)) / ticks_per_second; |
46 | 0 | i32 extra_secs = sane_mod(nsecs, 1'000'000'000); |
47 | 0 | return Duration::from_half_sanitized(secs, extra_secs, nsecs); |
48 | 0 | } |
49 | | |
50 | | Duration Duration::from_timespec(const struct timespec& ts) |
51 | 48 | { |
52 | 48 | i32 nsecs = ts.tv_nsec; |
53 | 48 | i32 extra_secs = sane_mod(nsecs, 1'000'000'000); |
54 | 48 | return Duration::from_half_sanitized(ts.tv_sec, extra_secs, nsecs); |
55 | 48 | } |
56 | | |
57 | | Duration Duration::from_timeval(const struct timeval& tv) |
58 | 0 | { |
59 | 0 | i32 usecs = tv.tv_usec; |
60 | 0 | i32 extra_secs = sane_mod(usecs, 1'000'000); |
61 | 0 | VERIFY(0 <= usecs && usecs < 1'000'000); |
62 | 0 | return Duration::from_half_sanitized(tv.tv_sec, extra_secs, usecs * 1'000); |
63 | 0 | } |
64 | | |
65 | | i64 Duration::to_truncated_seconds() const |
66 | 0 | { |
67 | 0 | VERIFY(m_nanoseconds < 1'000'000'000); |
68 | 0 | if (m_seconds < 0 && m_nanoseconds) { |
69 | | // Since m_seconds is negative, adding 1 can't possibly overflow |
70 | 0 | return m_seconds + 1; |
71 | 0 | } |
72 | 0 | return m_seconds; |
73 | 0 | } |
74 | | |
75 | | i64 Duration::to_truncated_milliseconds() const |
76 | 0 | { |
77 | 0 | VERIFY(m_nanoseconds < 1'000'000'000); |
78 | 0 | Checked<i64> milliseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds); |
79 | 0 | milliseconds *= 1'000; |
80 | 0 | milliseconds += m_nanoseconds / 1'000'000; |
81 | 0 | if (m_seconds < 0) { |
82 | 0 | if (m_nanoseconds % 1'000'000 != 0) { |
83 | | // Does not overflow: milliseconds <= 1'999. |
84 | 0 | milliseconds++; |
85 | 0 | } |
86 | | // We dropped one second previously, put it back in now that we have handled the rounding. |
87 | 0 | milliseconds -= 1'000; |
88 | 0 | } |
89 | 0 | if (!milliseconds.has_overflow()) |
90 | 0 | return milliseconds.value(); |
91 | 0 | return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL; |
92 | 0 | } |
93 | | |
94 | | i64 Duration::to_truncated_microseconds() const |
95 | 0 | { |
96 | 0 | VERIFY(m_nanoseconds < 1'000'000'000); |
97 | 0 | Checked<i64> microseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds); |
98 | 0 | microseconds *= 1'000'000; |
99 | 0 | microseconds += m_nanoseconds / 1'000; |
100 | 0 | if (m_seconds < 0) { |
101 | 0 | if (m_nanoseconds % 1'000 != 0) { |
102 | | // Does not overflow: microseconds <= 1'999'999. |
103 | 0 | microseconds++; |
104 | 0 | } |
105 | | // We dropped one second previously, put it back in now that we have handled the rounding. |
106 | 0 | microseconds -= 1'000'000; |
107 | 0 | } |
108 | 0 | if (!microseconds.has_overflow()) |
109 | 0 | return microseconds.value(); |
110 | 0 | return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL; |
111 | 0 | } |
112 | | |
113 | | timespec Duration::to_timespec() const |
114 | 0 | { |
115 | 0 | VERIFY(m_nanoseconds < 1'000'000'000); |
116 | 0 | return { static_cast<time_t>(m_seconds), static_cast<long>(m_nanoseconds) }; |
117 | 0 | } |
118 | | |
119 | | timeval Duration::to_timeval() const |
120 | 0 | { |
121 | 0 | VERIFY(m_nanoseconds < 1'000'000'000); |
122 | | // This is done because winsock defines tv_sec and tv_usec as long, and Linux64 as long int. |
123 | 0 | using sec_type = decltype(declval<timeval>().tv_sec); |
124 | 0 | using usec_type = decltype(declval<timeval>().tv_usec); |
125 | 0 | return { static_cast<sec_type>(m_seconds), static_cast<usec_type>(m_nanoseconds) / 1000 }; |
126 | 0 | } |
127 | | |
128 | | Duration Duration::from_half_sanitized(i64 seconds, i32 extra_seconds, u32 nanoseconds) |
129 | 48 | { |
130 | 48 | VERIFY(nanoseconds < 1'000'000'000); |
131 | | |
132 | 48 | if ((seconds <= 0 && extra_seconds > 0) || (seconds >= 0 && extra_seconds < 0)) { |
133 | | // Opposite signs mean that we can definitely add them together without fear of overflowing i64: |
134 | 0 | seconds += extra_seconds; |
135 | 0 | extra_seconds = 0; |
136 | 0 | } |
137 | | |
138 | | // Now the only possible way to become invalid is overflowing i64 towards positive infinity: |
139 | 48 | if (Checked<i64>::addition_would_overflow<i64, i64>(seconds, extra_seconds)) { |
140 | 0 | if (seconds < 0) { |
141 | 0 | return Duration::min(); |
142 | 0 | } else { |
143 | 0 | return Duration::max(); |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | 48 | return Duration { seconds + extra_seconds, nanoseconds }; |
148 | 48 | } |
149 | | |
150 | | #ifndef KERNEL |
151 | | namespace { |
152 | | static Duration now_time_from_clock(clockid_t clock_id) |
153 | 48 | { |
154 | 48 | timespec now_spec {}; |
155 | 48 | ::clock_gettime(clock_id, &now_spec); |
156 | 48 | return Duration::from_timespec(now_spec); |
157 | 48 | } |
158 | | } |
159 | | |
160 | | MonotonicTime MonotonicTime::now() |
161 | 48 | { |
162 | 48 | return MonotonicTime { now_time_from_clock(CLOCK_MONOTONIC) }; |
163 | 48 | } |
164 | | |
165 | | MonotonicTime MonotonicTime::now_coarse() |
166 | 0 | { |
167 | 0 | return MonotonicTime { now_time_from_clock(CLOCK_MONOTONIC_COARSE) }; |
168 | 0 | } |
169 | | |
170 | | UnixDateTime UnixDateTime::now() |
171 | 0 | { |
172 | 0 | return UnixDateTime { now_time_from_clock(CLOCK_REALTIME) }; |
173 | 0 | } |
174 | | |
175 | | UnixDateTime UnixDateTime::now_coarse() |
176 | 0 | { |
177 | | return UnixDateTime { now_time_from_clock(CLOCK_REALTIME_COARSE) }; |
178 | 0 | } |
179 | | |
180 | | #endif |
181 | | |
182 | | } |