mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-30 19:32:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			242 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			242 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020, the SerenityOS developers.
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <AK/Checked.h>
 | |
| #include <AK/Time.h>
 | |
| 
 | |
| // Make a reasonable guess as to which timespec/timeval definition to use.
 | |
| // It doesn't really matter, since both are identical.
 | |
| #ifdef KERNEL
 | |
| #    include <Kernel/UnixTypes.h>
 | |
| #else
 | |
| #    include <sys/time.h>
 | |
| #    include <time.h>
 | |
| #endif
 | |
| 
 | |
| namespace AK {
 | |
| 
 | |
| int days_in_month(int year, unsigned month)
 | |
| {
 | |
|     VERIFY(month >= 1 && month <= 12);
 | |
|     if (month == 2)
 | |
|         return is_leap_year(year) ? 29 : 28;
 | |
| 
 | |
|     bool is_long_month = (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12);
 | |
|     return is_long_month ? 31 : 30;
 | |
| }
 | |
| 
 | |
| unsigned day_of_week(int year, unsigned month, int day)
 | |
| {
 | |
|     VERIFY(month >= 1 && month <= 12);
 | |
|     constexpr Array seek_table = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
 | |
|     if (month < 3)
 | |
|         --year;
 | |
| 
 | |
|     return (year + year / 4 - year / 100 + year / 400 + seek_table[month - 1] + day) % 7;
 | |
| }
 | |
| 
 | |
| Duration Duration::from_ticks(clock_t ticks, time_t ticks_per_second)
 | |
| {
 | |
|     auto secs = ticks % ticks_per_second;
 | |
| 
 | |
|     i32 nsecs = 1'000'000'000 * (ticks - (ticks_per_second * secs)) / ticks_per_second;
 | |
|     i32 extra_secs = sane_mod(nsecs, 1'000'000'000);
 | |
|     return Duration::from_half_sanitized(secs, extra_secs, nsecs);
 | |
| }
 | |
| 
 | |
| Duration Duration::from_timespec(const struct timespec& ts)
 | |
| {
 | |
|     i32 nsecs = ts.tv_nsec;
 | |
|     i32 extra_secs = sane_mod(nsecs, 1'000'000'000);
 | |
|     return Duration::from_half_sanitized(ts.tv_sec, extra_secs, nsecs);
 | |
| }
 | |
| 
 | |
| Duration Duration::from_timeval(const struct timeval& tv)
 | |
| {
 | |
|     i32 usecs = tv.tv_usec;
 | |
|     i32 extra_secs = sane_mod(usecs, 1'000'000);
 | |
|     VERIFY(0 <= usecs && usecs < 1'000'000);
 | |
|     return Duration::from_half_sanitized(tv.tv_sec, extra_secs, usecs * 1'000);
 | |
| }
 | |
| 
 | |
| i64 Duration::to_truncated_seconds() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     if (m_seconds < 0 && m_nanoseconds) {
 | |
|         // Since m_seconds is negative, adding 1 can't possibly overflow
 | |
|         return m_seconds + 1;
 | |
|     }
 | |
|     return m_seconds;
 | |
| }
 | |
| 
 | |
| i64 Duration::to_truncated_milliseconds() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     Checked<i64> milliseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
 | |
|     milliseconds *= 1'000;
 | |
|     milliseconds += m_nanoseconds / 1'000'000;
 | |
|     if (m_seconds < 0) {
 | |
|         if (m_nanoseconds % 1'000'000 != 0) {
 | |
|             // Does not overflow: milliseconds <= 1'999.
 | |
|             milliseconds++;
 | |
|         }
 | |
|         // We dropped one second previously, put it back in now that we have handled the rounding.
 | |
|         milliseconds -= 1'000;
 | |
|     }
 | |
|     if (!milliseconds.has_overflow())
 | |
|         return milliseconds.value();
 | |
|     return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
 | |
| }
 | |
| 
 | |
| i64 Duration::to_truncated_microseconds() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     Checked<i64> microseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
 | |
|     microseconds *= 1'000'000;
 | |
|     microseconds += m_nanoseconds / 1'000;
 | |
|     if (m_seconds < 0) {
 | |
|         if (m_nanoseconds % 1'000 != 0) {
 | |
|             // Does not overflow: microseconds <= 1'999'999.
 | |
|             microseconds++;
 | |
|         }
 | |
|         // We dropped one second previously, put it back in now that we have handled the rounding.
 | |
|         microseconds -= 1'000'000;
 | |
|     }
 | |
|     if (!microseconds.has_overflow())
 | |
|         return microseconds.value();
 | |
|     return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
 | |
| }
 | |
| 
 | |
| i64 Duration::to_seconds() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     if (m_seconds >= 0 && m_nanoseconds) {
 | |
|         Checked<i64> seconds(m_seconds);
 | |
|         seconds++;
 | |
|         return seconds.has_overflow() ? 0x7fff'ffff'ffff'ffffLL : seconds.value();
 | |
|     }
 | |
|     return m_seconds;
 | |
| }
 | |
