Skip to content

Commit 63f573b

Browse files
committed
Support parsing datetime with timezone.
1 parent 392e9d1 commit 63f573b

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
@@ -112,6 +112,25 @@ TEST(Date, DatabaseStringTest)
112112
auto epoch = dbDateGMT.microSecondsSinceEpoch();
113113
EXPECT_EQ(epoch, 0);
114114
}
115+
TEST(Date, TimezoneTest)
116+
{
117+
std::string str0 = "2024-01-01 04:00:00.123";
118+
std::string str1 = "2024-01-01 12:00:00.123 +08:00";
119+
std::string str2 = "2024-01-01 11:00:00.123+0700";
120+
std::string str3 = "2024-01-01 10:00:00.123 0600";
121+
std::string str4 = "2024-01-01 03:00:00.123 -01:00";
122+
std::string str5 = "2024-01-01 02:00:00.123-02:00";
123+
std::string str6 = "2024-01-01 01:00:00.123 -0300";
124+
125+
auto date = trantor::Date::fromDbString(str0);
126+
for (auto &s : {str1, str2, str3, str4, str5, str6})
127+
{
128+
auto dateTz = trantor::Date::parseDatetimeTz(s);
129+
EXPECT_EQ(date.microSecondsSinceEpoch(),
130+
dateTz.microSecondsSinceEpoch());
131+
}
132+
}
133+
115134
int main(int argc, char **argv)
116135
{
117136
testing::InitGoogleTest(&argc, argv);

trantor/utils/Date.cc

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

358+
Date Date::parseDatetimeTz(const std::string &datetime)
359+
{
360+
unsigned int year = {0}, month = {0}, day = {0}, hour = {0}, minute = {0},
361+
second = {0}, microSecond = {0};
362+
int tzSign{0}, tzOffset{0};
363+
std::vector<std::string> v = splitString(datetime, " ");
364+
if (v.empty())
365+
{
366+
throw std::invalid_argument("Invalid date string: " + datetime);
367+
}
368+
369+
// parse date
370+
const std::vector<std::string> date = splitString(v[0], "-");
371+
if (date.size() != 3)
372+
{
373+
throw std::invalid_argument("Invalid date string: " + datetime);
374+
}
375+
year = std::stol(date[0]);
376+
month = std::stol(date[1]);
377+
day = std::stol(date[2]);
378+
379+
// only have date part
380+
if (v.size() <= 1)
381+
{
382+
return trantor::Date{year, month, day};
383+
}
384+
385+
// check timezone without space seperated
386+
if (v.size() == 2)
387+
{
388+
auto pos = v[1].find('+');
389+
if (pos != std::string::npos)
390+
{
391+
tzSign = 1;
392+
v.push_back(v[1].substr(pos + 1));
393+
v[1] = v[1].substr(0, pos);
394+
}
395+
else if ((pos = v[1].find('-')) != std::string::npos)
396+
{
397+
tzSign = -1;
398+
v.push_back(v[1].substr(pos + 1));
399+
v[1] = v[1].substr(0, pos);
400+
}
401+
}
402+
403+
// parse time
404+
std::vector<std::string> timeParts = splitString(v[1], ":");
405+
if (timeParts.size() < 2 || timeParts.size() > 3)
406+
{
407+
throw std::invalid_argument("Invalid time string: " + datetime);
408+
}
409+
hour = std::stol(timeParts[0]);
410+
minute = std::stol(timeParts[1]);
411+
if (timeParts.size() == 3)
412+
{
413+
auto secParts = splitString(timeParts[2], ".");
414+
second = std::stol(secParts[0]);
415+
// micro seconds
416+
if (secParts.size() > 1)
417+
{
418+
if (secParts[1].length() > 6)
419+
{
420+
secParts[1].resize(6);
421+
}
422+
else if (secParts[1].length() < 6)
423+
{
424+
secParts[1].append(6 - secParts[1].length(), '0');
425+
}
426+
microSecond = std::stol(secParts[1]);
427+
}
428+
}
429+
430+
// timezone
431+
if (v.size() >= 3)
432+
{
433+
std::string &tz = v[2];
434+
if (tzSign == 0)
435+
{
436+
if (tz[0] == '-')
437+
{
438+
tz = tz.substr(1);
439+
tzSign = -1;
440+
}
441+
else
442+
{
443+
tzSign = 1;
444+
}
445+
}
446+
447+
auto tzParts = splitString(tz, ":");
448+
if (tzParts.size() == 1 && tz.size() == 4)
449+
{
450+
tzParts = {tz.substr(0, 2), tz.substr(2)}; // 0800
451+
}
452+
int tzHour = std::stoi(tzParts[0]);
453+
int tzMin = tzParts.size() > 1 ? std::stoi(tzParts[1]) : 0;
454+
tzOffset = tzSign * (tzHour * 3600 + tzMin * 60);
455+
}
456+
457+
return trantor::Date(year, month, day, hour, minute, second, microSecond)
458+
.after(timezoneOffset() - tzOffset);
459+
}
460+
358461
std::string Date::toCustomFormattedStringLocal(const std::string &fmtStr,
359462
bool showMicroseconds) const
360463
{

trantor/utils/Date.h

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

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

0 commit comments

Comments
 (0)