diff --git a/base/base_tests/timer_test.cpp b/base/base_tests/timer_test.cpp index a9e191cad7..f1b3e4f98c 100644 --- a/base/base_tests/timer_test.cpp +++ b/base/base_tests/timer_test.cpp @@ -30,3 +30,23 @@ UNIT_TEST(Timer_CurrentStringTime) { LOG(LINFO, (my::FormatCurrentTime())); } + +UNIT_TEST(Timer_TimestampConversion) +{ + using namespace my; + + 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(INVALID_TIME_STAMP, StringToTimestamp("asd23423adsfbhj657"), ()); + TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012-aa-02T21:08:34Z"), ()); + TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012-12-0ZT21:08:34Z"), ()); + TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012:12:02T21-08-34Z"), ()); +} diff --git a/base/timer.cpp b/base/timer.cpp index 50935d8a7b..1a93925b88 100644 --- a/base/timer.cpp +++ b/base/timer.cpp @@ -1,8 +1,10 @@ #include "timer.hpp" +#include "assert.hpp" +#include "macros.hpp" #include "../std/target_os.hpp" #include "../std/systime.hpp" -#include "../std/ctime.hpp" + namespace my { @@ -41,14 +43,14 @@ void Timer::Reset() string FormatCurrentTime() { - time_t t = time(NULL); - string s = string(ctime(&t)); + time_t t = time(NULL); + string s(ctime(&t)); - for (size_t i = 0; i < s.size(); ++i) - if (s[i] == ' ') s[i] = '_'; + replace(s.begin(), s.end(), ' ', '_'); - s.erase(s.size() - 1, 1); - return s; + ASSERT_GREATER(s.size(), 1, ()); + s.resize(s.size() - 1); + return s; } uint32_t TodayAsYYMMDD() @@ -58,4 +60,76 @@ uint32_t TodayAsYYMMDD() return (pTm->tm_year - 100) * 10000 + (pTm->tm_mon + 1) * 100 + pTm->tm_mday; } +namespace +{ + time_t my_timegm(tm * tm) + { +#ifdef OMIM_OS_ANDROID + char * tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + + time_t ret = mktime(tm); + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + tzset(); + + return ret; +#else + return timegm(tm); +#endif + } +} + +string TimestampToString(time_t time) +{ + 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; +} + +time_t StringToTimestamp(string const & s) +{ + tm t; + memset(&t, 0, sizeof(t)); + // Return current time in the case of failure + time_t res = INVALID_TIME_STAMP; + // Parse UTC format + if (s.size() == 20) + { + if (6 == sscanf(s.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 = my_timegm(&t); + } + } + else if (s.size() == 25) + { + // Parse custom time zone offset format + char sign; + int tzHours, tzMinutes; + if (9 == sscanf(s.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 = my_timegm(&t); + // Fix timezone offset + if (sign == '-') + res = tt + tzHours * 3600 + tzMinutes * 60; + else if (sign == '+') + res = tt - tzHours * 3600 - tzMinutes * 60; + } + } + + return res; +} + } diff --git a/base/timer.hpp b/base/timer.hpp index af6cb32272..6521d4fed0 100644 --- a/base/timer.hpp +++ b/base/timer.hpp @@ -2,7 +2,8 @@ #include "../std/stdint.hpp" #include "../std/string.hpp" -#include "../std/stdint.hpp" +#include "../std/ctime.hpp" + namespace my { @@ -25,4 +26,15 @@ public: string FormatCurrentTime(); uint32_t TodayAsYYMMDD(); +/// Always creates strings in UTC time: 1997-07-16T07:30:15Z +/// Returns empty string on error +string TimestampToString(time_t time); + +time_t const INVALID_TIME_STAMP = -1; + +/// 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 & s); + } diff --git a/map/bookmark.cpp b/map/bookmark.cpp index 4fbcac0c2f..e64f552fc4 100644 --- a/map/bookmark.cpp +++ b/map/bookmark.cpp @@ -15,68 +15,6 @@ #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); @@ -205,7 +143,7 @@ namespace bookmark_impl m_org = m2::PointD(-1000, -1000); m_type.clear(); m_scale = -1.0; - m_timeStamp = Bookmark::INVALID_TIME_STAMP; + m_timeStamp = my::INVALID_TIME_STAMP; } void SetOrigin(string const & s) @@ -312,8 +250,8 @@ namespace bookmark_impl { if (currTag == "when") { - m_timeStamp = StringToTimestamp(value); - if (m_timeStamp == Bookmark::INVALID_TIME_STAMP) + m_timeStamp = my::StringToTimestamp(value); + if (m_timeStamp == my::INVALID_TIME_STAMP) LOG(LWARNING, ("Invalid timestamp in Placemark:", value)); } } @@ -448,9 +386,9 @@ void BookmarkCategory::SaveToKML(ostream & s) } time_t const timeStamp = bm->GetTimeStamp(); - if (timeStamp != Bookmark::INVALID_TIME_STAMP) + if (timeStamp != my::INVALID_TIME_STAMP) { - string const strTimeStamp = TimestampToString(timeStamp); + string const strTimeStamp = my::TimestampToString(timeStamp); ASSERT_EQUAL(strTimeStamp.size(), 20, ("We always generate fixed length UTC-format timestamp")); s << " " << strTimeStamp << "\n"; } diff --git a/map/bookmark.hpp b/map/bookmark.hpp index d1cda15ac6..50dc74e6d9 100644 --- a/map/bookmark.hpp +++ b/map/bookmark.hpp @@ -5,10 +5,11 @@ #include "../geometry/point2d.hpp" #include "../geometry/rect2d.hpp" +#include "../base/timer.hpp" + #include "../std/string.hpp" #include "../std/noncopyable.hpp" #include "../std/iostream.hpp" -#include "../std/ctime.hpp" class Bookmark @@ -21,9 +22,9 @@ class Bookmark time_t m_timeStamp; public: - Bookmark() : m_scale(-1.0), m_timeStamp(INVALID_TIME_STAMP) {} + Bookmark() : m_scale(-1.0), m_timeStamp(my::INVALID_TIME_STAMP) {} Bookmark(m2::PointD const & org, string const & name, string const & type) - : m_org(org), m_name(name), m_type(type), m_scale(-1.0), m_timeStamp(INVALID_TIME_STAMP) + : m_org(org), m_name(name), m_type(type), m_scale(-1.0), m_timeStamp(my::INVALID_TIME_STAMP) { } @@ -36,9 +37,7 @@ public: string const & GetDescription() const { return m_description; } void SetDescription(string const & description) { m_description = description; } - static time_t const INVALID_TIME_STAMP; - - /// @return INVALID_TIME_STAMP if bookmark has no timestamp + /// @return my::INVALID_TIME_STAMP if bookmark has no timestamp time_t GetTimeStamp() const { return m_timeStamp; } void SetTimeStamp(time_t timeStamp) { m_timeStamp = timeStamp; } diff --git a/map/map_tests/bookmarks_test.cpp b/map/map_tests/bookmarks_test.cpp index 0cf3003f5f..26a5e42017 100644 --- a/map/map_tests/bookmarks_test.cpp +++ b/map/map_tests/bookmarks_test.cpp @@ -9,10 +9,6 @@ #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 = @@ -123,7 +119,7 @@ char const * kmlString = TEST_EQUAL(bm->GetName(), "Nebraska", ()); TEST_EQUAL(bm->GetType(), "placemark-red", ()); TEST_EQUAL(bm->GetDescription(), "", ()); - TEST_EQUAL(bm->GetTimeStamp(), Bookmark::INVALID_TIME_STAMP, ()); + TEST_EQUAL(bm->GetTimeStamp(), my::INVALID_TIME_STAMP, ()); bm = cat.GetBookmark(1); TEST_EQUAL(bm->GetName(), "Monongahela National Forest", ()); @@ -146,7 +142,7 @@ char const * kmlString = TEST_ALMOST_EQUAL(MercatorBounds::YToLat(org.y), 53.89306, ()); TEST_EQUAL(bm->GetName(), "", ()); TEST_EQUAL(bm->GetDescription(), "Amps & ", ()); - TEST_EQUAL(bm->GetTimeStamp(), Bookmark::INVALID_TIME_STAMP, ()); + TEST_EQUAL(bm->GetTimeStamp(), my::INVALID_TIME_STAMP, ()); } } @@ -221,14 +217,14 @@ UNIT_TEST(Bookmarks_Timestamp) m2::PointD const orgPoint(10, 10); Bookmark b1(orgPoint, "name", "type"); b1.SetTimeStamp(timeStamp); - TEST_NOT_EQUAL(b1.GetTimeStamp(), Bookmark::INVALID_TIME_STAMP, ()); + TEST_NOT_EQUAL(b1.GetTimeStamp(), my::INVALID_TIME_STAMP, ()); Framework fm; fm.AddBookmark("cat", b1); BookmarkAndCategory res = fm.GetBookmark(fm.GtoP(orgPoint), 1.0); Bookmark const * b2 = fm.GetBmCategory(res.first)->GetBookmark(res.second); - TEST_NOT_EQUAL(b2->GetTimeStamp(), Bookmark::INVALID_TIME_STAMP, ()); + TEST_NOT_EQUAL(b2->GetTimeStamp(), my::INVALID_TIME_STAMP, ()); TEST_EQUAL(b2->GetTimeStamp(), timeStamp, ()); // Replace/update bookmark @@ -419,21 +415,3 @@ 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"), ()); -}