123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676 |
- /*
- ==============================================================================
- This file is part of the juce_core module of the JUCE library.
- Copyright (c) 2015 - ROLI Ltd.
- Permission to use, copy, modify, and/or distribute this software for any purpose with
- or without fee is hereby granted, provided that the above copyright notice and this
- permission notice appear in all copies.
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
- TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
- NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
- IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- ------------------------------------------------------------------------------
- NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
- All other JUCE modules are covered by a dual GPL/commercial license, so if you are
- using any other modules, be sure to check that you also comply with their license.
- For more details, visit www.juce.com
- ==============================================================================
- */
- namespace TimeHelpers
- {
- static std::tm millisToLocal (int64 millis) noexcept
- {
- #if JUCE_WINDOWS && JUCE_MINGW
- time_t now = (time_t) (millis / 1000);
- return *localtime (&now);
- #elif JUCE_WINDOWS
- std::tm result;
- millis /= 1000;
- if (_localtime64_s (&result, &millis) != 0)
- zerostruct (result);
- 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 std::tm millisToUTC (int64 millis) noexcept
- {
- #if JUCE_WINDOWS && JUCE_MINGW
- time_t now = (time_t) (millis / 1000);
- return *gmtime (&now);
- #elif JUCE_WINDOWS
- std::tm result;
- millis /= 1000;
- if (_gmtime64_s (&result, &millis) != 0)
- zerostruct (result);
- 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
- {
- 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));
- }
- static int extendedModulo (const int64 value, const int modulo) noexcept
- {
- return (int) (value >= 0 ? (value % modulo)
- : (value - ((value / modulo) + 1) * modulo));
- }
- static inline String formatString (const String& format, const std::tm* const tm)
- {
- #if JUCE_ANDROID
- typedef CharPointer_UTF8 StringType;
- #elif JUCE_WINDOWS
- typedef CharPointer_UTF16 StringType;
- #else
- typedef CharPointer_UTF32 StringType;
- #endif
- #ifdef JUCE_MSVC
- if (tm->tm_year < -1900 || tm->tm_year > 8099)
- return String(); // Visual Studio's library can only handle 0 -> 9999 AD
- #endif
- for (size_t bufferSize = 256; ; bufferSize += 256)
- {
- HeapBlock<StringType::CharType> buffer (bufferSize);
- const size_t numChars =
- #if JUCE_ANDROID
- strftime (buffer, bufferSize - 1, format.toUTF8(), tm);
- #elif JUCE_WINDOWS
- wcsftime (buffer, bufferSize - 1, format.toWideCharPointer(), tm);
- #else
- wcsftime (buffer, bufferSize - 1, format.toUTF32(), tm);
- #endif
- if (numChars > 0 || format.isEmpty())
- return String (StringType (buffer),
- StringType (buffer) + (int) numChars);
- }
- }
- //==============================================================================
- 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, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
- 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
- return dayOfYear [(isLeapYear (year) ? 12 : 0) + month];
- }
- static inline int64 daysFromYear0 (int year) noexcept
- {
- --year;
- return 365 * year + (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 (const Time& other) noexcept : millisSinceEpoch (other.millisSinceEpoch)
- {
- }
- Time::Time (const int64 ms) noexcept : millisSinceEpoch (ms)
- {
- }
- Time::Time (const int year,
- const int month,
- const int day,
- const int hours,
- const int minutes,
- const int seconds,
- const int milliseconds,
- const bool useLocalTime) noexcept
- {
- 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;
- millisSinceEpoch = 1000 * (useLocalTime ? (int64) mktime (&t)
- : TimeHelpers::mktime_utc (t))
- + milliseconds;
- }
- Time::~Time() noexcept
- {
- }
- Time& Time::operator= (const Time& other) noexcept
- {
- millisSinceEpoch = other.millisSinceEpoch;
- return *this;
- }
- //==============================================================================
- int64 Time::currentTimeMillis() noexcept
- {
- #if JUCE_WINDOWS && ! JUCE_MINGW
- struct _timeb t;
- _ftime_s (&t);
- return ((int64) t.time) * 1000 + t.millitm;
- #else
- struct timeval tv;
- gettimeofday (&tv, nullptr);
- return ((int64) tv.tv_sec) * 1000 + tv.tv_usec / 1000;
- #endif
- }
- Time JUCE_CALLTYPE Time::getCurrentTime() noexcept
- {
- return Time (currentTimeMillis());
- }
- //==============================================================================
- uint32 juce_millisecondsSinceStartup() noexcept;
- uint32 Time::getMillisecondCounter() noexcept
- {
- const uint32 now = juce_millisecondsSinceStartup();
- if (now < TimeHelpers::lastMSCounterValue)
- {
- // in multi-threaded apps this might be called concurrently, so
- // make sure that our last counter value only increases and doesn't
- // go backwards..
- if (now < TimeHelpers::lastMSCounterValue - 1000)
- TimeHelpers::lastMSCounterValue = now;
- }
- else
- {
- TimeHelpers::lastMSCounterValue = now;
- }
- return now;
- }
- uint32 Time::getApproximateMillisecondCounter() noexcept
- {
- if (TimeHelpers::lastMSCounterValue == 0)
- getMillisecondCounter();
- return TimeHelpers::lastMSCounterValue;
- }
- void Time::waitForMillisecondCounter (const uint32 targetTime) noexcept
- {
- for (;;)
- {
- const uint32 now = getMillisecondCounter();
- if (now >= targetTime)
- break;
- const int toWait = (int) (targetTime - now);
- if (toWait > 2)
- {
- Thread::sleep (jmin (20, toWait >> 1));
- }
- else
- {
- // xxx should consider using mutex_pause on the mac as it apparently
- // makes it seem less like a spinlock and avoids lowering the thread pri.
- for (int i = 10; --i >= 0;)
- Thread::yield();
- }
- }
- }
- //==============================================================================
- double Time::highResolutionTicksToSeconds (const int64 ticks) noexcept
- {
- return ticks / (double) getHighResolutionTicksPerSecond();
- }
- int64 Time::secondsToHighResolutionTicks (const double seconds) noexcept
- {
- return (int64) (seconds * (double) getHighResolutionTicksPerSecond());
- }
- //==============================================================================
- String Time::toString (const bool includeDate,
- const bool includeTime,
- const bool includeSeconds,
- const bool use24HourClock) const noexcept
- {
- String result;
- if (includeDate)
- {
- result << getDayOfMonth() << ' '
- << getMonthName (true) << ' '
- << getYear();
- if (includeTime)
- result << ' ';
- }
- if (includeTime)
- {
- const int mins = getMinutes();
- result << (use24HourClock ? getHours() : getHoursInAmPmFormat())
- << (mins < 10 ? ":0" : ":") << mins;
- if (includeSeconds)
- {
- const int secs = getSeconds();
- result << (secs < 10 ? ":0" : ":") << secs;
- }
- if (! use24HourClock)
- result << (isAfternoon() ? "pm" : "am");
- }
- return result.trimEnd();
- }
- String Time::formatted (const String& format) const
- {
- std::tm t (TimeHelpers::millisToLocal (millisSinceEpoch));
- return TimeHelpers::formatString (format, &t);
- }
- //==============================================================================
- int Time::getYear() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_year + 1900; }
- int Time::getMonth() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_mon; }
- int Time::getDayOfYear() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_yday; }
- int Time::getDayOfMonth() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_mday; }
- int Time::getDayOfWeek() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_wday; }
- int Time::getHours() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_hour; }
- int Time::getMinutes() const noexcept { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_min; }
- int Time::getSeconds() const noexcept { return TimeHelpers::extendedModulo (millisSinceEpoch / 1000, 60); }
- int Time::getMilliseconds() const noexcept { return TimeHelpers::extendedModulo (millisSinceEpoch, 1000); }
- int Time::getHoursInAmPmFormat() const noexcept
- {
- const int hours = getHours();
- if (hours == 0) return 12;
- if (hours <= 12) return hours;
- return hours - 12;
- }
- bool Time::isAfternoon() const noexcept
- {
- return getHours() >= 12;
- }
- bool Time::isDaylightSavingTime() const noexcept
- {
- return TimeHelpers::millisToLocal (millisSinceEpoch).tm_isdst != 0;
- }
- String Time::getTimeZone() const noexcept
- {
- String zone[2];
- #if JUCE_WINDOWS
- #if JUCE_MSVC || JUCE_CLANG
- _tzset();
- for (int i = 0; i < 2; ++i)
- {
- char name[128] = { 0 };
- size_t length;
- _get_tzname (&length, name, 127, i);
- zone[i] = name;
- }
- #else
- #warning "Can't find a replacement for tzset on mingw - ideas welcome!"
- #endif
- #else
- tzset();
- const char** const zonePtr = (const char**) tzname;
- zone[0] = zonePtr[0];
- zone[1] = zonePtr[1];
- #endif
- if (isDaylightSavingTime())
- {
- zone[0] = zone[1];
- if (zone[0].length() > 3
- && zone[0].containsIgnoreCase ("daylight")
- && zone[0].contains ("GMT"))
- zone[0] = "BST";
- }
- return zone[0].substring (0, 3);
- }
- int Time::getUTCOffsetSeconds() const noexcept
- {
- return TimeHelpers::getUTCOffsetSeconds (millisSinceEpoch);
- }
- String Time::getUTCOffsetString (bool includeSemiColon) const
- {
- if (int seconds = getUTCOffsetSeconds())
- {
- const int minutes = seconds / 60;
- return String::formatted (includeSemiColon ? "%+03d:%02d"
- : "%+03d%02d",
- minutes / 60,
- minutes % 60);
- }
- return "Z";
- }
- String Time::toISO8601 (bool includeDividerCharacters) const
- {
- return String::formatted (includeDividerCharacters ? "%04d-%02d-%02dT%02d:%02d:%06.03f"
- : "%04d%02d%02dT%02d%02d%06.03f",
- getYear(),
- getMonth() + 1,
- getDayOfMonth(),
- getHours(),
- getMinutes(),
- getSeconds() + getMilliseconds() / 1000.0)
- + getUTCOffsetString (includeDividerCharacters);
- }
- static int parseFixedSizeIntAndSkip (String::CharPointerType& t, int numChars, char charToSkip) noexcept
- {
- int n = 0;
- for (int i = numChars; --i >= 0;)
- {
- const int digit = (int) (*t - '0');
- if (! isPositiveAndBelow (digit, 10))
- return -1;
- ++t;
- n = n * 10 + digit;
- }
- if (charToSkip != 0 && *t == (juce_wchar) charToSkip)
- ++t;
- return n;
- }
- Time Time::fromISO8601 (StringRef iso) noexcept
- {
- String::CharPointerType t = iso.text;
- const int year = parseFixedSizeIntAndSkip (t, 4, '-');
- if (year < 0)
- return Time();
- const int month = parseFixedSizeIntAndSkip (t, 2, '-');
- if (month < 0)
- return Time();
- const int day = parseFixedSizeIntAndSkip (t, 2, 0);
- if (day < 0)
- return Time();
- int hours = 0, minutes = 0, milliseconds = 0;
- if (*t == 'T')
- {
- ++t;
- hours = parseFixedSizeIntAndSkip (t, 2, ':');
- if (hours < 0)
- return Time();
- minutes = parseFixedSizeIntAndSkip (t, 2, ':');
- if (minutes < 0)
- return Time();
- milliseconds = (int) (1000.0 * CharacterFunctions::readDoubleValue (t));
- }
- const juce_wchar nextChar = t.getAndAdvance();
- if (nextChar == '-' || nextChar == '+')
- {
- const int offsetHours = parseFixedSizeIntAndSkip (t, 2, ':');
- if (offsetHours < 0)
- return Time();
- const int offsetMinutes = parseFixedSizeIntAndSkip (t, 2, 0);
- if (offsetMinutes < 0)
- return Time();
- const int offsetMs = (offsetHours * 60 + offsetMinutes) * 60 * 1000;
- milliseconds += nextChar == '-' ? offsetMs : -offsetMs; // NB: this seems backwards but is correct!
- }
- else if (nextChar != 0 && nextChar != 'Z')
- {
- return Time();
- }
- return Time (year, month - 1, day, hours, minutes, 0, milliseconds, false);
- }
- String Time::getMonthName (const bool threeLetterVersion) const
- {
- return getMonthName (getMonth(), threeLetterVersion);
- }
- String Time::getWeekdayName (const bool threeLetterVersion) const
- {
- return getWeekdayName (getDayOfWeek(), threeLetterVersion);
- }
- static const char* const shortMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
- static const char* const longMonthNames[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
- String Time::getMonthName (int monthNumber, const bool threeLetterVersion)
- {
- monthNumber %= 12;
- return TRANS (threeLetterVersion ? shortMonthNames [monthNumber]
- : longMonthNames [monthNumber]);
- }
- String Time::getWeekdayName (int day, const bool threeLetterVersion)
- {
- static const char* const shortDayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
- static const char* const longDayNames[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
- day %= 7;
- return TRANS (threeLetterVersion ? shortDayNames [day]
- : longDayNames [day]);
- }
- //==============================================================================
- Time& Time::operator+= (RelativeTime delta) noexcept { millisSinceEpoch += delta.inMilliseconds(); return *this; }
- Time& Time::operator-= (RelativeTime delta) noexcept { millisSinceEpoch -= delta.inMilliseconds(); return *this; }
- Time operator+ (Time time, RelativeTime delta) noexcept { Time t (time); return t += delta; }
- Time operator- (Time time, RelativeTime delta) noexcept { Time t (time); return t -= delta; }
- Time operator+ (RelativeTime delta, Time time) noexcept { Time t (time); return t += delta; }
- const RelativeTime operator- (Time time1, Time time2) noexcept { return RelativeTime::milliseconds (time1.toMilliseconds() - time2.toMilliseconds()); }
- bool operator== (Time time1, Time time2) noexcept { return time1.toMilliseconds() == time2.toMilliseconds(); }
- bool operator!= (Time time1, Time time2) noexcept { return time1.toMilliseconds() != time2.toMilliseconds(); }
- bool operator< (Time time1, Time time2) noexcept { return time1.toMilliseconds() < time2.toMilliseconds(); }
- bool operator> (Time time1, Time time2) noexcept { return time1.toMilliseconds() > time2.toMilliseconds(); }
- bool operator<= (Time time1, Time time2) noexcept { return time1.toMilliseconds() <= time2.toMilliseconds(); }
- bool operator>= (Time time1, Time time2) noexcept { return time1.toMilliseconds() >= time2.toMilliseconds(); }
- static int getMonthNumberForCompileDate (const String& m) noexcept
- {
- for (int i = 0; i < 12; ++i)
- if (m.equalsIgnoreCase (shortMonthNames[i]))
- return i;
- // If you hit this because your compiler has an unusual __DATE__
- // format, let us know so we can add support for it!
- jassertfalse;
- return 0;
- }
- Time Time::getCompilationDate()
- {
- StringArray dateTokens, timeTokens;
- dateTokens.addTokens (__DATE__, true);
- dateTokens.removeEmptyStrings (true);
- timeTokens.addTokens (__TIME__, ":", StringRef());
- return Time (dateTokens[2].getIntValue(),
- getMonthNumberForCompileDate (dateTokens[0]),
- dateTokens[1].getIntValue(),
- timeTokens[0].getIntValue(),
- timeTokens[1].getIntValue());
- }
- //==============================================================================
- //==============================================================================
- #if JUCE_UNIT_TESTS
- class TimeTests : public UnitTest
- {
- public:
- TimeTests() : UnitTest ("Time") {}
- void runTest() override
- {
- beginTest ("Time");
- Time t = Time::getCurrentTime();
- expect (t > Time());
- Thread::sleep (15);
- expect (Time::getCurrentTime() > t);
- expect (t.getTimeZone().isNotEmpty());
- expect (t.getUTCOffsetString (true) == "Z" || t.getUTCOffsetString (true).length() == 6);
- expect (t.getUTCOffsetString (false) == "Z" || t.getUTCOffsetString (false).length() == 5);
- expect (Time::fromISO8601 (t.toISO8601 (true)) == t);
- expect (Time::fromISO8601 (t.toISO8601 (false)) == t);
- expect (Time::fromISO8601 ("2016-02-16") == Time (2016, 1, 16, 0, 0, 0, 0, false));
- expect (Time::fromISO8601 ("20160216Z") == Time (2016, 1, 16, 0, 0, 0, 0, false));
- expect (Time::fromISO8601 ("2016-02-16T15:03:57+00:00") == Time (2016, 1, 16, 15, 3, 57, 0, false));
- expect (Time::fromISO8601 ("20160216T150357+0000") == Time (2016, 1, 16, 15, 3, 57, 0, false));
- expect (Time::fromISO8601 ("2016-02-16T15:03:57.999+00:00") == Time (2016, 1, 16, 15, 3, 57, 999, false));
- expect (Time::fromISO8601 ("20160216T150357.999+0000") == Time (2016, 1, 16, 15, 3, 57, 999, false));
- expect (Time::fromISO8601 ("2016-02-16T15:03:57.999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false));
- 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 (1970, 0, 1, 0, 0, 0, 0, false) == Time (0));
- expect (Time (2106, 1, 7, 6, 28, 15, 0, false) == Time (4294967295000));
- expect (Time (2007, 10, 7, 1, 7, 20, 0, false) == Time (1194397640000));
- expect (Time (2038, 0, 19, 3, 14, 7, 0, false) == Time (2147483647000));
- expect (Time (2016, 2, 7, 11, 20, 8, 0, false) == Time (1457349608000));
- expect (Time (1969, 11, 31, 23, 59, 59, 0, false) == Time (-1000));
- expect (Time (1901, 11, 13, 20, 45, 53, 0, false) == Time (-2147483647000));
- 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));
- }
- };
- static TimeTests timeTests;
- #endif
|