Fix use of Z as a timezone (meaning UTC).
[libdcp.git] / src / local_time.cc
index 727583e5eaf1c1af47f1c0cd52dfa13ebb1fc311..f125b38f6cb12011cc8cd48926f8ab943e924fa6 100644 (file)
@@ -124,43 +124,19 @@ LocalTime::set_local_time_zone ()
 
 LocalTime::LocalTime (string s)
 {
-       /* 2013-01-05T18:06:59 or 2013-01-05T18:06:59.123 or 2013-01-05T18:06:59+04:00 or 2013-01-05T18:06:59.123+04:00 */
-       /* 0123456789012345678 or 01234567890123456789012 or 0123456789012345678901234 or 01234567890123456789012345678 */
+       /* 2013-01-05T18:06:59[.frac][TZ]
+        * Where .frac is fractional seconds
+        *       TZ is something like +04:00
+        */
 
        if (s.length() < 19) {
                throw TimeFormatError (s);
        }
 
-       bool with_millisecond = false;
-       bool with_tz = false;
-
-       switch (s.length ()) {
-       case 19:
-               break;
-       case 23:
-               with_millisecond = true;
-               break;
-       case 25:
-               with_tz = true;
-               break;
-       case 29:
-               with_millisecond = with_tz = true;
-               break;
-       default:
-               throw TimeFormatError (s);
-       }
-
-       int const tz_pos = with_millisecond ? 23 : 19;
+       /* Date and time with whole seconds */
 
-       /* Check incidental characters */
        if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':') {
-               throw TimeFormatError (s);
-       }
-       if (with_millisecond && s[19] != '.') {
-               throw TimeFormatError (s);
-       }
-       if (with_tz && s[tz_pos] != '+' && s[tz_pos] != '-') {
-               throw TimeFormatError (s);
+               throw TimeFormatError(s);
        }
 
        _year = lexical_cast<int>(s.substr(0, 4));
@@ -169,27 +145,64 @@ LocalTime::LocalTime (string s)
        _hour = lexical_cast<int>(s.substr(11, 2));
        _minute = lexical_cast<int>(s.substr(14, 2));
        _second = lexical_cast<int>(s.substr(17, 2));
-       _millisecond = with_millisecond ? lexical_cast<int>(s.substr(20, 3)) : 0;
 
-       _offset.set_hour(with_tz ? lexical_cast<int>(s.substr(tz_pos + 1, 2)) : 0);
-       _offset.set_minute(with_tz ? lexical_cast<int>(s.substr(tz_pos + 4, 2)) : 0);
+       size_t pos = 19;
+
+       /* Fractional seconds */
+       if (s.length() > pos && s[pos] == '.') {
+               auto end = s.find('+', pos);
+               if (end == std::string::npos) {
+                       end = s.find('-', pos);
+               }
+               if (end == std::string::npos) {
+                       end = s.length();
+               }
+               auto const length = end - pos;
+               _millisecond = lexical_cast<int>(s.substr(pos + 1, std::min(static_cast<size_t>(3), length - 1)));
+               pos = end;
+       } else {
+               _millisecond = 0;
+       }
 
-       if (with_tz && s[tz_pos] == '-') {
-               _offset.set_hour(-_offset.hour());
-               _offset.set_minute(-_offset.minute());
+       /* Timezone */
+       if (pos != s.length() && s[pos] != 'Z') {
+               if (s[pos] != '+' && s[pos] != '-') {
+                       throw TimeFormatError(s);
+               }
+               if ((s.length() - pos) != 6) {
+                       throw TimeFormatError(s);
+               }
+
+               _offset.set_hour(lexical_cast<int>(s.substr(pos + 1, 2)));
+               _offset.set_minute(lexical_cast<int>(s.substr(pos + 4, 2)));
+
+               if (s[pos] == '-') {
+                       _offset.set_hour(-_offset.hour());
+                       _offset.set_minute(-_offset.minute());
+               }
        }
 }
 
 
 string
