diff --git a/map/bookmark.cpp b/map/bookmark.cpp index 7a892d0b1a..95c1cd3499 100644 --- a/map/bookmark.cpp +++ b/map/bookmark.cpp @@ -15,6 +15,68 @@ #include "../std/algorithm.hpp" +//////////////////////////////////////////////////////// +time_t const Bookmark::INVALID_TIME_STAMP = static_cast(-1); + +/// @name Helper functions to convert kml timestamps +//@{ + +/// Always creates strings in UTC time: 1997-07-16T07:30:15Z +/// Returns empty string on error +string TimestampToString(time_t time) +{ + struct tm * t = gmtime(&time); + char buf[21] = { 0 }; + ::snprintf(buf, ARRAY_SIZE(buf), "%04d-%02d-%02dT%02d:%02d:%02dZ", t->tm_year + 1900, + t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + return buf; +} + +/// Accepts strings in UTC format: 1997-07-16T07:30:15Z +/// And with custom time offset: 1997-07-16T10:30:15+03:00 +/// @return INVALID_TIME_STAMP if string is invalid +time_t StringToTimestamp(string const & timeStr) +{ + struct tm t; + memset(&t, 0, sizeof(t)); + // Return current time in the case of failure + time_t res = Bookmark::INVALID_TIME_STAMP; + // Parse UTC format + if (timeStr.size() == 20) + { + if (6 == sscanf(timeStr.c_str(), "%4d-%2d-%2dT%2d:%2d:%2dZ", &t.tm_year, + &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec)) + { + t.tm_year -= 1900; + t.tm_mon -= 1; + res = timegm(&t); + } + } + else if (timeStr.size() == 25) + { + // Parse custom time zone offset format + char sign; + int tzHours, tzMinutes; + if (9 == sscanf(timeStr.c_str(), "%4d-%2d-%2dT%2d:%2d:%2d%c%2d:%2d", &t.tm_year, + &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, + &sign, &tzHours, &tzMinutes)) + { + t.tm_year -= 1900; + t.tm_mon -= 1; + time_t const tt = timegm(&t); + // Fix timezone offset + if (sign == '-') + res = tt + tzHours * 3600 + tzMinutes * 60; + else if (sign == '+') + res = tt - tzHours * 3600 - tzMinutes * 60; + } + } + + return res; +} + +//@} + void BookmarkCategory::AddBookmarkImpl(Bookmark const & bm, double scale) { Bookmark * p = new Bookmark(bm); diff --git a/map/bookmark.hpp b/map/bookmark.hpp index 8df2e45d87..932c3d2922 100644 --- a/map/bookmark.hpp +++ b/map/bookmark.hpp @@ -8,6 +8,7 @@ #include "../std/string.hpp" #include "../std/noncopyable.hpp" #include "../std/iostream.hpp" +#include "../std/ctime.hpp" class Bookmark @@ -33,6 +34,8 @@ public: string const & GetType() const { return m_type; } m2::RectD GetViewport() const { return m2::RectD(m_org, m_org); } + static time_t const INVALID_TIME_STAMP; + double GetScale() const { return m_scale; } void SetScale(double scale) { m_scale = scale; } }; diff --git a/map/map_tests/bookmarks_test.cpp b/map/map_tests/bookmarks_test.cpp index c95f8b0948..0f284a7f3c 100644 --- a/map/map_tests/bookmarks_test.cpp +++ b/map/map_tests/bookmarks_test.cpp @@ -9,6 +9,10 @@ #include "../../std/fstream.hpp" +// Prototypes for private functions in bookmarks.cpp +string TimestampToString(time_t time); +time_t StringToTimestamp(string const & timeStr); + namespace { char const * kmlString = @@ -373,3 +377,21 @@ UNIT_TEST(Bookmarks_AddingMoving) DeleteCategoryFiles(); } + +UNIT_TEST(TimestampConversion) +{ + TEST_EQUAL(TimestampToString(0), "1970-01-01T00:00:00Z", ()); + TEST_EQUAL(TimestampToString(1354482514), "2012-12-02T21:08:34Z", ()); + TEST_EQUAL(StringToTimestamp("1970-01-01T00:00:00Z"), 0, ()); + TEST_EQUAL(StringToTimestamp("2012-12-02T21:08:34Z"), 1354482514, ()); + TEST_EQUAL(StringToTimestamp("2012-12-03T00:38:34+03:30"), 1354482514, ()); + TEST_EQUAL(StringToTimestamp("2012-12-02T11:08:34-10:00"), 1354482514, ()); + + time_t now = time(0); + TEST_EQUAL(now, StringToTimestamp(TimestampToString(now)), ()); + + TEST_EQUAL(Bookmark::INVALID_TIME_STAMP, StringToTimestamp("asd23423adsfbhj657"), ()); + TEST_EQUAL(Bookmark::INVALID_TIME_STAMP, StringToTimestamp("2012-aa-02T21:08:34Z"), ()); + TEST_EQUAL(Bookmark::INVALID_TIME_STAMP, StringToTimestamp("2012-12-0ZT21:08:34Z"), ()); + TEST_EQUAL(Bookmark::INVALID_TIME_STAMP, StringToTimestamp("2012:12:02T21-08-34Z"), ()); +}