]> git.tdb.fi Git - libs/core.git/blobdiff - source/time/datetime.cpp
Make to_unixtime const
[libs/core.git] / source / time / datetime.cpp
index 4fcec9641f4bf23a2ac25c4c17c5f23c24bd7bd9..86238863282340996339071236f51b4a212e193f 100644 (file)
@@ -1,18 +1,26 @@
-/* $Id$ */
+/* $Id$
+
+This file is part of libmspcore     
+Copyright © 2006  Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <cstdlib>
 #include <sstream>
 #include <iomanip>
-#include <msp/error.h>
+#include "../core/except.h"
 #include "datetime.h"
 #include "timestamp.h"
+#include "units.h"
 
 using namespace std;
 
 namespace {
 
-inline bool is_leap_year(int32_t y)
+inline bool is_leap_year(int y)
 { return y%4==0 && (y%100 || y%400==0); }
 
-inline uint8_t month_days(int32_t y, uint8_t m)
+inline unsigned char month_days(int y, unsigned char m)
 {
        switch(m)
        {
@@ -40,40 +48,41 @@ inline int cmp_(T a, T b)
 
 }
 
-#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)
+DateTime::DateTime(const TimeStamp &ts)
 {
-       add_raw(ts.raw());
+       init(ts);
+}
+
+DateTime::DateTime(const TimeStamp &ts, const TimeZone &tz)
+{
+       init(ts);
+       convert_timezone(tz);
+}
+
+DateTime::DateTime(int y, unsigned char m, unsigned char d)
+{
+       init(y, m, d, 0, 0, 0, 0);
+}
+
+DateTime::DateTime(int y, unsigned char m, unsigned char d, unsigned char h, unsigned char n, unsigned char s)
+{
+       init(y, m, d, h, n, s, 0);
+}
+
+DateTime::DateTime(int y, unsigned char m, unsigned char d, unsigned char h, unsigned char n, unsigned char s, unsigned u)
+{
+       init(y, m, d, h, n, s, u);
 }
 
-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)
+void DateTime::add_days(int days)
 {
-       unsigned new_year=year;
+       int new_year=year;
 
        /* Leap years have a 400 year cycle, so any 400 consecutive years have a
-       constant number of days */
+       constant number of days (400*365+97=146097) */
        new_year+=days/146097*400;
        days%=146097;
 
@@ -93,6 +102,7 @@ void DateTime::add_days(int32_t days)
        new_year+=cycles*4;
 
        // See how many non-leap-years we counted as leap years and reclaim the lost days
+       // XXX This breaks with negative years
        unsigned missed_leap_days=((year-fudge)%100+cycles*4)/100;
        if((year-fudge)%400+cycles*4>=400)
                --missed_leap_days;
@@ -110,8 +120,8 @@ void DateTime::add_days(int32_t days)
                // We passed a leap year - decrement days
                if(days==0)
                {
+                       days=is_leap_year(new_year-fudge)?365:364;
                        --new_year;
-                       days=is_leap_year(new_year)?365:364;
                }
                else
                        --days;
@@ -134,6 +144,17 @@ void DateTime::add_days(int32_t days)
        mday+=days;
 }
 
+void DateTime::set_timezone(const TimeZone &tz)
+{
+       zone=tz;
+}
+
+void DateTime::convert_timezone(const TimeZone &tz)
+{
+       add_raw((zone.get_offset()-tz.get_offset()).raw());
+       zone=tz;
+}
+
 DateTime DateTime::operator+(const TimeDelta &td) const
 {
        DateTime dt(*this);
@@ -147,6 +168,19 @@ DateTime &DateTime::operator+=(const TimeDelta &td)
        return *this;
 }
 
+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))
@@ -169,9 +203,9 @@ int DateTime::cmp(const DateTime &dt) const
 TimeStamp DateTime::get_timestamp() const
 {
        if(year<-289701 || year>293641)
-               throw Exception("DateTime is not representable as a TimeStamp");
+               throw InvalidState("DateTime is not representable as a TimeStamp");
 
-       int64_t raw=(((hour*60LL)+minute)*60+second)*1000000+usec;
+       RawTime 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)
@@ -211,7 +245,7 @@ string DateTime::format(const string &fmt) const
                        else if(*i=='y')
                                ss<<setw(2)<<year%100;
                        else if(*i=='Y')
-                               ss<<year;
+                               ss<<setw(4)<<internal<<year;
                        else if(*i=='%')
                                ss<<'%';
                }
@@ -222,13 +256,53 @@ string DateTime::format(const string &fmt) const
        return ss.str();
 }
 
-void DateTime::add_raw(int64_t raw)
+string DateTime::format_rfc3339() const
 {
-       int32_t days=raw/86400000000LL;
+       string result=format("%Y-%m-%dT%H:%M:%S");
+       if(const TimeDelta &offs=zone.get_offset())
+       {
+               ostringstream ss;
+               ss.fill('0');
+               int m=abs(static_cast<int>(offs/Time::min));
+               ss<<(offs<zero ? '+' : '-')<<setw(2)<<m/60<<':'<<setw(2)<<m%60;
+               result+=ss.str();
+       }
+       else
+               result+='Z';
+       return result;
+}
+
+void DateTime::init(const TimeStamp &ts)
+{
+       year=1970;
+       month=1;
+       mday=1;
+       hour=0;
+       minute=0;
+       second=0;
+       usec=0;
+       add_raw(ts.raw());
+}
+
+void DateTime::init(int y, unsigned char m, unsigned char d, unsigned char h, unsigned char n, unsigned char s, unsigned u)
+{
+       year=y;
+       month=m;
+       mday=d;
+       hour=h;
+       minute=n;
+       second=s;
+       usec=u;
+       validate();
+}
+
+void DateTime::add_raw(RawTime raw)
+{
+       int days=static_cast<int>(raw/86400000000LL);
        raw%=86400000000LL;
        if(raw<0)
        {
-               ++days;
+               --days;
                raw+=86400000000LL;
        }
 
@@ -263,5 +337,21 @@ void DateTime::normalize()
        }
 }
 
+void DateTime::validate() const
+{
+       if(usec>=1000000)
+               throw InvalidParameterValue("Microseconds out of range");
+       if(second>=60)
+               throw InvalidParameterValue("Seconds out of range");
+       if(minute>=60)
+               throw InvalidParameterValue("Minutes out of range");
+       if(hour>=24)
+               throw InvalidParameterValue("Hours out of range");
+       if(month<1 || month>12)
+               throw InvalidParameterValue("Month out of range");
+       if(mday<1 || mday>month_days(year, month))
+               throw InvalidParameterValue("Day of month out of range");
+}
+
 } // namespace Time
 } // namespace Msp