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");
diff --git a/test/TestISO8601.cxx b/test/TestISO8601.cxx
index 6fdc11dc8..e97798831 100644
--- a/test/TestISO8601.cxx
+++ b/test/TestISO8601.cxx
@@ -57,6 +57,12 @@ static constexpr struct {
 
 	/* without time zone */
 	{ "2019-02-04T16:46:41", 1549298801, std::chrono::seconds(1) },
+
+	/* with time zone */
+	{ "2019-02-04T16:46:41+02", 1549291601, std::chrono::seconds(1) },
+	{ "2019-02-04T16:46:41+0200", 1549291601, std::chrono::seconds(1) },
+	{ "2019-02-04T16:46:41+02:00", 1549291601, std::chrono::seconds(1) },
+	{ "2019-02-04T16:46:41-0200", 1549306001, std::chrono::seconds(1) },
 };
 
 TEST(ISO8601, Parse)