Skip to content

Instantly share code, notes, and snippets.

@ttldtor
Last active April 11, 2024 13:48
Show Gist options
  • Save ttldtor/6fa746139251794433e76c6efd97d155 to your computer and use it in GitHub Desktop.
Save ttldtor/6fa746139251794433e76c6efd97d155 to your computer and use it in GitHub Desktop.
enum TimeZone { LOCAL, GMT };
// https://stackoverflow.com/a/58037981/21913386
// Algorithm: http://howardhinnant.github.io/date_algorithms.html
int daysFromEpoch(int y, int m, int d) {
y -= m <= 2;
int era = y / 400;
int yoe = y - era * 400; // [0, 399]
int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365]
int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
return era * 146097 + doe - 719468;
}
// It does not modify broken-down time
time_t timegm(struct tm const *t) {
int year = t->tm_year + 1900;
int month = t->tm_mon; // 0-11
if (month > 11) {
year += month / 12;
month %= 12;
} else if (month < 0) {
int years_diff = (11 - month) / 12;
year -= years_diff;
month += 12 * years_diff;
}
int days_since_epoch = daysFromEpoch(year, month + 1, t->tm_mday);
return 60 * (60 * (24L * days_since_epoch + t->tm_hour) + t->tm_min) + t->tm_sec;
}
// Function to convert a tm structure to a time_point in UTC/GMT
std::chrono::system_clock::time_point tmToUTCTimePoint(std::tm *tm) {
// Construct time_t from tm as if it is UTC time
time_t utcTime = timegm(tm);
// Convert time_t to system_clock::time_point
return std::chrono::system_clock::from_time_t(utcTime);
}
constexpr long long INVALID_TIMESTAMP = std::numeric_limits<long long>::min();
template <TimeZone> inline long long parseDateTime(const std::string &dateTimeString, const std::string &format);
template <> inline long long parseDateTime<LOCAL>(const std::string &dateTimeString, const std::string &format) {
std::tm tm = {};
std::istringstream ss(dateTimeString);
ss >> std::get_time(&tm, format.c_str());
if (ss.fail()) {
return INVALID_TIMESTAMP;
}
return std::chrono::system_clock::from_time_t(std::mktime(&tm)).time_since_epoch().count() * 1000;
}
template <> inline long long parseDateTime<GMT>(const std::string &dateTimeString, const std::string &format) {
std::tm tm = {};
std::istringstream ss(dateTimeString);
ss >> std::get_time(&tm, format.c_str());
if (ss.fail()) {
return INVALID_TIMESTAMP;
}
return tmToUTCTimePoint(&tm).time_since_epoch().count() * 1000;
}
/*
* Parse date string in formats:
* "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%dT%H:%M", "%Y-%m-%d",
* "%d-%m-%Y %H:%M:%S", "%d-%m-%YT%H:%M:%S", "%d-%m-%Y %H:%M", "%d-%m-%YT%H:%M", "%d-%m-%Y",
* "%Y-%m-%d %H:%M:%SZ", "%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%d %H:%MZ", "%Y-%m-%dT%H:%MZ", "%Y-%m-%dZ",
* "%d-%m-%Y %H:%M:%SZ", "%d-%m-%YT%H:%M:%SZ", "%d-%m-%Y %H:%MZ", "%d-%m-%YT%H:%MZ", "%d-%m-%YZ",
* "<timestamp_in_millis>"
*/
long long parseDateTime(const std::string &dateTimeString) {
static const std::vector<std::string> formats{
"%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%dT%H:%M", "%Y-%m-%d",
"%d-%m-%Y %H:%M:%S", "%d-%m-%YT%H:%M:%S", "%d-%m-%Y %H:%M", "%d-%m-%YT%H:%M", "%d-%m-%Y",
};
if (dateTimeString.size() > 1 && dateTimeString.back() == 'Z') {
auto dateTimeString2 = dateTimeString.substr(0, dateTimeString.size() - 1);
for (auto &&f : formats) {
auto timestamp = parseDateTime<GMT>(dateTimeString2, f);
if (timestamp != INVALID_TIMESTAMP) {
return timestamp;
}
}
}
for (auto &&f : formats) {
auto timestamp = parseDateTime<LOCAL>(dateTimeString, f);
if (timestamp != INVALID_TIMESTAMP) {
return timestamp;
}
}
long long result{};
std::istringstream ss(dateTimeString);
ss >> result;
if (ss.fail()) {
return INVALID_TIMESTAMP;
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment