]> git.tdb.fi Git - libs/core.git/commitdiff
Add support for time zones
authorMikko Rasa <tdb@tdb.fi>
Mon, 15 Dec 2008 11:33:48 +0000 (11:33 +0000)
committerMikko Rasa <tdb@tdb.fi>
Mon, 15 Dec 2008 11:33:48 +0000 (11:33 +0000)
Fix bugs in DateTime addition operations
Add subtraction operators for DateTime

source/time/datetime.cpp
source/time/datetime.h
source/time/timezone.cpp [new file with mode: 0644]
source/time/timezone.h [new file with mode: 0644]

index 2d7c19585cefeea0838b045643a9b9aaac588db7..283a7c6c7578c73484224e27615f92fe78d7ee79 100644 (file)
@@ -1,9 +1,11 @@
 /* $Id$ */
+#include <cstdlib>
 #include <sstream>
 #include <iomanip>
 #include "../core/except.h"
 #include "datetime.h"
 #include "timestamp.h"
+#include "units.h"
 
 using namespace std;
 
@@ -43,57 +45,35 @@ inline int cmp_(T a, T b)
 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(int y, unsigned char m, unsigned char d):
-       year(y),
-       month(m),
-       mday(d),
-       hour(0),
-       minute(0),
-       second(0),
-       usec(0)
+DateTime::DateTime(const TimeStamp &ts, const TimeZone &tz)
 {
-       validate();
+       init(ts);
+       convert_timezone(tz);
 }
 
-DateTime::DateTime(int y, unsigned char m, unsigned char d, unsigned char h, unsigned char n, unsigned char s):
-       year(y),
-       month(m),
-       mday(d),
-       hour(h),
-       minute(n),
-       second(s),
-       usec(0)
+DateTime::DateTime(int y, unsigned char m, unsigned char d)
 {
-       validate();
+       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, unsigned u):
-       year(y),
-       month(m),
-       mday(d),
-       hour(h),
-       minute(n),
-       second(s),
-       usec(u)
+DateTime::DateTime(int y, unsigned char m, unsigned char d, unsigned char h, unsigned char n, unsigned char s)
 {
-       validate();
+       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);
 }
 
 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 (400*365+97=146097) */
@@ -116,6 +96,7 @@ void DateTime::add_days(int 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;
@@ -133,8 +114,8 @@ void DateTime::add_days(int 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;
@@ -157,6 +138,17 @@ void DateTime::add_days(int 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);
@@ -170,6 +162,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))
@@ -234,7 +239,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<<'%';
                }
@@ -245,13 +250,53 @@ string DateTime::format(const string &fmt) const
        return ss.str();
 }
 
+string DateTime::format_rfc3339() const
+{
+       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;
        }
 
index 57ca89d8280535eea94efa301f750308688f9196..5f9e8c65706394f102cec45aa9c437d9db363864 100644 (file)
@@ -3,6 +3,7 @@
 #define MSP_TIME_DATETIME_H_
 
 #include <string>
+#include "timezone.h"
 #include "types.h"
 
 namespace Msp {
@@ -24,6 +25,7 @@ class DateTime
 {
 public:
        DateTime(const TimeStamp &);
+       DateTime(const TimeStamp &, const TimeZone &);
        DateTime(int, unsigned char, unsigned char);
        DateTime(int, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char);
        DateTime(int, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned);
@@ -37,9 +39,13 @@ public:
        unsigned      get_usec() const   { return usec; }
 
        void add_days(int);
+       void set_timezone(const TimeZone &);
+       void convert_timezone(const TimeZone &);
 
        DateTime operator+(const TimeDelta &) const;
        DateTime &operator+=(const TimeDelta &);
+       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; }
@@ -51,6 +57,7 @@ public:
        int cmp(const DateTime &) const;
        TimeStamp get_timestamp() const;
        std::string format(const std::string &) const;
+       std::string format_rfc3339() const;
 private:
        int           year;
        unsigned char month;
@@ -59,7 +66,10 @@ private:
        unsigned char minute;
        unsigned char second;
        unsigned      usec;
+       TimeZone      zone;
 
+       void init(const TimeStamp &);
+       void init(int, unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, unsigned);
        void add_raw(RawTime);
        void normalize();
        void validate() const;
diff --git a/source/time/timezone.cpp b/source/time/timezone.cpp
new file mode 100644 (file)
index 0000000..fd2487b
--- /dev/null
@@ -0,0 +1,80 @@
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2008  Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <cstdlib>
+#include <sstream>
+#include <iomanip>
+#include <time.h>
+#include "timezone.h"
+#include "units.h"
+
+using namespace std;
+
+namespace {
+
+using Msp::Time::TimeZone;
+
+TimeZone get_local_timezone()
+{
+#ifdef WIN32
+       TIME_ZONE_INFORMATION tzinfo;
+       DWORD dst=GetTimeZoneInformation(&tzinfo);
+       if(dst==TIME_ZONE_ID_INVALID)
+               throw SystemError("Failed to get time zone information", GetLastError());
+
+       int offset=tzinfo.Bias;
+       if(dst==TIME_ZONE_ID_STANDARD)
+               offset+=tzinfo.StandardBias;
+       else if(dst==TIME_ZONE_ID_DAYLIGHT)
+               offset+=tzinfo.DaylightBias;
+
+       return TimeZone(offset);
+#else
+       tzset();
+       return TimeZone(timezone/60);
+#endif
+}
+
+}
+
+namespace Msp {
+namespace Time {
+
+TimeZone::TimeZone():
+       name("UTC")
+{ }
+
+TimeZone::TimeZone(int minutes_west):
+       offset(minutes_west*min)
+{
+       if(minutes_west)
+       {
+               ostringstream ss;
+               ss.fill('0');
+               int m=abs(minutes_west);
+               ss<<"UTC"<<(minutes_west<0 ? '-' : '+')<<m/60;
+               if(m%60)
+                       ss<<':'<<setw(2)<<m%60;
+       }
+       else
+               name="UTC";
+}
+
+const TimeZone &TimeZone::utc()
+{
+       static TimeZone tz(0);
+       return tz;
+}
+
+const TimeZone &TimeZone::local()
+{
+       static TimeZone tz=get_local_timezone();
+       return tz;
+}
+
+} // namespace Time
+} // namespace Msp
diff --git a/source/time/timezone.h b/source/time/timezone.h
new file mode 100644 (file)
index 0000000..699801d
--- /dev/null
@@ -0,0 +1,36 @@
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2008  Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_TIME_TIMEZONE_H_
+#define MSP_TIME_TIMEZONE_H_
+
+#include "timedelta.h"
+
+namespace Msp {
+namespace Time {
+
+class TimeZone
+{
+private:
+       std::string name;
+       TimeDelta offset;
+
+public:
+       TimeZone();
+       TimeZone(int);
+       
+       const std::string &get_name() const { return name; }
+       const TimeDelta &get_offset() const { return offset; }
+
+       static const TimeZone &utc();
+       static const TimeZone &local();
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif