Added DateTime
authorMikko Rasa <tdb@tdb.fi>
Mon, 26 Mar 2007 20:08:11 +0000 (20:08 +0000)
committerMikko Rasa <tdb@tdb.fi>
Mon, 26 Mar 2007 20:08:11 +0000 (20:08 +0000)
Reworked operator<< for TimeDelta

source/time/datetime.cpp [new file with mode: 0644]
source/time/datetime.h [new file with mode: 0644]
source/time/timedelta.cpp
source/time/timedelta.h
source/time/timestamp.h
source/time/utils.cpp
source/time/utils.h

diff --git a/source/time/datetime.cpp b/source/time/datetime.cpp
new file mode 100644 (file)
index 0000000..4fcec96
--- /dev/null
@@ -0,0 +1,267 @@
+/* $Id$ */
+#include <sstream>
+#include <iomanip>
+#include <msp/error.h>
+#include "datetime.h"
+#include "timestamp.h"
+
+using namespace std;
+
+namespace {
+
+inline bool is_leap_year(int32_t y)
+{ return y%4==0 && (y%100 || y%400==0); }
+
+inline uint8_t month_days(int32_t y, uint8_t m)
+{
+       switch(m)
+       {
+       case 4:
+       case 6:
+       case 9:
+       case 11:
+               return 30;
+       case 2:
+               return is_leap_year(y)?29:28;
+       default:
+               return 31;
+       }
+}
+
+template<typename T>
+inline int cmp_(T a, T b)
+{
+       if(a<b)
+               return -1;
+       if(a>b)
+               return 1;
+       return 0;
+}
+
+}
+
+#include <iostream>
+using namespace std;
+
+namespace Msp {
+namespace Time {
+
+DateTime::DateTime(const TimeStamp &ts):
+       year(1970),
+       month(1),
+       mday(1),
+       hour(0),
+       minute(0),
+       second(0),
+       usec(0)
+{
+       add_raw(ts.raw());
+}
+
+DateTime::DateTime(int32_t y, uint8_t m, uint8_t d):
+       year(y),
+       month(m),
+       mday(d),
+       hour(0),
+       minute(0),
+       second(0),
+       usec(0)
+{ }
+
+void DateTime::add_days(int32_t days)
+{
+       unsigned new_year=year;
+
+       /* Leap years have a 400 year cycle, so any 400 consecutive years have a
+       constant number of days */
+       new_year+=days/146097*400;
+       days%=146097;
+
+       if(days<0)
+       {
+               new_year-=400;
+               days+=146097;
+       }
+
+       // Fudge factor for leap day
+       int fudge=(month<=2)?1:0;
+
+       // (Almost) every 4 year cycle has 1 leap year and 3 normal years
+       unsigned cycles=days/1461;
+       days%=1461;
+
+       new_year+=cycles*4;
+
+       // See how many non-leap-years we counted as leap years and reclaim the lost days
+       unsigned missed_leap_days=((year-fudge)%100+cycles*4)/100;
+       if((year-fudge)%400+cycles*4>=400)
+               --missed_leap_days;
+
+       days+=missed_leap_days;
+
+       // Count single years from the 4 year cycle
+       cycles=days/365;
+       days%=365;
+
+       new_year+=cycles;
+
+       if((year-fudge)%4+cycles>=4)
+       {
+               // We passed a leap year - decrement days
+               if(days==0)
+               {
+                       --new_year;
+                       days=is_leap_year(new_year)?365:364;
+               }
+               else
+                       --days;
+       }
+
+       year=new_year;
+
+       // Step months
+       while(mday+days>month_days(year, month))
+       {
+               days-=month_days(year, month);
+               ++month;
+               if(month>12)
+               {
+                       ++year;
+                       month=1;
+               }
+       }
+
+       mday+=days;
+}
+
+DateTime DateTime::operator+(const TimeDelta &td) const
+{
+       DateTime dt(*this);
+       dt.add_raw(td.raw());
+       return dt;
+}
+
+DateTime &DateTime::operator+=(const TimeDelta &td)
+{
+       add_raw(td.raw());
+       return *this;
+}
+
+int DateTime::cmp(const DateTime &dt) const
+{
+       if(int c=cmp_(year, dt.year))
+               return c;
+       if(int c=cmp_(month, dt.month))
+               return c;
+       if(int c=cmp_(mday, dt.mday))
+               return c;
+       if(int c=cmp_(hour, dt.hour))
+               return c;
+       if(int c=cmp_(minute, dt.minute))
+               return c;
+       if(int c=cmp_(second, dt.second))
+               return c;
+       if(int c=cmp_(usec, dt.usec))
+               return c;
+       return 0;
+}
+
+TimeStamp DateTime::get_timestamp() const
+{
+       if(year<-289701 || year>293641)
+               throw Exception("DateTime is not representable as a TimeStamp");
+
+       int64_t raw=(((hour*60LL)+minute)*60+second)*1000000+usec;
+       int days=(year-1970)*365;
+       days+=(year-1)/4-(year-1)/100+(year-1)/400-477;
+       for(unsigned i=1; i<month; ++i)
+               days+=month_days(year, i);
+       days+=mday-1;
+
+       raw+=days*86400000000LL;
+
+       return TimeStamp(raw);
+}
+
+string DateTime::format(const string &fmt) const
+{
+       ostringstream ss;
+       ss.fill('0');
+       for(string::const_iterator i=fmt.begin(); i!=fmt.end(); ++i)
+       {
+               if(*i=='%')
+               {
+                       ++i;
+                       if(i==fmt.end())
+                               break;
+                       else if(*i=='d')
+                               ss<<setw(2)<<int(mday);
+                       else if(*i=='H')
+                               ss<<setw(2)<<int(hour);
+                       else if(*i=='I')
+                               ss<<setw(2)<<hour%12;
+                       else if(*i=='m')
+                               ss<<setw(2)<<int(month);
+                       else if(*i=='M')
+                               ss<<setw(2)<<int(minute);
+                       else if(*i=='p')
+                               ss<<((hour>=12) ? "PM" : "AM");
+                       else if(*i=='S')
+                               ss<<setw(2)<<int(second);
+                       else if(*i=='y')
+                               ss<<setw(2)<<year%100;
+                       else if(*i=='Y')
+                               ss<<year;
+                       else if(*i=='%')
+                               ss<<'%';
+               }
+               else
+                       ss<<*i;
+       }
+
+       return ss.str();
+}
+
+void DateTime::add_raw(int64_t raw)
+{
+       int32_t days=raw/86400000000LL;
+       raw%=86400000000LL;
+       if(raw<0)
+       {
+               ++days;
+               raw+=86400000000LL;
+       }
+
+       usec+=raw%1000000; raw/=1000000;
+       second+=raw%60;    raw/=60;
+       minute+=raw%60;    raw/=60;
+       hour+=raw%24;      raw/=24;
+
+       add_days(days);
+       normalize();
+}
+
+void DateTime::normalize()
+{
+       second+=usec/1000000;
+       usec%=1000000;
+       minute+=second/60;
+       second%=60;
+       hour+=minute/60;
+       minute%=60;
+       mday+=hour/24;
+       hour%=24;
+       while(mday>month_days(year, month))
+       {
+               mday-=month_days(year, month);
+               ++month;
+               if(month>12)
+               {
+                       ++year;
+                       month=1;
+               }
+       }
+}
+
+} // namespace Time
+} // namespace Msp
diff --git a/source/time/datetime.h b/source/time/datetime.h
new file mode 100644 (file)
index 0000000..4f439e6
--- /dev/null
@@ -0,0 +1,70 @@
+/* $Id$ */
+#ifndef MSP_TIME_DATETIME_H_
+#define MSP_TIME_DATETIME_H_
+
+#include <stdint.h>
+#include <string>
+
+namespace Msp {
+namespace Time {
+
+class TimeDelta;
+class TimeStamp;
+
+/**
+Provides handling of arbitary dates and times.  Can represent a moment of time
+in the range of about ±2.1×10⁹ years.  It can also be formatted into a string
+for presentation to the user.
+
+Due to the complex internal representation, arithmetic operations on a DateTime
+are relatively slow.  For purposes of internal scheduling in a program, a
+TimeStamp is a better choice.
+*/
+class DateTime
+{
+public:
+       DateTime(const TimeStamp &);
+       DateTime(int32_t, uint8_t, uint8_t);
+       DateTime(int32_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
+       DateTime(int32_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint32_t);
+       
+       int32_t  get_year() const   { return year; }
+       uint8_t  get_month() const  { return month; }
+       uint8_t  get_mday() const   { return mday; }
+       uint8_t  get_hour() const   { return hour; }
+       uint8_t  get_minute() const { return minute; }
+       uint8_t  get_second() const { return second; }
+       uint32_t get_usec() const   { return usec; }
+
+       void add_days(int32_t);
+
+       DateTime operator+(const TimeDelta &) const;
+       DateTime &operator+=(const TimeDelta &);
+
+       bool operator==(const DateTime &d) const { return cmp(d)==0; }
+       bool operator!=(const DateTime &d) const { return cmp(d)!=0; }
+       bool operator<(const DateTime &d) const { return cmp(d)<0; }
+       bool operator<=(const DateTime &d) const { return cmp(d)<=0; }
+       bool operator>(const DateTime &d) const { return cmp(d)>0; }
+       bool operator>=(const DateTime &d) const { return cmp(d)>=0; }
+
+       int cmp(const DateTime &) const;
+       TimeStamp get_timestamp() const;
+       std::string format(const std::string &) const;
+private:
+       int32_t year;
+       uint8_t month;
+       uint8_t mday;
+       uint8_t hour;
+       uint8_t minute;
+       uint8_t second;
+       uint32_t usec;
+
+       void add_raw(int64_t);
+       void normalize();
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif
index acf9eeffaec66925f6175154883ac33628ebab95..f7b037ea7d154dac0e7b931106fbd2932a1dbbba 100644 (file)
@@ -4,88 +4,72 @@ Copyright © 2006  Mikko Rasa, Mikkosoft Productions
 Distributed under the LGPL
 */
 #include <sstream>
+#include <iomanip>
 #include "timedelta.h"
 #include "units.h"
 
 using namespace std;
 
-namespace Msp {
-namespace Time {
+namespace {
 
 void print_part(ostream &out, int64_t &value, int64_t unit, char sep, bool &first)
 {
-       if(value>=unit || !first)
-       {
-               if(first)
-                       out<<value/unit;
-               else
-               {
-                       out.width(2);
-                       out<<value/unit;
-               }
-               if(sep) out<<sep;
-               value%=unit;
-               first=false;
-       }
+       if(!value || (value<unit && first))
+               return;
+       
+       if(!first)
+               out<<sep<<setw(2);
+       
+       out<<value/unit;
+       value%=unit;
+       first=false;
 }
 
+}
+
+namespace Msp {
+namespace Time {
+
 ostream &operator<<(ostream &out, const TimeDelta &td)
 {
        ostringstream ss;
        ss.fill('0');
-       if(td.usec<1000)
-               ss<<td.usec<<"µs";
-       else if(td.usec<1000000)
+
+       int64_t value=td.raw();
+
+       if(value<0)
        {
-               ss<<td.usec/1000;
-               if(td.usec%1000)
-               {
-                       ss<<'.';
-                       ss.width(3);
-                       ss<<td.usec%1000;
-               }
-               ss<<"ms";
+               ss<<'-';
+               value=-value;
        }
-       else if(td.usec<60000000)
+
+       if(value==0)
+               ss<<'0';
+       else if(value<1000)
+               ss<<value<<"µs";
+       else if(value<1000000)
        {
-               ss<<td.usec/1000000;
-               if(td.usec%1000000)
-               {
-                       ss<<'.';
-                       if(td.usec%1000)
-                       {
-                               ss.width(6);
-                               ss<<td.usec%1000000;
-                       }
-                       else
-                       {
-                               ss.width(3);
-                               ss<<td.usec/1000%1000;
-                       }
-               }
-               ss<<"s";
+               ss<<value/1000;
+               value%=1000;
+               if(value)
+                       ss<<'.'<<setw(3)<<value;
+               ss<<"ms";
        }
        else
        {
-               int64_t temp=td.usec;
-               bool    first=true;
-               print_part(ss, temp, day.raw(),  '-', first);
-               print_part(ss, temp, hour.raw(), ':', first);
-               print_part(ss, temp, min.raw(),  ':', first);
-               print_part(ss, temp, sec.raw(),  0,   first);
-               if(temp)
+               bool first=true;
+               print_part(ss, value, 86400000000LL,  0,  first);
+               print_part(ss, value, 3600000000LL,  '-', first);
+               print_part(ss, value, 60000000LL,    ':', first);
+               print_part(ss, value, 1000000LL,     ':', first);
+
+               if(value)
                {
                        ss<<'.';
-                       if(temp%1000)
-                       {
-                               ss.width(6);
-                               ss<<temp;
-                       }
+                       if(value%1000)
+                               ss<<setw(6)<<value;
                        else
-                       {
-                               ss.width(3);
-                               ss<<temp/1000;
-                       }
+                               ss<<setw(3)<<value/1000;
                }
                ss<<"s";
        }
index 5c5b633abdc9a3d233977df0dcf27e64fe81f5ed..2d4649883088187fffca7994bcb6bcfc2f590397 100644 (file)
@@ -70,8 +70,6 @@ public:
        bool      operator!=(const TimeDelta &t) const { return usec!=t.usec; }
 
        operator bool() const                          { return usec; }
-
-       friend std::ostream &operator<<(std::ostream &, const TimeDelta &);
 private:
        int64_t usec;
 };
@@ -79,6 +77,8 @@ private:
 template<typename T>
 inline TimeDelta operator*(T a, const TimeDelta &t)   { return t*a; }
 
+extern std::ostream &operator<<(std::ostream &, const TimeDelta &);
+
 } // namespace Time
 } // namespace Msp
 
index 984333da7a357edb51d9464c9b01baa890e5739b..1e1b275ab97a799cf8450e7c99ecb6472d37b1ed 100644 (file)
@@ -13,9 +13,10 @@ namespace Msp {
 namespace Time {
 
 /**
-Represents a moment in time, such as the last tick.  This class is NOT intended
-to be used for storing arbitary user-defined times, I'll add a DateTime class
-if the need arises.
+Represents a moment in time.  The main source of TimeStamps is the now()
+function.
+
+For representing user-specified times, use the DateTime class.
 */
 class TimeStamp
 {
@@ -33,8 +34,8 @@ public:
        explicit TimeStamp(int64_t u): usec(u) { }
 
        /**
-       Returns the raw number stored inside the TimeStamp.  This should only be used
-       for serialization and the result should not be interpreted in any way.
+       Returns the raw number stored inside the TimeStamp.  This value should be
+       considered opaque and only be used for serialization.
        */
        int64_t   raw() const { return usec; }
 
index d6b63328a32d32a3519da8f6dd0bd20b093e602e..ad0d24550338f7c22117057a2354d42f93e6c09e 100644 (file)
@@ -9,11 +9,14 @@ Distributed under the LGPL
 #include <sys/resource.h>
 #include <sys/time.h>
 #endif
+#include "datetime.h"
 #include "timedelta.h"
 #include "timestamp.h"
 #include "units.h"
 #include "utils.h"
 
+using namespace std;
+
 namespace Msp {
 namespace Time {
 
@@ -50,6 +53,11 @@ TimeStamp now()
 #endif
 }
 
+string format_now(const string &fmt)
+{
+       return DateTime(now()).format(fmt);
+}
+
 /**
 Returns the CPU time used by the program so far.
 */
index e16cb65649e7e0bac017035b83ef2435a6eb674e..bc04bad8a35e14c88adfbd201ca430eb9b57229a 100644 (file)
@@ -13,6 +13,7 @@ class TimeDelta;
 class TimeStamp;
 
 extern TimeStamp now();
+extern std::string format_now(const std::string &);
 extern TimeDelta get_cpu_time();
 extern int sleep(const TimeDelta &);