summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2019-08-19 22:38:06 +0200
committerMax Kellermann <max@musicpd.org>2019-12-16 23:24:43 +0100
commit7d8b1860c315ca403560385d6b426877f2e8e46b (patch)
tree2e454c11fddac5322bfc516ed0a1cb2a905962c1 /src
parentb06825829bbc71876dc8ba2c3d7acdc96e9f208e (diff)
time/ISO8601: support time zone offset
Diffstat (limited to 'src')
-rw-r--r--src/time/ISO8601.cxx53
1 files changed, 53 insertions, 0 deletions
diff --git a/src/time/ISO8601.cxx b/src/time/ISO8601.cxx
index f08938e4e..6671a8ca4 100644
--- a/src/time/ISO8601.cxx
+++ b/src/time/ISO8601.cxx
@@ -58,6 +58,56 @@ FormatISO8601(std::chrono::system_clock::time_point tp)
return FormatISO8601(GmTime(tp));
}
+static std::pair<unsigned, unsigned>
+ParseTimeZoneOffsetRaw(const char *&s)
+{
+ char *endptr;
+ unsigned long value = strtoul(s, &endptr, 10);
+ if (endptr == s + 4) {
+ s = endptr;
+ return std::make_pair(value / 100, value % 100);
+ } else if (endptr == s + 2) {
+ s = endptr;
+
+ unsigned hours = value, minutes = 0;
+ if (*s == ':') {
+ ++s;
+ minutes = strtoul(s, &endptr, 10);
+ if (endptr != s + 2)
+ throw std::runtime_error("Failed to parse time zone offset");
+
+ s = endptr;
+ }
+
+ return std::make_pair(hours, minutes);
+ } else
+ throw std::runtime_error("Failed to parse time zone offset");
+}
+
+static std::chrono::system_clock::duration
+ParseTimeZoneOffset(const char *&s)
+{
+ assert(*s == '+' || *s == '-');
+
+ bool negative = *s == '-';
+ ++s;
+
+ auto raw = ParseTimeZoneOffsetRaw(s);
+ if (raw.first > 13)
+ throw std::runtime_error("Time offset hours out of range");
+
+ if (raw.second >= 60)
+ throw std::runtime_error("Time offset minutes out of range");
+
+ std::chrono::system_clock::duration d = std::chrono::hours(raw.first);
+ d += std::chrono::minutes(raw.second);
+
+ if (negative)
+ d = -d;
+
+ return d;
+}
+
std::pair<std::chrono::system_clock::time_point,
std::chrono::system_clock::duration>
ParseISO8601(const char *s)
@@ -93,8 +143,11 @@ ParseISO8601(const char *s)
auto tp = TimeGm(tm);
+ /* time zone */
if (*s == 'Z')
++s;
+ else if (*s == '+' || *s == '-')
+ tp -= ParseTimeZoneOffset(s);
if (*s != 0)
throw std::runtime_error("Garbage at end of time stamp");