Kaya Kupferschmidt avatar Kaya Kupferschmidt committed 13069cc

Improved arithmetic with leap seconds

Comments (0)

Files changed (6)

source/libs/magnum_core/source/magnum/util/DateTime.cpp

 
 
 namespace {
+using magnum::util::DateTime;
+using magnum::types::int64;
 
 struct LeapSecondEntry
 {
     return leapSecondTable[leapSecondTableSize-1].totalSeconds;
 }
 
+static bool isLeapMinute(int jd, int64 tick)
+{
+    static const int64 lastMinute = 23*DateTime::TicksPerHour + 59*DateTime::TicksPerMinute;
+    if (tick < lastMinute)
+        return false;
+
+    for(int i = 0; i < leapSecondTableSize; ++i) {
+        int ljd = leapSecondTable[i].julianDay;
+        if (ljd == jd)
+            return true;
+        if (ljd > jd)
+            return false;
+    }
+
+    return false;
+}
+
 }
 
 namespace magnum { namespace util {
         julianDay--;
     }
 
-    curLeapSeconds = getLeapSecondsForJulianDay(julianDay);
+    setAbsolute(julianDay, curTicks);
+}
+
+
+/*--------------------------------------------------------------------------*/
+/**
+ */
+void DateTime::setAbsolute(int julianDay, int64 curTicks)
+{
+    int curLeapSeconds = getLeapSecondsForJulianDay(julianDay);
     curTicks -= curLeapSeconds * TicksPerSecond;
 
     // Correctly handle over/underflows due to leap-second adjustments
  */
 void DateTime::addTicks(int64 ticks)
 {
-    // TODO consider leap seconds
+    int julianDay = m_Date.getJulianDay();
+    int curLeapSeconds = getLeapSecondsForJulianDay(julianDay);
+    ticks += curLeapSeconds * TicksPerSecond;
 
     bool sign = ticks < 0;
     int numDays = sign ? -int(-ticks / TicksPerDay) : int(ticks / TicksPerDay);
-    ticks -= TicksPerDay*numDays;
+    ticks -= numDays * TicksPerDay;
+    julianDay += numDays;
 
-    if (ticks != 0) {
-        Time oldtime = m_Time;
-        m_Time.addTicks(ticks);
-        if (ticks > 0 && oldtime > m_Time) {
-            numDays++;
-        }
-        else if (ticks < 0 && oldtime < m_Time) {
-            numDays--;
-        }
+    int64 curTicks = m_Time.getTicks();
+    curTicks += ticks;
+    if (curTicks >= TicksPerDay) {
+        curTicks -= TicksPerDay;
+        julianDay++;
     }
-    
-    if (numDays != 0) {
-        m_Date.addDays(numDays);
+    else if (curTicks < 0) {
+        curTicks += TicksPerDay;
+        julianDay--;
     }
+
+    setAbsolute(julianDay, curTicks);
 }
 
 
  */
 void DateTime::addMinutes(int minutes)
 {
+    int julianDay = m_Date.getJulianDay();
     bool sign = minutes < 0;
     int numDays = sign ? -int(-minutes / MinutesPerDay) : int(minutes / MinutesPerDay);
     minutes -= numDays * MinutesPerDay;
+    julianDay += numDays;
 
-    if (minutes != 0) {
-        Time oldtime = m_Time;
-        m_Time.addMinutes(minutes);
-        if (minutes > 0 && oldtime > m_Time) {
-            numDays++;
-        }
-        else if (minutes < 0 && oldtime < m_Time) {
-            numDays--;
-        }
+    int64 curTicks = m_Time.getTicks();
+    int leapSeconds = 0;
+    if (curTicks >= TicksPerDay) {
+        leapSeconds = int((curTicks - TicksPerDay) / TicksPerSecond) + 1;
+        curTicks -= leapSeconds * TicksPerSecond;
     }
 
-    if (numDays != 0) {
-        m_Date.addDays(numDays);
+    curTicks += minutes * TicksPerMinute;
+    if (curTicks >= TicksPerDay) {
+        curTicks -= TicksPerDay;
+        julianDay++;
     }
+    else if (curTicks < 0) {
+        curTicks += TicksPerDay;
+        julianDay--;
+    }
+
+    if (leapSeconds > 0 && isLeapMinute(julianDay, curTicks)) {
+        curTicks += leapSeconds * TicksPerSecond;
+    }
+
+    m_Date.setJulianDay(julianDay);
+    m_Time.setTicks(curTicks);
 }
 
 

source/libs/magnum_core/source/magnum/util/DateTime.h

 	static const int MillisecondsPerMinute = 60*1000;
 	static const int MillisecondsPerHour = 60*60*1000;
 	static const int MillisecondsPerDay = 24*60*60*1000;
-    static const uint64 TicksPerMillisecond = Time::TicksPerMillisecond;
-    static const uint64 TicksPerSecond = Time::TicksPerSecond;
-	static const uint64 TicksPerDay = Time::TicksPerDay;
+    static const int64 TicksPerMillisecond = Time::TicksPerMillisecond;
+    static const int64 TicksPerSecond = Time::TicksPerSecond;
+    static const int64 TicksPerMinute = Time::TicksPerMinute;
+    static const int64 TicksPerHour = Time::TicksPerHour;
+    static const int64 TicksPerDay = Time::TicksPerDay;
 
     static const DateTime Invalid;
 
         { addDays(-time.getTotalDays()); return *this; }
 
 private:
+    void setAbsolute(int julianDay, int64 ticks);
+
+private:
 	Date m_Date;
 	Time m_Time;
 };

source/libs/magnum_core/source/magnum/util/Time.cpp

 {
     if (!isValid())
         return;
-        
+
     m_Ticks += hours*TicksPerHour;
     normalise();
 }

source/libs/magnum_core/source/magnum/util/Time.h

 class MAGNUM_CORE_API Time {
 STRUCT_METAINFO(Time);
 public:
-	static const uint64	TicksPerMicrosecond = 10ULL;
-	static const uint64	TicksPerMillisecond = 10000ULL;
-	static const uint64	TicksPerSecond = 10000000ULL;
-	static const uint64	TicksPerMinute = 600000000ULL;
-	static const uint64	TicksPerHour = 36000000000ULL;
-	static const uint64	TicksPerDay	= 24LL*36000000000ULL;
+    static const int64	TicksPerMicrosecond = 10LL;
+    static const int64	TicksPerMillisecond = 10000LL;
+    static const int64	TicksPerSecond = 10000000LL;
+    static const int64	TicksPerMinute = 600000000LL;
+    static const int64	TicksPerHour = 36000000000LL;
+    static const int64	TicksPerDay	= 24LL*36000000000LL;
 	static const int MinutesPerHour = 60;
 	static const int SecondsPerMinute = 60;
 	static const int MillisecondsPerSecond = 1000;

source/unittest/util/Test_DateTime.h

     TEST_ASSERT( datetime.getHour() == 23 );
     TEST_ASSERT( datetime.getMinute() == 59 );
     TEST_ASSERT( datetime.getSecond() == 60 );
+
+    datetime.setDate(2012,06,30);
+    datetime.setTime(23,59,60);
+    datetime -= Minutes(1);
+    TEST_ASSERT( datetime.getYear() == 2012 );
+    TEST_ASSERT( datetime.getMonth() == 6 );
+    TEST_ASSERT( datetime.getDayOfMonth() == 30 );
+    TEST_ASSERT( datetime.getHour() == 23 );
+    TEST_ASSERT( datetime.getMinute() == 58 );
+    TEST_ASSERT( datetime.getSecond() == 59 );
+
+    datetime.setDate(2012,06,30);
+    datetime.setTime(23,59,60);
+    datetime += Minutes(1);
+    TEST_ASSERT( datetime.getYear() == 2012 );
+    TEST_ASSERT( datetime.getMonth() == 7 );
+    TEST_ASSERT( datetime.getDayOfMonth() == 1 );
+    TEST_ASSERT( datetime.getHour() == 0 );
+    TEST_ASSERT( datetime.getMinute() == 0 );
+    TEST_ASSERT( datetime.getSecond() == 59 );
 }
 
 

source/unittest/util/Test_Time.h

     TEST_ASSERT( time.getSecond() == 61 );
     TEST_ASSERT( time.getMillisecond() == 0 );
     TEST_ASSERT( time.getLeapSeconds() == 2 );
+
+    time.setTime(23,59,60);
+    time -= Minutes(1);
+    TEST_ASSERT( time.getHour() == 23 );
+    TEST_ASSERT( time.getMinute() == 58 );
+    TEST_ASSERT( time.getSecond() == 59 );
+
+    time.setTime(23,59,60);
+    time += Minutes(1);
+    TEST_ASSERT( time.getHour() == 0 );
+    TEST_ASSERT( time.getMinute() == 0 );
+    TEST_ASSERT( time.getSecond() == 59 );
 }
 
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.