| 
 | |
| i64 Duration::to_milliseconds() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     Checked<i64> milliseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
 | |
|     milliseconds *= 1'000;
 | |
|     milliseconds += m_nanoseconds / 1'000'000;
 | |
|     if (m_seconds >= 0 && m_nanoseconds % 1'000'000 != 0)
 | |
|         milliseconds++;
 | |
|     if (m_seconds < 0) {
 | |
|         // We dropped one second previously, put it back in now that we have handled the rounding.
 | |
|         milliseconds -= 1'000;
 | |
|     }
 | |
|     if (!milliseconds.has_overflow())
 | |
|         return milliseconds.value();
 | |
|     return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
 | |
| }
 | |
| 
 | |
| i64 Duration::to_microseconds() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     Checked<i64> microseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
 | |
|     microseconds *= 1'000'000;
 | |
|     microseconds += m_nanoseconds / 1'000;
 | |
|     if (m_seconds >= 0 && m_nanoseconds % 1'000 != 0)
 | |
|         microseconds++;
 | |
|     if (m_seconds < 0) {
 | |
|         // We dropped one second previously, put it back in now that we have handled the rounding.
 | |
|         microseconds -= 1'000'000;
 | |
|     }
 | |
|     if (!microseconds.has_overflow())
 | |
|         return microseconds.value();
 | |
|     return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
 | |
| }
 | |
| 
 | |
| i64 Duration::to_nanoseconds() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     Checked<i64> nanoseconds((m_seconds < 0) ? m_seconds + 1 : m_seconds);
 | |
|     nanoseconds *= 1'000'000'000;
 | |
|     nanoseconds += m_nanoseconds;
 | |
|     if (m_seconds < 0) {
 | |
|         // We dropped one second previously, put it back in now that we have handled the rounding.
 | |
|         nanoseconds -= 1'000'000'000;
 | |
|     }
 | |
|     if (!nanoseconds.has_overflow())
 | |
|         return nanoseconds.value();
 | |
|     return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
 | |
| }
 | |
| 
 | |
| timespec Duration::to_timespec() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     return { static_cast<time_t>(m_seconds), static_cast<long>(m_nanoseconds) };
 | |
| }
 | |
| 
 | |
| timeval Duration::to_timeval() const
 | |
| {
 | |
|     VERIFY(m_nanoseconds < 1'000'000'000);
 | |
|     // This is done because winsock defines tv_sec and tv_usec as long, and Linux64 as long int.
 | |
|     using sec_type = decltype(declval<timeval>().tv_sec);
 | |
|     using usec_type = decltype(declval<timeval>().tv_usec);
 | |
|     return { static_cast<sec_type>(m_seconds), static_cast<usec_type>(m_nanoseconds) / 1000 };
 | |
| }
 | |
| 
 | |
| Duration Duration::from_half_sanitized(i64 seconds, i32 extra_seconds, u32 nanoseconds)
 | |
| {
 | |
|     VERIFY(nanoseconds < 1'000'000'000);
 | |
| 
 | |
|     if ((seconds <= 0 && extra_seconds > 0) || (seconds >= 0 && extra_seconds < 0)) {
 | |
|         // Opposite signs mean that we can definitely add them together without fear of overflowing i64:
 | |
|         seconds += extra_seconds;
 | |
|         extra_seconds = 0;
 | |
|     }
 | |
| 
 | |
|     // Now the only possible way to become invalid is overflowing i64 towards positive infinity:
 | |
|     if (Checked<i64>::addition_would_overflow<i64, i64>(seconds, extra_seconds)) {
 | |
|         if (seconds < 0) {
 | |
|             return Duration::min();
 | |
|         } else {
 | |
|             return Duration::max();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return Duration { seconds + extra_seconds, nanoseconds };
 | |
| }
 | |
| 
 | |
| #ifndef KERNEL
 | |
| namespace {
 | |
| static Duration now_time_from_clock(clockid_t clock_id)
 | |
| {
 | |
|     timespec now_spec {};
 | |
|     ::clock_gettime(clock_id, &now_spec);
 | |
|     return Duration::from_timespec(now_spec);
 | |
| }
 | |
| }
 | |
| 
 | |
| MonotonicTime MonotonicTime::now()
 | |
| {
 | |
|     return MonotonicTime { now_time_from_clock(CLOCK_MONOTONIC) };
 | |
| }
 | |
| 
 | |
| MonotonicTime MonotonicTime::now_coarse()
 | |
| {
 | |
|     return MonotonicTime { now_time_from_clock(CLOCK_MONOTONIC_COARSE) };
 | |
| }
 | |
| 
 | |
| UnixDateTime UnixDateTime::now()
 | |
| {
 | |
|     return UnixDateTime { now_time_from_clock(CLOCK_REALTIME) };
 | |
| }
 | |
| 
 | |
| UnixDateTime UnixDateTime::now_coarse()
 | |
| {
 | |
|     return UnixDateTime { now_time_from_clock(CLOCK_REALTIME_COARSE) };
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| }
 | 
