Skip to content

Commit 653b62f

Browse files
committed
Support parsing datetime with timezone.
1 parent 65f2455 commit 653b62f

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

trantor/unittests/DateUnittest.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,25 @@ TEST(Date, DatabaseStringTest)
107107
us = (dbDate.microSecondsSinceEpoch() % 1000000);
108108
EXPECT_EQ(us, 3);
109109
}
110+
TEST(Date, TimezoneTest)
111+
{
112+
std::string str0 = "2024-01-01 04:00:00.123";
113+
std::string str1 = "2024-01-01 12:00:00.123 +08:00";
114+
std::string str2 = "2024-01-01 11:00:00.123+0700";
115+
std::string str3 = "2024-01-01 10:00:00.123 0600";
116+
std::string str4 = "2024-01-01 03:00:00.123 -01:00";
117+
std::string str5 = "2024-01-01 02:00:00.123-02:00";
118+
std::string str6 = "2024-01-01 01:00:00.123 -0300";
119+
120+
auto date = trantor::Date::fromDbString(str0);
121+
for (auto &s : {str1, str2, str3, str4, str5, str6})
122+
{
123+
auto dateTz = trantor::Date::parseDatetimeTz(s);
124+
EXPECT_EQ(date.microSecondsSinceEpoch(),
125+
dateTz.microSecondsSinceEpoch());
126+
}
127+
}
128+
110129
int main(int argc, char **argv)
111130
{
112131
testing::InitGoogleTest(&argc, argv);

trantor/utils/Date.cc

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,109 @@ Date Date::fromDbString(const std::string &datetime)
323323
static_cast<double>(timezoneOffset()));
324324
}
325325

326+
Date Date::parseDatetimeTz(const std::string &datetime)
327+
{
328+
unsigned int year = {0}, month = {0}, day = {0}, hour = {0}, minute = {0},
329+
second = {0}, microSecond = {0};
330+
int tzSign{0}, tzOffset{0};
331+
std::vector<std::string> v = splitString(datetime, " ");
332+
if (v.empty())
333+
{
334+
throw std::invalid_argument("Invalid date string: " + datetime);
335+
}
336+
337+
// parse date
338+
const std::vector<std::string> date = splitString(v[0], "-");
339+
if (date.size() != 3)
340+
{
341+
throw std::invalid_argument("Invalid date string: " + datetime);
342+
}
343+
year = std::stol(date[0]);
344+
month = std::stol(date[1]);
345+
day = std::stol(date[2]);
346+
347+
// only have date part
348+
if (v.size() <= 1)
349+
{
350+
return trantor::Date{year, month, day};
351+
}
352+
353+
// check timezone without space seperated
354+
if (v.size() == 2)
355+
{
356+
auto pos = v[1].find('+');
357+
if (pos != std::string::npos)
358+
{
359+
tzSign = 1;
360+
v.push_back(v[1].substr(pos + 1));
361+
v[1] = v[1].substr(0, pos);
362+
}
363+
else if ((pos = v[1].find('-')) != std::string::npos)
364+
{
365+
tzSign = -1;
366+
v.push_back(v[1].substr(pos + 1));
367+
v[1] = v[1].substr(0, pos);
368+
}
369+
}
370+
371+
// parse time
372+
std::vector<std::string> timeParts = splitString(v[1], ":");
373+
if (timeParts.size() < 2 || timeParts.size() > 3)
374+
{
375+
throw std::invalid_argument("Invalid time string: " + datetime);
376+
}
377+
hour = std::stol(timeParts[0]);
378+
minute = std::stol(timeParts[1]);
379+
if (timeParts.size() == 3)
380+
{
381+
auto secParts = splitString(timeParts[2], ".");
382+
second = std::stol(secParts[0]);
383+
// micro seconds
384+
if (secParts.size() > 1)
385+
{
386+
if (secParts[1].length() > 6)
387+
{
388+
secParts[1].resize(6);
389+
}
390+
else if (secParts[1].length() < 6)
391+
{
392+
secParts[1].append(6 - secParts[1].length(), '0');
393+
}
394+
microSecond = std::stol(secParts[1]);
395+
}
396+
}
397+
398+
// timezone
399+
if (v.size() >= 3)
400+
{
401+
std::string &tz = v[2];
402+
if (tzSign == 0)
403+
{
404+
if (tz[0] == '-')
405+
{
406+
tz = tz.substr(1);
407+
tzSign = -1;
408+
}
409+
else
410+
{
411+
tzSign = 1;
412+
}
413+
}
414+
415+
auto tzParts = splitString(tz, ":");
416+
if (tzParts.size() == 1 && tz.size() == 4)
417+
{
418+
tzParts = {tz.substr(0, 2), tz.substr(2)}; // 0800
419+
}
420+
int tzHour = std::stoi(tzParts[0]);
421+
int tzMin = tzParts.size() > 1 ? std::stoi(tzParts[1]) : 0;
422+
tzOffset = tzSign * (tzHour * 3600 + tzMin * 60);
423+
}
424+
425+
return trantor::Date(year, month, day, hour, minute, second, microSecond)
426+
.after(timezoneOffset() - tzOffset);
427+
}
428+
326429
std::string Date::toCustomFormattedStringLocal(const std::string &fmtStr,
327430
bool showMicroseconds) const
328431
{

trantor/utils/Date.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,17 @@ class TRANTOR_EXPORT Date
295295
*/
296296
static Date fromDbString(const std::string &datetime);
297297

298+
/**
299+
* @brief Parse a datetime string
300+
* Could be following format:
301+
* - yyyy-mm-dd
302+
* - yyyy-mm-dd HH:MM[:SS[.ffffff]]
303+
* - yyyy-mm-dd HH:MM[:SS[.ffffff]][ ][+-]08
304+
* - yyyy-mm-dd HH:MM[:SS[.ffffff]][ ][+-]08:00
305+
* - yyyy-mm-dd HH:MM[:SS[.ffffff]][ ][+-]0800
306+
*/
307+
static Date parseDatetimeTz(const std::string &datetime);
308+
298309
/* clang-format off */
299310
/**
300311
* @brief Generate a UTC time string.

0 commit comments

Comments
 (0)