-LocalTime::as_string (bool with_millisecond) const
+LocalTime::as_string(bool with_millisecond, bool with_timezone) const
 {
        char buffer[32];
-       snprintf (
+
+       auto const written = snprintf(
                buffer, sizeof (buffer),
-               "%sT%s%s%02d:%02d",
-               date().c_str(), time_of_day(true, with_millisecond).c_str(), (_offset.hour() >= 0 ? "+" : "-"), abs(_offset.hour()), abs(_offset.minute())
+               "%sT%s",
+               date().c_str(), time_of_day(true, with_millisecond).c_str()
                );
+
+       DCP_ASSERT(written < 32);
+
+       if (with_timezone) {
+               snprintf(
+                       buffer + written, sizeof(buffer) - written,
+                       "%s%02d:%02d", (_offset.hour() >= 0 ? "+" : "-"), abs(_offset.hour()), abs(_offset.minute())
+                       );
+       }
        return buffer;
 }
 
@@ -272,57 +285,82 @@ LocalTime::add_minutes (int m)
 bool
 LocalTime::operator== (LocalTime const & other) const
 {
-       return _year == other._year && _month == other._month && _day == other._day &&
-               _hour == other._hour && _second == other._second && _millisecond == other._millisecond &&
-               _offset == other._offset;
+       auto a = as_utc();
+       auto b = other.as_utc();
+
+       return a.year() == b.year() && a.month() == b.month() && a.day() == b.day() &&
+               a.hour() == b.hour() && a.minute() == b.minute() && a.second() == b.second() && a.millisecond() == b.millisecond();
 }
 
 
 bool
 LocalTime::operator< (LocalTime const & other) const
 {
-       DCP_ASSERT(_offset == other._offset);
+       auto a = as_utc();
+       auto b = other.as_utc();
 
-       if (_year != other._year) {
-               return _year < other._year;
+       if (a.year() != b.year()) {
+               return a.year() < b.year();
        }
-       if (_month != other._month) {
-               return _month < other._month;
+       if (a.month() != b.month()) {
+               return a.month() < b.month();
        }
-       if (_day != other._day) {
-               return _day < other._day;
+       if (a.day() != b.day()) {
+               return a.day() < b.day();
        }
-       if (_hour != other._hour) {
-               return _hour < other._hour;
+       if (a.hour() != b.hour()) {
+               return a.hour() < b.hour();
        }
-       if (_second != other._second) {
-               return _second < other._second;
+       if (a.minute() != b.minute()) {
+               return a.minute() < other.minute();
        }
-       return _millisecond < other._millisecond;
+       if (a.second() != b.second()) {
+               return a.second() < b.second();
+       }
+       return a.millisecond() < b.millisecond();
 }
 
 
+bool
+LocalTime::operator<=(LocalTime const& other) const
+{
+       return *this < other || *this == other;
+}
+
+
+
 bool
 LocalTime::operator>(LocalTime const & other) const
 {
-       DCP_ASSERT(_offset == other._offset);
+       auto a = as_utc();
+       auto b = other.as_utc();
 
-       if (_year != other._year) {
-               return _year > other._year;
+       if (a.year() != b.year()) {
+               return a.year() > b.year();
+       }
+       if (a.month() != b.month()) {
+               return a.month() > b.month();
        }
-       if (_month != other._month) {
-               return _month > other._month;
+       if (a.day() != b.day()) {
+               return a.day() > b.day();
        }
-       if (_day != other._day) {
-               return _day > other._day;
+       if (a.hour() != b.hour()) {
+               return a.hour() > b.hour();
        }
-       if (_hour != other._hour) {
-               return _hour > other._hour;
+       if (a.minute() != b.minute()) {
+               return a.minute() > b.minute();
        }
-       if (_second != other._second) {
-               return _second > other._second;
+       if (a.second() != b.second()) {
+               return a.second() > b.second();
        }
-       return _millisecond > other._millisecond;
+       return a.millisecond() > b.millisecond();
+}
+
+
+bool
+LocalTime::operator>=(LocalTime const& other) const
+{
+       return *this > other || *this == other;
 }
 
 
@@ -372,3 +410,11 @@ LocalTime::from_asn1_generalized_time (string time)
 }
 
 
+LocalTime
+LocalTime::as_utc() const
+{
+       auto t = *this;
+       t.add(boost::posix_time::time_duration(-_offset.hour(), -_offset.minute(), 0));
+       return t;
+}
+