1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2017 - ROLI Ltd.
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 namespace TimeHelpers
27 {
millisToLocal(int64 millis)28     static std::tm millisToLocal (int64 millis) noexcept
29     {
30        #if JUCE_WINDOWS && JUCE_MINGW
31         auto now = (time_t) (millis / 1000);
32         return *localtime (&now);
33 
34        #elif JUCE_WINDOWS
35         std::tm result;
36         millis /= 1000;
37 
38         if (_localtime64_s (&result, &millis) != 0)
39             zerostruct (result);
40 
41         return result;
42 
43        #else
44         std::tm result;
45         auto now = (time_t) (millis / 1000);
46 
47         if (localtime_r (&now, &result) == nullptr)
48             zerostruct (result);
49 
50         return result;
51        #endif
52     }
53 
millisToUTC(int64 millis)54     static std::tm millisToUTC (int64 millis) noexcept
55     {
56        #if JUCE_WINDOWS && JUCE_MINGW
57         auto now = (time_t) (millis / 1000);
58         return *gmtime (&now);
59 
60        #elif JUCE_WINDOWS
61         std::tm result;
62         millis /= 1000;
63 
64         if (_gmtime64_s (&result, &millis) != 0)
65             zerostruct (result);
66 
67         return result;
68 
69        #else
70         std::tm result;
71         auto now = (time_t) (millis / 1000);
72 
73         if (gmtime_r (&now, &result) == nullptr)
74             zerostruct (result);
75 
76         return result;
77        #endif
78     }
79 
getUTCOffsetSeconds(const int64 millis)80     static int getUTCOffsetSeconds (const int64 millis) noexcept
81     {
82         auto utc = millisToUTC (millis);
83         utc.tm_isdst = -1;  // Treat this UTC time as local to find the offset
84 
85         return (int) ((millis / 1000) - (int64) mktime (&utc));
86     }
87 
extendedModulo(const int64 value,const int modulo)88     static int extendedModulo (const int64 value, const int modulo) noexcept
89     {
90         return (int) (value >= 0 ? (value % modulo)
91                                  : (value - ((value / modulo) + 1) * modulo));
92     }
93 
formatString(const String & format,const std::tm * const tm)94     static inline String formatString (const String& format, const std::tm* const tm)
95     {
96        #if JUCE_ANDROID
97         using StringType = CharPointer_UTF8;
98        #elif JUCE_WINDOWS
99         using StringType = CharPointer_UTF16;
100        #else
101         using StringType = CharPointer_UTF32;
102        #endif
103 
104        #ifdef JUCE_MSVC
105         if (tm->tm_year < -1900 || tm->tm_year > 8099)
106             return {};   // Visual Studio's library can only handle 0 -> 9999 AD
107         #endif
108 
109         for (size_t bufferSize = 256; ; bufferSize += 256)
110         {
111             HeapBlock<StringType::CharType> buffer (bufferSize);
112 
113             auto numChars =
114                        #if JUCE_ANDROID
115                         strftime (buffer, bufferSize - 1, format.toUTF8(), tm);
116                        #elif JUCE_WINDOWS
117                         wcsftime (buffer, bufferSize - 1, format.toWideCharPointer(), tm);
118                        #else
119                         wcsftime (buffer, bufferSize - 1, format.toUTF32(), tm);
120                        #endif
121 
122             if (numChars > 0 || format.isEmpty())
123                 return String (StringType (buffer),
124                                StringType (buffer) + (int) numChars);
125         }
126     }
127 
128     //==============================================================================
isLeapYear(int year)129     static inline bool isLeapYear (int year) noexcept
130     {
131         return (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0));
132     }
133 
daysFromJan1(int year,int month)134     static inline int daysFromJan1 (int year, int month) noexcept
135     {
136         const short dayOfYear[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
137                                     0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
138 
139         return dayOfYear [(isLeapYear (year) ? 12 : 0) + month];
140     }
141 
daysFromYear0(int year)142     static inline int64 daysFromYear0 (int year) noexcept
143     {
144         --year;
145         return 365 * year + (year / 400) - (year / 100) + (year / 4);
146     }
147 
daysFrom1970(int year)148     static inline int64 daysFrom1970 (int year) noexcept
149     {
150         return daysFromYear0 (year) - daysFromYear0 (1970);
151     }
152 
daysFrom1970(int year,int month)153     static inline int64 daysFrom1970 (int year, int month) noexcept
154     {
155         if (month > 11)
156         {
157             year += month / 12;
158             month %= 12;
159         }
160         else if (month < 0)
161         {
162             auto numYears = (11 - month) / 12;
163             year -= numYears;
164             month += 12 * numYears;
165         }
166 
167         return daysFrom1970 (year) + daysFromJan1 (year, month);
168     }
169 
170     // There's no posix function that does a UTC version of mktime,
171     // so annoyingly we need to implement this manually..
mktime_utc(const std::tm & t)172     static inline int64 mktime_utc (const std::tm& t) noexcept
173     {
174         return 24 * 3600 * (daysFrom1970 (t.tm_year + 1900, t.tm_mon) + (t.tm_mday - 1))
175                 + 3600 * t.tm_hour
176                 + 60 * t.tm_min
177                 + t.tm_sec;
178     }
179 
180     static Atomic<uint32> lastMSCounterValue { (uint32) 0 };
181 }
182 
183 //==============================================================================
Time(int64 ms)184 Time::Time (int64 ms) noexcept  : millisSinceEpoch (ms) {}
185 
Time(int year,int month,int day,int hours,int minutes,int seconds,int milliseconds,bool useLocalTime)186 Time::Time (int year, int month, int day,
187             int hours, int minutes, int seconds, int milliseconds,
188             bool useLocalTime) noexcept
189 {
190     std::tm t;
191     t.tm_year   = year - 1900;
192     t.tm_mon    = month;
193     t.tm_mday   = day;
194     t.tm_hour   = hours;
195     t.tm_min    = minutes;
196     t.tm_sec    = seconds;
197     t.tm_isdst  = -1;
198 
199     millisSinceEpoch = 1000 * (useLocalTime ? (int64) mktime (&t)
200                                             : TimeHelpers::mktime_utc (t))
201                          + milliseconds;
202 }
203 
204 //==============================================================================
currentTimeMillis()205 int64 Time::currentTimeMillis() noexcept
206 {
207    #if JUCE_WINDOWS && ! JUCE_MINGW
208     struct _timeb t;
209     _ftime_s (&t);
210     return ((int64) t.time) * 1000 + t.millitm;
211    #else
212     struct timeval tv;
213     gettimeofday (&tv, nullptr);
214     return ((int64) tv.tv_sec) * 1000 + tv.tv_usec / 1000;
215    #endif
216 }
217 
getCurrentTime()218 Time JUCE_CALLTYPE Time::getCurrentTime() noexcept
219 {
220     return Time (currentTimeMillis());
221 }
222 
223 //==============================================================================
224 uint32 juce_millisecondsSinceStartup() noexcept;
225 
getMillisecondCounter()226 uint32 Time::getMillisecondCounter() noexcept
227 {
228     auto now = juce_millisecondsSinceStartup();
229 
230     if (now < TimeHelpers::lastMSCounterValue.get())
231     {
232         // in multi-threaded apps this might be called concurrently, so
233         // make sure that our last counter value only increases and doesn't
234         // go backwards..
235         if (now < TimeHelpers::lastMSCounterValue.get() - (uint32) 1000)
236             TimeHelpers::lastMSCounterValue = now;
237     }
238     else
239     {
240         TimeHelpers::lastMSCounterValue = now;
241     }
242 
243     return now;
244 }
245 
getApproximateMillisecondCounter()246 uint32 Time::getApproximateMillisecondCounter() noexcept
247 {
248     auto t = TimeHelpers::lastMSCounterValue.get();
249     return t == 0 ? getMillisecondCounter() : t;
250 }
251 
waitForMillisecondCounter(uint32 targetTime)252 void Time::waitForMillisecondCounter (uint32 targetTime) noexcept
253 {
254     for (;;)
255     {
256         auto now = getMillisecondCounter();
257 
258         if (now >= targetTime)
259             break;
260 
261         auto toWait = (int) (targetTime - now);
262 
263         if (toWait > 2)
264         {
265             Thread::sleep (jmin (20, toWait >> 1));
266         }
267         else
268         {
269             // xxx should consider using mutex_pause on the mac as it apparently
270             // makes it seem less like a spinlock and avoids lowering the thread pri.
271             for (int i = 10; --i >= 0;)
272                 Thread::yield();
273         }
274     }
275 }
276 
277 //==============================================================================
highResolutionTicksToSeconds(const int64 ticks)278 double Time::highResolutionTicksToSeconds (const int64 ticks) noexcept
279 {
280     return ticks / (double) getHighResolutionTicksPerSecond();
281 }
282 
secondsToHighResolutionTicks(const double seconds)283 int64 Time::secondsToHighResolutionTicks (const double seconds) noexcept
284 {
285     return (int64) (seconds * (double) getHighResolutionTicksPerSecond());
286 }
287 
288 //==============================================================================
toString(bool includeDate,bool includeTime,bool includeSeconds,bool use24HourClock) const289 String Time::toString (bool includeDate,
290                        bool includeTime,
291                        bool includeSeconds,
292                        bool use24HourClock) const
293 {
294     String result;
295 
296     if (includeDate)
297     {
298         result << getDayOfMonth() << ' '
299                << getMonthName (true) << ' '
300                << getYear();
301 
302         if (includeTime)
303             result << ' ';
304     }
305 
306     if (includeTime)
307     {
308         auto mins = getMinutes();
309 
310         result << (use24HourClock ? getHours() : getHoursInAmPmFormat())
311                << (mins < 10 ? ":0" : ":") << mins;
312 
313         if (includeSeconds)
314         {
315             auto secs = getSeconds();
316             result << (secs < 10 ? ":0" : ":") << secs;
317         }
318 
319         if (! use24HourClock)
320             result << (isAfternoon() ? "pm" : "am");
321     }
322 
323     return result.trimEnd();
324 }
325 
formatted(const String & format) const326 String Time::formatted (const String& format) const
327 {
328     std::tm t (TimeHelpers::millisToLocal (millisSinceEpoch));
329     return TimeHelpers::formatString (format, &t);
330 }
331 
332 //==============================================================================
getYear() const333 int Time::getYear() const noexcept          { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_year + 1900; }
getMonth() const334 int Time::getMonth() const noexcept         { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_mon; }
getDayOfYear() const335 int Time::getDayOfYear() const noexcept     { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_yday; }
getDayOfMonth() const336 int Time::getDayOfMonth() const noexcept    { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_mday; }
getDayOfWeek() const337 int Time::getDayOfWeek() const noexcept     { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_wday; }
getHours() const338 int Time::getHours() const noexcept         { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_hour; }
getMinutes() const339 int Time::getMinutes() const noexcept       { return TimeHelpers::millisToLocal (millisSinceEpoch).tm_min; }
getSeconds() const340 int Time::getSeconds() const noexcept       { return TimeHelpers::extendedModulo (millisSinceEpoch / 1000, 60); }
getMilliseconds() const341 int Time::getMilliseconds() const noexcept  { return TimeHelpers::extendedModulo (millisSinceEpoch, 1000); }
342 
getHoursInAmPmFormat() const343 int Time::getHoursInAmPmFormat() const noexcept
344 {
345     auto hours = getHours();
346 
347     if (hours == 0)  return 12;
348     if (hours <= 12) return hours;
349 
350     return hours - 12;
351 }
352 
isAfternoon() const353 bool Time::isAfternoon() const noexcept
354 {
355     return getHours() >= 12;
356 }
357 
isDaylightSavingTime() const358 bool Time::isDaylightSavingTime() const noexcept
359 {
360     return TimeHelpers::millisToLocal (millisSinceEpoch).tm_isdst != 0;
361 }
362 
getTimeZone() const363 String Time::getTimeZone() const
364 {
365     String zone[2];
366 
367   #if JUCE_WINDOWS
368    #if JUCE_MSVC || JUCE_CLANG
369     _tzset();
370 
371     for (int i = 0; i < 2; ++i)
372     {
373         char name[128] = { 0 };
374         size_t length;
375         _get_tzname (&length, name, sizeof (name) - 1, i);
376         zone[i] = name;
377     }
378    #else
379     #warning "Can't find a replacement for tzset on mingw - ideas welcome!"
380    #endif
381   #else
382     tzset();
383 
384     auto zonePtr = (const char**) tzname;
385     zone[0] = zonePtr[0];
386     zone[1] = zonePtr[1];
387   #endif
388 
389     if (isDaylightSavingTime())
390     {
391         zone[0] = zone[1];
392 
393         if (zone[0].length() > 3
394              && zone[0].containsIgnoreCase ("daylight")
395              && zone[0].contains ("GMT"))
396             zone[0] = "BST";
397     }
398 
399     return zone[0].substring (0, 3);
400 }
401 
getUTCOffsetSeconds() const402 int Time::getUTCOffsetSeconds() const noexcept
403 {
404     return TimeHelpers::getUTCOffsetSeconds (millisSinceEpoch);
405 }
406 
getUTCOffsetString(bool includeSemiColon) const407 String Time::getUTCOffsetString (bool includeSemiColon) const
408 {
409     if (auto seconds = getUTCOffsetSeconds())
410     {
411         auto minutes = seconds / 60;
412 
413         return String::formatted (includeSemiColon ? "%+03d:%02d"
414                                                    : "%+03d%02d",
415                                   minutes / 60,
416                                   minutes % 60);
417     }
418 
419     return "Z";
420 }
421 
toISO8601(bool includeDividerCharacters) const422 String Time::toISO8601 (bool includeDividerCharacters) const
423 {
424     return String::formatted (includeDividerCharacters ? "%04d-%02d-%02dT%02d:%02d:%06.03f"
425                                                        : "%04d%02d%02dT%02d%02d%06.03f",
426                               getYear(),
427                               getMonth() + 1,
428                               getDayOfMonth(),
429                               getHours(),
430                               getMinutes(),
431                               getSeconds() + getMilliseconds() / 1000.0)
432             + getUTCOffsetString (includeDividerCharacters);
433 }
434 
parseFixedSizeIntAndSkip(String::CharPointerType & t,int numChars,char charToSkip)435 static int parseFixedSizeIntAndSkip (String::CharPointerType& t, int numChars, char charToSkip) noexcept
436 {
437     int n = 0;
438 
439     for (int i = numChars; --i >= 0;)
440     {
441         auto digit = (int) (*t - '0');
442 
443         if (! isPositiveAndBelow (digit, 10))
444             return -1;
445 
446         ++t;
447         n = n * 10 + digit;
448     }
449 
450     if (charToSkip != 0 && *t == (juce_wchar) charToSkip)
451         ++t;
452 
453     return n;
454 }
455 
fromISO8601(StringRef iso)456 Time Time::fromISO8601 (StringRef iso)
457 {
458     auto t = iso.text;
459     auto year = parseFixedSizeIntAndSkip (t, 4, '-');
460 
461     if (year < 0)
462         return {};
463 
464     auto month = parseFixedSizeIntAndSkip (t, 2, '-');
465 
466     if (month < 0)
467         return {};
468 
469     auto day = parseFixedSizeIntAndSkip (t, 2, 0);
470 
471     if (day < 0)
472         return {};
473 
474     int hours = 0, minutes = 0, milliseconds = 0;
475 
476     if (*t == 'T')
477     {
478         ++t;
479         hours = parseFixedSizeIntAndSkip (t, 2, ':');
480 
481         if (hours < 0)
482             return {};
483 
484         minutes = parseFixedSizeIntAndSkip (t, 2, ':');
485 
486         if (minutes < 0)
487             return {};
488 
489         auto seconds = parseFixedSizeIntAndSkip (t, 2, 0);
490 
491         if (seconds < 0)
492              return {};
493 
494         if (*t == '.' || *t == ',')
495         {
496             ++t;
497             milliseconds = parseFixedSizeIntAndSkip (t, 3, 0);
498 
499             if (milliseconds < 0)
500                 return {};
501         }
502 
503         milliseconds += 1000 * seconds;
504     }
505 
506     auto nextChar = t.getAndAdvance();
507 
508     if (nextChar == '-' || nextChar == '+')
509     {
510         auto offsetHours = parseFixedSizeIntAndSkip (t, 2, ':');
511 
512         if (offsetHours < 0)
513             return {};
514 
515         auto offsetMinutes = parseFixedSizeIntAndSkip (t, 2, 0);
516 
517         if (offsetMinutes < 0)
518             return {};
519 
520         auto offsetMs = (offsetHours * 60 + offsetMinutes) * 60 * 1000;
521         milliseconds += nextChar == '-' ? offsetMs : -offsetMs; // NB: this seems backwards but is correct!
522     }
523     else if (nextChar != 0 && nextChar != 'Z')
524     {
525         return {};
526     }
527 
528     return Time (year, month - 1, day, hours, minutes, 0, milliseconds, false);
529 }
530 
getMonthName(const bool threeLetterVersion) const531 String Time::getMonthName (const bool threeLetterVersion) const
532 {
533     return getMonthName (getMonth(), threeLetterVersion);
534 }
535 
getWeekdayName(const bool threeLetterVersion) const536 String Time::getWeekdayName (const bool threeLetterVersion) const
537 {
538     return getWeekdayName (getDayOfWeek(), threeLetterVersion);
539 }
540 
541 static const char* const shortMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
542 static const char* const longMonthNames[]  = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
543 
getMonthName(int monthNumber,const bool threeLetterVersion)544 String Time::getMonthName (int monthNumber, const bool threeLetterVersion)
545 {
546     monthNumber %= 12;
547 
548     return TRANS (threeLetterVersion ? shortMonthNames [monthNumber]
549                                      : longMonthNames [monthNumber]);
550 }
551 
getWeekdayName(int day,const bool threeLetterVersion)552 String Time::getWeekdayName (int day, const bool threeLetterVersion)
553 {
554     static const char* const shortDayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
555     static const char* const longDayNames[]  = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
556 
557     day %= 7;
558 
559     return TRANS (threeLetterVersion ? shortDayNames [day]
560                                      : longDayNames [day]);
561 }
562 
563 //==============================================================================
operator +=(RelativeTime delta)564 Time& Time::operator+= (RelativeTime delta) noexcept           { millisSinceEpoch += delta.inMilliseconds(); return *this; }
operator -=(RelativeTime delta)565 Time& Time::operator-= (RelativeTime delta) noexcept           { millisSinceEpoch -= delta.inMilliseconds(); return *this; }
566 
operator +(Time time,RelativeTime delta)567 Time operator+ (Time time, RelativeTime delta) noexcept        { Time t (time); return t += delta; }
operator -(Time time,RelativeTime delta)568 Time operator- (Time time, RelativeTime delta) noexcept        { Time t (time); return t -= delta; }
operator +(RelativeTime delta,Time time)569 Time operator+ (RelativeTime delta, Time time) noexcept        { Time t (time); return t += delta; }
operator -(Time time1,Time time2)570 const RelativeTime operator- (Time time1, Time time2) noexcept { return RelativeTime::milliseconds (time1.toMilliseconds() - time2.toMilliseconds()); }
571 
operator ==(Time time1,Time time2)572 bool operator== (Time time1, Time time2) noexcept      { return time1.toMilliseconds() == time2.toMilliseconds(); }
operator !=(Time time1,Time time2)573 bool operator!= (Time time1, Time time2) noexcept      { return time1.toMilliseconds() != time2.toMilliseconds(); }
operator <(Time time1,Time time2)574 bool operator<  (Time time1, Time time2) noexcept      { return time1.toMilliseconds() <  time2.toMilliseconds(); }
operator >(Time time1,Time time2)575 bool operator>  (Time time1, Time time2) noexcept      { return time1.toMilliseconds() >  time2.toMilliseconds(); }
operator <=(Time time1,Time time2)576 bool operator<= (Time time1, Time time2) noexcept      { return time1.toMilliseconds() <= time2.toMilliseconds(); }
operator >=(Time time1,Time time2)577 bool operator>= (Time time1, Time time2) noexcept      { return time1.toMilliseconds() >= time2.toMilliseconds(); }
578 
getMonthNumberForCompileDate(const String & m)579 static int getMonthNumberForCompileDate (const String& m)
580 {
581     for (int i = 0; i < 12; ++i)
582         if (m.equalsIgnoreCase (shortMonthNames[i]))
583             return i;
584 
585     // If you hit this because your compiler has an unusual __DATE__
586     // format, let us know so we can add support for it!
587     jassertfalse;
588     return 0;
589 }
590 
getCompilationDate()591 Time Time::getCompilationDate()
592 {
593     StringArray dateTokens, timeTokens;
594 
595     dateTokens.addTokens (__DATE__, true);
596     dateTokens.removeEmptyStrings (true);
597 
598     timeTokens.addTokens (__TIME__, ":", StringRef());
599 
600     return Time (dateTokens[2].getIntValue(),
601                  getMonthNumberForCompileDate (dateTokens[0]),
602                  dateTokens[1].getIntValue(),
603                  timeTokens[0].getIntValue(),
604                  timeTokens[1].getIntValue());
605 }
606 
607 
608 //==============================================================================
609 //==============================================================================
610 #if JUCE_UNIT_TESTS
611 
612 class TimeTests  : public UnitTest
613 {
614 public:
TimeTests()615     TimeTests()
616         : UnitTest ("Time", UnitTestCategories::time)
617     {}
618 
runTest()619     void runTest() override
620     {
621         beginTest ("Time");
622 
623         Time t = Time::getCurrentTime();
624         expect (t > Time());
625 
626         Thread::sleep (15);
627         expect (Time::getCurrentTime() > t);
628 
629         expect (t.getTimeZone().isNotEmpty());
630         expect (t.getUTCOffsetString (true)  == "Z" || t.getUTCOffsetString (true).length() == 6);
631         expect (t.getUTCOffsetString (false) == "Z" || t.getUTCOffsetString (false).length() == 5);
632 
633         expect (Time::fromISO8601 (t.toISO8601 (true)) == t);
634         expect (Time::fromISO8601 (t.toISO8601 (false)) == t);
635 
636         expect (Time::fromISO8601 ("2016-02-16") == Time (2016, 1, 16, 0, 0, 0, 0, false));
637         expect (Time::fromISO8601 ("20160216Z")  == Time (2016, 1, 16, 0, 0, 0, 0, false));
638 
639         expect (Time::fromISO8601 ("2016-02-16T15:03:57+00:00") == Time (2016, 1, 16, 15, 3, 57, 0, false));
640         expect (Time::fromISO8601 ("20160216T150357+0000")      == Time (2016, 1, 16, 15, 3, 57, 0, false));
641 
642         expect (Time::fromISO8601 ("2016-02-16T15:03:57.999+00:00") == Time (2016, 1, 16, 15, 3, 57, 999, false));
643         expect (Time::fromISO8601 ("20160216T150357.999+0000")      == Time (2016, 1, 16, 15, 3, 57, 999, false));
644         expect (Time::fromISO8601 ("2016-02-16T15:03:57.999Z")      == Time (2016, 1, 16, 15, 3, 57, 999, false));
645         expect (Time::fromISO8601 ("2016-02-16T15:03:57,999Z")      == Time (2016, 1, 16, 15, 3, 57, 999, false));
646         expect (Time::fromISO8601 ("20160216T150357.999Z")          == Time (2016, 1, 16, 15, 3, 57, 999, false));
647         expect (Time::fromISO8601 ("20160216T150357,999Z")          == Time (2016, 1, 16, 15, 3, 57, 999, false));
648 
649         expect (Time::fromISO8601 ("2016-02-16T15:03:57.999-02:30") == Time (2016, 1, 16, 17, 33, 57, 999, false));
650         expect (Time::fromISO8601 ("2016-02-16T15:03:57,999-02:30") == Time (2016, 1, 16, 17, 33, 57, 999, false));
651         expect (Time::fromISO8601 ("20160216T150357.999-0230")      == Time (2016, 1, 16, 17, 33, 57, 999, false));
652         expect (Time::fromISO8601 ("20160216T150357,999-0230")      == Time (2016, 1, 16, 17, 33, 57, 999, false));
653 
654         expect (Time (1970,  0,  1,  0,  0,  0, 0, false) == Time (0));
655         expect (Time (2106,  1,  7,  6, 28, 15, 0, false) == Time (4294967295000));
656         expect (Time (2007, 10,  7,  1,  7, 20, 0, false) == Time (1194397640000));
657         expect (Time (2038,  0, 19,  3, 14,  7, 0, false) == Time (2147483647000));
658         expect (Time (2016,  2,  7, 11, 20,  8, 0, false) == Time (1457349608000));
659         expect (Time (1969, 11, 31, 23, 59, 59, 0, false) == Time (-1000));
660         expect (Time (1901, 11, 13, 20, 45, 53, 0, false) == Time (-2147483647000));
661 
662         expect (Time (1982, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (1983, 1, 1, 12, 0, 0, 0, true));
663         expect (Time (1970, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (1971, 1, 1, 12, 0, 0, 0, true));
664         expect (Time (2038, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (2039, 1, 1, 12, 0, 0, 0, true));
665 
666         expect (Time (1982, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (1983, 1, 1, 12, 0, 0, 0, false));
667         expect (Time (1970, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (1971, 1, 1, 12, 0, 0, 0, false));
668         expect (Time (2038, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (2039, 1, 1, 12, 0, 0, 0, false));
669     }
670 };
671 
672 static TimeTests timeTests;
673 
674 #endif
675 
676 } // namespace juce
677