* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
* Description: timezone util
*/
#ifndef OMNI_RUNTIME_TIMEZONE_UTIL_H
#define OMNI_RUNTIME_TIMEZONE_UTIL_H
#include <set>
#include <string>
#include <functional>
#include <map>
#include <stdlib.h>
#include "type/date_time_utils.h"
#include "type/date32.h"
namespace omniruntime::codegen::function {
static const int YEAR_LENGTH = 4;
static const int MONTH_LENGTH = 2;
static const int DAY_LENGTH = 2;
static const int HOUR_LENGTH = 2;
static const int MINUTE_LENGTH = 2;
static const int SECOND_LENGTH = 2;
static const int TIME_LENGTH = 19;
static const int DATE_LENGTH = 10;
static const int TIME_FORMAT_LENGTH = 17;
static const int DATE_FORMAT_LENGTH = 8;
static const int MIN_YEAR = 0;
static const int MAX_YEAR = 9999;
static const int MIN_MONTH = 1;
static const int MAX_MONTH = 12;
static const int MIN_DAY = 1;
static const int DAYS_PER_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static const int FEBRUARY_DAY_IN_LEAP_YEAR = 29;
static const int FEBRUARY = 2;
static const int MIN_HOUR = 0;
static const int MAX_HOUR = 23;
static const int MIN_MINUTE = 0;
static const int MAX_MINUTE = 59;
static const int MIN_SECOND = 0;
static const int MAX_SECOND = 59;
static const int GREGORIAN_CALENDAR_START_YEAR = 1582;
static const std::set<time_t> UNIX_TIMESTAMP_FROM_DATE_SHANGHAI_NON_DST_SET = {
-18526 * type::SECOND_OF_DAY, -10806 * type::SECOND_OF_DAY,
-10519 * type::SECOND_OF_DAY, -10197 * type::SECOND_OF_DAY,
-8632 * type::SECOND_OF_DAY, -8297 * type::SECOND_OF_DAY,
-7915 * type::SECOND_OF_DAY, -7550 * type::SECOND_OF_DAY,
5967 * type::SECOND_OF_DAY, 6310 * type::SECOND_OF_DAY,
6681 * type::SECOND_OF_DAY, 7045 * type::SECOND_OF_DAY,
7409 * type::SECOND_OF_DAY, 7773 * type::SECOND_OF_DAY
};
static const std::set<time_t> UNIX_TIMESTAMP_FROM_DATE_SHANGHAI_DST_SET = {
6100 * type::SECOND_OF_DAY, 6464 * type::SECOND_OF_DAY,
6828 * type::SECOND_OF_DAY, 7199 * type::SECOND_OF_DAY,
7563 * type::SECOND_OF_DAY, 7927 * type::SECOND_OF_DAY
};
static const std::set<std::string> UNIX_TIMESTAMP_FROM_STR_SHANGHAI_NON_DST_SET = {
"1919-04-13", "1940-06-01", "1941-03-15", "1942-01-31", "1946-05-15", "1947-04-15", "1948-05-01", "1949-05-01"
};
using JudgeDSTActionByUnixTimestampFromDate = std::function<short(const struct tm *, time_t)>;
static std::map<std::string, JudgeDSTActionByUnixTimestampFromDate> AdjustDSTByUnixTimestampFromDateMap = {
{"GMT+08:00", [](const struct tm *timeInfo, time_t desiredTime) {return timeInfo->tm_isdst;}},
{"Asia/Shanghai", [](const struct tm *timeInfo, time_t desiredTime) {
short flag = 0;
flag = UNIX_TIMESTAMP_FROM_DATE_SHANGHAI_NON_DST_SET.find(desiredTime) !=
UNIX_TIMESTAMP_FROM_DATE_SHANGHAI_NON_DST_SET.end() ? 1 : flag;
flag = UNIX_TIMESTAMP_FROM_DATE_SHANGHAI_DST_SET.find(desiredTime) !=
UNIX_TIMESTAMP_FROM_DATE_SHANGHAI_DST_SET.end() ? -1 : flag;
return flag;
}
}
};
using JudgeDSTActionByUnixTimestampFromStr =
std::function<bool(const struct tm *, const char *, int32_t, const char *, int32_t)>;
static std::map<std::string, JudgeDSTActionByUnixTimestampFromStr> JudgeDSTByUnixTimestampFromStrMap = {
{"GMT+08:00", [](const struct tm *timeInfo, const char *timeStr, int32_t timeLen,
const char *fmtStr, int32_t fmtLen) {return false;}},
{"Asia/Shanghai", [](const struct tm *timeInfo, const char *timeStr, int32_t timeLen,
const char *fmtStr, int32_t fmtLen) {
bool flag = false;
flag = timeInfo->tm_isdst > 0;
std::string substr(timeStr, timeLen);
flag = UNIX_TIMESTAMP_FROM_STR_SHANGHAI_NON_DST_SET.find(substr) ==
UNIX_TIMESTAMP_FROM_STR_SHANGHAI_NON_DST_SET.end() && flag;
return flag;
}
}
};
using JudgeDSTActionByFromUnixTime = std::function<bool(const struct tm *)>;
static std::map<std::string, JudgeDSTActionByFromUnixTime> JudgeDSTByFromUnixTimeMap = {
{"GMT+08:00", [](const struct tm *timeInfo) {return timeInfo->tm_isdst > 0 ? false : true;}},
{"Asia/Shanghai", [](const struct tm *timeInfo) {return true;}}
};
class TimeZoneUtil {
public:
static inline short AdjustDSTByUnixTimestampFromDate(const char *tzStr,
int32_t tzLen, const struct tm *timeInfo, time_t desiredTime)
{
std::string timeZoneStr(tzStr, tzLen);
auto it = AdjustDSTByUnixTimestampFromDateMap.find(timeZoneStr);
return it ->second(timeInfo, desiredTime);
}
static inline bool JudgeDSTByUnixTimestampFromStr(const char *tzStr, int32_t tzLen, const struct tm *timeInfo,
const char *timeStr, int32_t timeLen, const char *fmtStr, int32_t fmtLen)
{
std::string timeZoneStr(tzStr, tzLen);
auto it = JudgeDSTByUnixTimestampFromStrMap.find(timeZoneStr);
return it->second(timeInfo, timeStr, timeLen, fmtStr, fmtLen);
}
static inline bool JudgeDSTByFromUnixTime(const char *tzStr, int32_t tzLen, const struct tm * timeInfo)
{
std::string timeZoneStr(tzStr, tzLen);
auto it = JudgeDSTByFromUnixTimeMap.find(timeZoneStr);
return it->second(timeInfo);
}
static inline const char* GetTZ(const char *tzStr)
{
if (strcmp(tzStr, "GMT+08:00") == 0) {
return "Etc/GMT-8";
} else {
return tzStr;
}
}
};
class TimeUtil {
public:
static bool IsTimeValid(const char *timeStr, int timeLen, const char *fmtStr, int fmtLen, const char *policyStr)
{
if ((timeLen != TIME_LENGTH || fmtLen != TIME_FORMAT_LENGTH) &&
(timeLen != DATE_LENGTH || fmtLen != DATE_FORMAT_LENGTH)) {
return false;
}
int year = 0;
int month = 0;
int day = 0;
int offset = 0;
bool retYear = CheckAndGetNonNegativeInteger(timeStr, timeLen, offset, YEAR_LENGTH, year);
if (!retYear || year < MIN_YEAR || year > MAX_YEAR) {
return false;
}
offset = YEAR_LENGTH + 1;
bool retMonth = CheckAndGetNonNegativeInteger(timeStr, timeLen, offset, MONTH_LENGTH, month);
if (!retMonth || month < MIN_MONTH || month > MAX_MONTH) {
return false;
}
offset += MONTH_LENGTH + 1;
bool retDay = CheckAndGetNonNegativeInteger(timeStr, timeLen, offset, DAY_LENGTH, day);
if (!retDay) {
return false;
}
bool hasException = false;
if (month == FEBRUARY) {
if (LocalDate::IsGregorianLeapYear(year)) {
if (day < MIN_DAY || day > FEBRUARY_DAY_IN_LEAP_YEAR) {
return false;
}
} else if (strcmp(policyStr, "EXCEPTION") == 0 && LocalDate::IsJulianLeapYear(year) &&
year < GREGORIAN_CALENDAR_START_YEAR && year > 0 && day == FEBRUARY_DAY_IN_LEAP_YEAR) {
hasException = true;
} else {
if (day < MIN_DAY || day > DAYS_PER_MONTH[month-1]) {
return false;
}
}
} else {
if (day < MIN_DAY || day > DAYS_PER_MONTH[month-1]) {
return false;
}
}
if (fmtLen == TIME_FORMAT_LENGTH) {
int hour = 0;
int minute = 0;
int second = 0;
offset += DAY_LENGTH + 1;
bool retHour = CheckAndGetNonNegativeInteger(timeStr, timeLen, offset, HOUR_LENGTH, hour);
if (!retHour || hour < MIN_HOUR || hour > MAX_HOUR) {
return false;
}
offset += HOUR_LENGTH + 1;
bool retMinute = CheckAndGetNonNegativeInteger(timeStr, timeLen, offset, MINUTE_LENGTH, minute);
if (!retMinute || minute < MIN_MINUTE || minute > MAX_MINUTE) {
return false;
}
offset += MINUTE_LENGTH + 1;
bool retSecond = CheckAndGetNonNegativeInteger(timeStr, timeLen, offset, SECOND_LENGTH, second);
if (!retSecond || second < MIN_SECOND || second > MAX_SECOND) {
return false;
}
}
if (hasException) {
throw exception::OmniException("OPERATOR_RUNTIME_ERROR",
"Invalid date 'February 29' as '" + std::to_string(year) + "' is not a leap year");
}
return true;
}
static size_t GetWallTimeMillis()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
}
private:
static bool CheckAndGetNonNegativeInteger(const char *str, int strLen, int start, int substrLen, int &outValue)
{
outValue = 0;
auto ptr = str + start;
for (int i = 0; i < substrLen; i++) {
auto value = ptr[i] - '0';
if (value < 0 || value > 9) {
return false;
}
outValue = outValue * 10 + value;
}
return true;
}
};
}
#endif