diff --git a/modules/juce_core/time/juce_Time.cpp b/modules/juce_core/time/juce_Time.cpp index 8b12acbb43..1d0b613ffe 100644 --- a/modules/juce_core/time/juce_Time.cpp +++ b/modules/juce_core/time/juce_Time.cpp @@ -28,93 +28,61 @@ namespace TimeHelpers { - static struct tm millisecondsToTM (const int64 jdm) noexcept + static std::tm millisToLocal (int64 millis) noexcept { - struct tm result; - - const int days = (int) (jdm / 86400LL); - const int a = 32044 + days; - const int b = (4 * a + 3) / 146097; - const int c = a - (b * 146097) / 4; - const int d = (4 * c + 3) / 1461; - const int e = c - (d * 1461) / 4; - const int m = (5 * e + 2) / 153; - - result.tm_mday = e - (153 * m + 2) / 5 + 1; - result.tm_mon = m + 2 - 12 * (m / 10); - result.tm_year = b * 100 + d - 6700 + (m / 10); - result.tm_wday = (days + 1) % 7; - result.tm_yday = -1; - - int t = (int) (jdm % 86400LL); - result.tm_hour = t / 3600; - t %= 3600; - result.tm_min = t / 60; - result.tm_sec = t % 60; - result.tm_isdst = -1; - - return result; - } - - static bool isBeyond1970to2030Range (const int64 seconds) - { - return seconds < 86400LL || seconds >= 2145916800LL; - } - - static struct tm millisToLocal (const int64 millis) noexcept - { - const int64 seconds = millis / 1000; - - if (isBeyond1970to2030Range (seconds)) - { - const int timeZoneAdjustment = 31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000); - return millisecondsToTM (seconds + timeZoneAdjustment + 210866803200LL); - } - - struct tm result; - time_t now = static_cast (seconds); - #if JUCE_WINDOWS && JUCE_MINGW + time_t now = (time_t) (millis / 1000); return *localtime (&now); + #elif JUCE_WINDOWS - if (now >= 0 && now <= 0x793406fff) - localtime_s (&result, &now); - else + std::tm result; + millis /= 1000; + + if (_localtime64_s (&result, &millis) != 0) zerostruct (result); - #else - localtime_r (&now, &result); // more thread-safe - #endif return result; + + #else + std::tm result; + time_t now = (time_t) (millis / 1000); + + if (localtime_r (&now, &result) == nullptr) + zerostruct (result); + + return result; + #endif } - static struct tm millisToUTC (const int64 millis) noexcept + static std::tm millisToUTC (int64 millis) noexcept { - const int64 seconds = millis / 1000; - - if (isBeyond1970to2030Range (seconds)) - return millisecondsToTM (seconds + 210866803200LL); - - struct tm result; - time_t now = static_cast (seconds); - #if JUCE_WINDOWS && JUCE_MINGW + time_t now = (time_t) (millis / 1000); return *gmtime (&now); + #elif JUCE_WINDOWS - if (now >= 0 && now <= 0x793406fff) - gmtime_s (&result, &now); - else + std::tm result; + millis /= 1000; + + if (_gmtime64_s (&result, &millis) != 0) zerostruct (result); - #else - gmtime_r (&now, &result); // more thread-safe - #endif return result; + + #else + std::tm result; + time_t now = (time_t) (millis / 1000); + + if (gmtime_r (&now, &result) == nullptr) + zerostruct (result); + + return result; + #endif } static int getUTCOffsetSeconds (const int64 millis) noexcept { - struct tm utc = millisToUTC (millis); + std::tm utc = millisToUTC (millis); utc.tm_isdst = -1; // Treat this UTC time as local to find the offset return (int) ((millis / 1000) - (int64) mktime (&utc)); @@ -126,7 +94,7 @@ namespace TimeHelpers : (value - ((value / modulo) + 1) * modulo)); } - static inline String formatString (const String& format, const struct tm* const tm) + static inline String formatString (const String& format, const std::tm* const tm) { #if JUCE_ANDROID typedef CharPointer_UTF8 StringType; @@ -160,22 +128,70 @@ namespace TimeHelpers } } + //============================================================================== + static inline bool isLeapYear (int year) noexcept + { + return (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0)); + } + + static inline int daysFromJan1 (int year, int month) noexcept + { + const short dayOfYear[] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + return dayOfYear [(isLeapYear (year) ? 12 : 0) + month]; + } + + static inline int64 daysFromYear0 (int year) noexcept + { + return 365 * (year - 1) + (year / 400) - (year / 100) + (year / 4); + } + + static inline int64 daysFrom1970 (int year) noexcept + { + return daysFromYear0 (year) - daysFromYear0 (1970); + } + + static inline int64 daysFrom1970 (int year, int month) noexcept + { + if (month > 11) + { + year += month / 12; + month %= 12; + } + else if (month < 0) + { + const int numYears = (11 - month) / 12; + year -= numYears; + month += 12 * numYears; + } + + return daysFrom1970 (year) + daysFromJan1 (year, month); + } + + // There's no posix function that does a UTC version of mktime, + // so annoyingly we need to implement this manually.. + static inline int64 mktime_utc (const std::tm& t) noexcept + { + return 24 * 3600 * (daysFrom1970 (t.tm_year + 1900, t.tm_mon) + (t.tm_mday - 1)) + + 3600 * t.tm_hour + + 60 * t.tm_min + + t.tm_sec; + } + static uint32 lastMSCounterValue = 0; } //============================================================================== -Time::Time() noexcept - : millisSinceEpoch (0) +Time::Time() noexcept : millisSinceEpoch (0) { } -Time::Time (const Time& other) noexcept - : millisSinceEpoch (other.millisSinceEpoch) +Time::Time (const Time& other) noexcept : millisSinceEpoch (other.millisSinceEpoch) { } -Time::Time (const int64 ms) noexcept - : millisSinceEpoch (ms) +Time::Time (const int64 ms) noexcept : millisSinceEpoch (ms) { } @@ -188,41 +204,26 @@ Time::Time (const int year, const int milliseconds, const bool useLocalTime) noexcept { - jassert (year > 100); // year must be a 4-digit version + std::tm t; + t.tm_year = year - 1900; + t.tm_mon = month; + t.tm_mday = day; + t.tm_hour = hours; + t.tm_min = minutes; + t.tm_sec = seconds; + t.tm_isdst = -1; - if (year < 1971 || year >= 2038 || ! useLocalTime) + const int64 time = useLocalTime ? (int64) mktime (&t) + : TimeHelpers::mktime_utc (t); + + if (time >= 0) { - // use extended maths for dates beyond 1970 to 2037.. - const int timeZoneAdjustment = useLocalTime ? (31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000)) - : 0; - const int a = (13 - month) / 12; - const int y = year + 4800 - a; - const int jd = day + (153 * (month + 12 * a - 2) + 2) / 5 - + (y * 365) + (y / 4) - (y / 100) + (y / 400) - - 32045; - - const int64 s = ((int64) jd) * 86400LL - 210866803200LL; - - millisSinceEpoch = 1000 * (s + (hours * 3600 + minutes * 60 + seconds - timeZoneAdjustment)) - + milliseconds; + millisSinceEpoch = 1000 * time + milliseconds; } else { - struct tm t; - t.tm_year = year - 1900; - t.tm_mon = month; - t.tm_mday = day; - t.tm_hour = hours; - t.tm_min = minutes; - t.tm_sec = seconds; - t.tm_isdst = -1; - - millisSinceEpoch = 1000 * (int64) mktime (&t); - - if (millisSinceEpoch < 0) - millisSinceEpoch = 0; - else - millisSinceEpoch += milliseconds; + jassertfalse; // trying to create a date that is beyond the range that mktime supports! + millisSinceEpoch = 0; } } @@ -362,7 +363,7 @@ String Time::toString (const bool includeDate, String Time::formatted (const String& format) const { - struct tm t (TimeHelpers::millisToLocal (millisSinceEpoch)); + std::tm t (TimeHelpers::millisToLocal (millisSinceEpoch)); return TimeHelpers::formatString (format, &t); } @@ -659,6 +660,14 @@ public: expect (Time::fromISO8601 ("20160216T150357.999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false)); expect (Time::fromISO8601 ("2016-02-16T15:03:57.999-02:30") == Time (2016, 1, 16, 17, 33, 57, 999, false)); expect (Time::fromISO8601 ("20160216T150357.999-0230") == Time (2016, 1, 16, 17, 33, 57, 999, false)); + + expect (Time (1982, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (1983, 1, 1, 12, 0, 0, 0, true)); + expect (Time (1970, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (1971, 1, 1, 12, 0, 0, 0, true)); + expect (Time (2038, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (2039, 1, 1, 12, 0, 0, 0, true)); + + expect (Time (1982, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (1983, 1, 1, 12, 0, 0, 0, false)); + expect (Time (1970, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (1971, 1, 1, 12, 0, 0, 0, false)); + expect (Time (2038, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (2039, 1, 1, 12, 0, 0, 0, false)); } };