/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. Copyright (c) 2009, 2019, MariaDB Corporation. Copyright (c) 2021 OceanBase Technology Co.,Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ #include #include #include "ob_oralce_format_models.h" #include "mariadb_ctype.h" #include #include "ob_bitmap.h" int32_t calc_max_name_length(const struct ObTimeConstStr names[], const int64_t size); #define FALSE_IT(stmt) ({ (stmt); FALSE; }) #define INT32_MAX_DIGITS_LEN 10 #define EPOCH_YEAR4 1970 #define EPOCH_WDAY 4 // 1970-1-1 is thursday. #define LEAP_YEAR_COUNT(y) ((y) / 4 - (y) / 100 + (y) / 400) #define IS_LEAP_YEAR(y) ((((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) ? 1 : 0) #define DAYS_PER_NYEAR 365 #define DAYS_PER_LYEAR 366 static const int32_t MIN_OFFSET_MINUTES = -15 * 60 - 59; static const int32_t MAX_OFFSET_MINUTES = 15 * 60 + 59; static const int32_t ZERO_DATE = (int32_t)(-106751991); static const int64_t ZERO_TIME = 0; static const int64_t MAX_VARCHAR_LENGTH = 1024L * 1024L; // unit is byte static const int64_t COMMON_ELEMENT_NUMBER = 10; static const int16_t MAX_SCALE_FOR_ORACLE_TEMPORAL = 9; static const int16_t DEFAULT_SCALE_FOR_ORACLE_FRACTIONAL_SECONDS = 6; static const int64_t UNKNOWN_LENGTH_OF_ELEMENT = 20; static const int64_t power_of_10[INT32_MAX_DIGITS_LEN] = { 1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L, 100000000L, 1000000000L, //2147483647 }; const int64_t TZ_PART_MIN[DATETIME_PART_CNT] = { 1, 1, 1, 0, 0, 0, 0}; const int64_t TZ_PART_MAX[DATETIME_PART_CNT] = {9999, 12, 31, 23, 59, 59, 1000000000}; static const int8_t DAYS_PER_MON[2][12 + 1] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static const int32_t DAYS_UNTIL_MON[2][12 + 1] = { {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} }; /** * 3 days after wday 5 is wday 1, so [3][5] = 1. * 5 days before wday 2 is wday 4, so [-5][2] = 4. * and so on, max wday is 7, min offset is -6, max offset days is 6. */ static const int8_t WDAY_OFFSET_ARR[DAYS_PER_WEEK * 2 - 1][DAYS_PER_WEEK + 1] = { {0, 2, 3, 4, 5, 6, 7, 1}, {0, 3, 4, 5, 6, 7, 1, 2}, {0, 4, 5, 6, 7, 1, 2, 3}, {0, 5, 6, 7, 1, 2, 3, 4}, {0, 6, 7, 1, 2, 3, 4, 5}, {0, 7, 1, 2, 3, 4, 5, 6}, // offset = -1, wday = 1/2/3/4/5/6/7. {0, 1, 2, 3, 4, 5, 6, 7}, // offset = 0, wday = 1/2/3/4/5/6/7. {0, 2, 3, 4, 5, 6, 7, 1}, // offset = 1, wday = 1/2/3/4/5/6/7. {0, 3, 4, 5, 6, 7, 1, 2}, {0, 4, 5, 6, 7, 1, 2, 3}, {0, 5, 6, 7, 1, 2, 3, 4}, {0, 6, 7, 1, 2, 3, 4, 5}, {0, 7, 1, 2, 3, 4, 5, 6} }; static const int8_t (*WDAY_OFFSET)[DAYS_PER_WEEK + 1] = &WDAY_OFFSET_ARR[6]; /* * if wday of yday 1 is 4, not SUN_BEGIN, not GE_4_BEGIN, yday of fitst day of week 1 is 5, so [4][0][0] = 5. * if wday of yday 1 is 2, SUN_BEGIN, GE_4_BEGIN, yday of fitst day of week 1 is 5, so [2][1][1] = 1. * and so on, max wday is 7, and other two is 1. * ps: if the first week is not full week(GE_4_BEGIN), the yday maybe zero or neg, such as 0 means * the last day of prev year, and so on. */ static const int8_t YDAY_WEEK1[DAYS_PER_WEEK + 1][2][2] = { {{0, 0}, {0, 0}}, {{1, 1}, {7, 0}}, // wday of day 1 is 1. {{7, 0}, {6, -1}}, // 2. {{6, -1}, {5, -2}}, // 3. {{5, -2}, {4, 4}}, // 4. {{4, 4}, {3, 3}}, // 5. {{3, 3}, {2, 2}}, // 6. {{2, 2}, {1, 1}} // 7. }; #define WEEK_MODE_CNT 8 static const ObDTMode WEEK_MODE[WEEK_MODE_CNT] = { DT_WEEK_SUN_BEGIN | DT_WEEK_ZERO_BEGIN , DT_WEEK_ZERO_BEGIN | DT_WEEK_GE_4_BEGIN , DT_WEEK_SUN_BEGIN , DT_WEEK_GE_4_BEGIN , //ISO-8601 standard week DT_WEEK_SUN_BEGIN | DT_WEEK_ZERO_BEGIN | DT_WEEK_GE_4_BEGIN , DT_WEEK_ZERO_BEGIN , DT_WEEK_SUN_BEGIN | DT_WEEK_GE_4_BEGIN , 0 }; static const int32_t DAYS_PER_YEAR[2]= { DAYS_PER_NYEAR, DAYS_PER_LYEAR }; static const struct ObTimeConstStr WDAY_NAMES[DAYS_PER_WEEK + 1] = { {"null", 4}, {"Monday", 6}, {"Tuesday", 7}, {"Wednesday", 9}, {"Thursday", 8}, {"Friday", 6}, {"Saturday", 8}, {"Sunday", 6} }; static const int32_t MAX_WDAY_NAME_LENGTH = 9; static const struct ObTimeConstStr WDAY_ABBR_NAMES[DAYS_PER_WEEK + 1] = { {"null", 4}, {"Mon", 3}, {"Tue", 3}, {"Wed", 3}, {"Thu", 3}, {"Fri", 3}, {"Sat", 3}, {"Sun", 3} }; static const struct ObTimeConstStr MON_NAMES[12 + 1] = { {"null", 4}, {"January", 7}, {"February", 8}, {"March", 5}, {"April", 5}, {"May", 3}, {"June", 4}, {"July", 4}, {"August", 6}, {"September", 9}, {"October", 7}, {"November", 8}, {"December", 8} }; static const int32_t MAX_MON_NAME_LENGTH = 9; static const struct ObTimeConstStr MON_ABBR_NAMES[12 + 1] = { {"null", 4}, {"Jan", 3}, {"Feb", 3}, {"Mar", 3}, {"Apr", 3}, {"May", 3}, {"Jun", 3}, {"Jul", 3}, {"Aug", 3}, {"Sep", 3}, {"Oct", 3}, {"Nov", 3}, {"Dec", 3} }; static int64_t CONFLICT_GROUP_MAP[MAX_FLAG_NUMBER] = { /**AD*/ ERA_GROUP, /**AD2*/ ERA_GROUP, /**BC*/ ERA_GROUP, /**BC2*/ ERA_GROUP, /**CC,*/ NEVER_APPEAR_GROUP, /**SCC*/ NEVER_APPEAR_GROUP, /**D*/ WEEK_OF_DAY_GROUP, /**DAY*/ WEEK_OF_DAY_GROUP, /**DD*/ NON_CONFLICT_GROUP, /**DDD*/ DAY_OF_YEAR_GROUP, /**DY*/ WEEK_OF_DAY_GROUP, /**FF1*/ NON_CONFLICT_GROUP, /**FF2*/ NON_CONFLICT_GROUP, /**FF3*/ NON_CONFLICT_GROUP, /**FF4*/ NON_CONFLICT_GROUP, /**FF5*/ NON_CONFLICT_GROUP, /**FF6*/ NON_CONFLICT_GROUP, /**FF7*/ NON_CONFLICT_GROUP, /**FF8*/ NON_CONFLICT_GROUP, /**FF9*/ NON_CONFLICT_GROUP, /**FF*/ NON_CONFLICT_GROUP, /**HH*/ HOUR_GROUP, /**HH24*/ HOUR_GROUP, /**HH12*/ HOUR_GROUP, /**IW*/ NEVER_APPEAR_GROUP, /**I*/ NEVER_APPEAR_GROUP, /**IY*/ NEVER_APPEAR_GROUP, /**IYY*/ NEVER_APPEAR_GROUP, /**IYYY*/ NEVER_APPEAR_GROUP, /**MI*/ NON_CONFLICT_GROUP, /**MM*/ MONTH_GROUP, /**MONTH*/ MONTH_GROUP, /**MON*/ MONTH_GROUP, /**AM*/ MERIDIAN_INDICATOR_GROUP, /**AM2*/ MERIDIAN_INDICATOR_GROUP, /**PM*/ MERIDIAN_INDICATOR_GROUP, /**PM2*/ MERIDIAN_INDICATOR_GROUP, /**Q*/ NON_CONFLICT_GROUP, /**RR*/ YEAR_GROUP, /**RRRR*/ YEAR_GROUP, /**SS*/ NON_CONFLICT_GROUP, /**SSSSS*/ RUNTIME_CONFLICT_SOLVE_GROUP, /**WW*/ NEVER_APPEAR_GROUP, /**W*/ NEVER_APPEAR_GROUP, /**YGYYY*/ YEAR_GROUP, /**YEAR*/ NEVER_APPEAR_GROUP, /**SYEAR*/ NEVER_APPEAR_GROUP, /**YYYY*/ YEAR_GROUP, /**SYYYY*/ YEAR_GROUP, /**YYY*/ YEAR_GROUP, /**YY*/ YEAR_GROUP, /**Y*/ YEAR_GROUP, /**DS*/ RUNTIME_CONFLICT_SOLVE_GROUP, /**DL*/ RUNTIME_CONFLICT_SOLVE_GROUP, /**TZH*/ RUNTIME_CONFLICT_SOLVE_GROUP, /**TZM*/ RUNTIME_CONFLICT_SOLVE_GROUP, /**TZD*/ RUNTIME_CONFLICT_SOLVE_GROUP, /**TZR*/ RUNTIME_CONFLICT_SOLVE_GROUP, /**X*/ NON_CONFLICT_GROUP, /**J*/ DAY_OF_YEAR_GROUP }; //define the length ,may be less ,but should not max than this static int64_t EXPECTED_MATCHING_LENGTH[MAX_FLAG_NUMBER] = { /**AD*/ 2, /**AD2*/ 4, /**BC*/ 2, /**BC2*/ 4, /**CC,*/ 0, //never used /**SCC*/ 0, //never used /**D*/ 1, /**DAY*/ 0, //non-numeric, ignored /**DD*/ 2, /**DDD*/ 3, /**DY*/ 0, //non-numeric, ignored /**FF1*/ 1, /**FF2*/ 2, /**FF3*/ 3, /**FF4*/ 4, /**FF5*/ 5, /**FF6*/ 6, /**FF7*/ 7, /**FF8*/ 8, /**FF9*/ 9, /**FF*/ 9, /**HH*/ 2, /**HH24*/ 2, /**HH12*/ 2, /**IW*/ 0, //never used /**I*/ 0, //never used /**IY*/ 0, //never used /**IYY*/ 0, //never used /**IYYY*/ 0, //never used /**MI*/ 2, /**MM*/ 2, /**MONTH*/ 0, //non-numeric, ignored /**MON*/ 0, //non-numeric, ignored /**AM*/ 2, /**AM2*/ 4, /**PM*/ 2, /**PM2*/ 4, /**Q*/ 0, //never used /**RR*/ 0, //special case /**RRRR*/ 0, //special case /**SS*/ 2, /**SSSSS*/ 5, /**WW*/ 0, //never used /**W*/ 0, //never used /**YGYYY*/ 5, /**YEAR*/ 0, //never used /**SYEAR*/ 0, //never used /**YYYY*/ 4, /**SYYYY*/ 4, /**YYY*/ 3, /**YY*/ 2, /**Y*/ 1, /**DS*/ 0, //todo /**DL*/ 0, //todo /**TZH*/ 2, //todo /**TZM*/ 2, //todo /**TZD*/ 0, //non-numeric, ignored /**TZR*/ 0, //non-numeric, ignored /**X*/ 0, //non-numeric, ignored /**J*/ 7 }; int32_t calc_max_name_length(const struct ObTimeConstStr names[], const int64_t size) { int32_t res = 0; int64_t i; for (i = 1; i <= size; ++i) { if (res < names[i].len_) { res = names[i].len_; } } return res; } int databuff_vprintf(char *buf, const int64_t buf_len, int64_t *pos, const char *fmt, va_list args) { int ret = 0; if (NULL != buf && 0 <= *pos && *pos < buf_len) { int len = vsnprintf(buf + *pos, buf_len - *pos, fmt, args); if (len < 0) { ret = 1; } else if (len < buf_len - *pos) { *pos += len; } else { *pos = buf_len - 1; //skip '\0' written by vsnprintf ret = 1; } } else { ret = 1; } return ret; } int databuff_printf(char *buf, const int64_t buf_len, int64_t *pos, const char *fmt, ...) { int ret = 0; va_list args; va_start(args, fmt); if (0 != (ret = databuff_vprintf(buf, buf_len, pos, fmt, args))) { } else {} va_end(args); return ret; } //PATTERN in windows(wingdi.h)Duplicate definition,So change PATTERN to PATTERNEX //do not define duplicate pattern const struct ObTimeConstStr PATTERNEX[MAX_FLAG_NUMBER] = { #define ObTimeConstStr(x) {x, sizeof(x) - 1} ObTimeConstStr("AD"), ObTimeConstStr("A.D."), ObTimeConstStr("BC"), ObTimeConstStr("B.C."), ObTimeConstStr("CC"), ObTimeConstStr("SCC"), ObTimeConstStr("D"), ObTimeConstStr("DAY"), ObTimeConstStr("DD"), ObTimeConstStr("DDD"), ObTimeConstStr("DY"), ObTimeConstStr("FF1"), ObTimeConstStr("FF2"), ObTimeConstStr("FF3"), ObTimeConstStr("FF4"), ObTimeConstStr("FF5"), ObTimeConstStr("FF6"), ObTimeConstStr("FF7"), ObTimeConstStr("FF8"), ObTimeConstStr("FF9"), ObTimeConstStr("FF"), ObTimeConstStr("HH"), ObTimeConstStr("HH24"), ObTimeConstStr("HH12"), ObTimeConstStr("IW"), ObTimeConstStr("I"), ObTimeConstStr("IY"), ObTimeConstStr("IYY"), ObTimeConstStr("IYYY"), ObTimeConstStr("MI"), ObTimeConstStr("MM"), ObTimeConstStr("MONTH"), ObTimeConstStr("MON"), ObTimeConstStr("AM"), ObTimeConstStr("A.M."), ObTimeConstStr("PM"), ObTimeConstStr("P.M."), ObTimeConstStr("Q"), ObTimeConstStr("RR"), ObTimeConstStr("RRRR"), ObTimeConstStr("SS"), ObTimeConstStr("SSSSS"), ObTimeConstStr("WW"), ObTimeConstStr("W"), ObTimeConstStr("Y,YYY"), ObTimeConstStr("YEAR"), ObTimeConstStr("SYEAR"), ObTimeConstStr("YYYY"), ObTimeConstStr("SYYYY"), ObTimeConstStr("YYY"), ObTimeConstStr("YY"), ObTimeConstStr("Y"), ObTimeConstStr("DS"), ObTimeConstStr("DL"), ObTimeConstStr("TZH"), ObTimeConstStr("TZM"), ObTimeConstStr("TZD"), ObTimeConstStr("TZR"), ObTimeConstStr("X"), #undef ObTimeConstStr }; static const struct ObOracleTimeLimiter LIMITER_YEAR = {1, 9999, 1}; static const struct ObOracleTimeLimiter LIMITER_MONTH = {1, 12, 1}; static const struct ObOracleTimeLimiter LIMITER_MONTH_DAY = {1, 31, 1}; static const struct ObOracleTimeLimiter LIMITER_WEEK_DAY = {1, 7, 1}; static const struct ObOracleTimeLimiter LIMITER_YEAR_DAY = {1, 366, 1}; static const struct ObOracleTimeLimiter LIMITER_HOUR12 = {1, 12, 1}; static const struct ObOracleTimeLimiter LIMITER_HOUR24 = {0, 23, 1}; static const struct ObOracleTimeLimiter LIMITER_MINUTE = {0, 59, 1}; static const struct ObOracleTimeLimiter LIMITER_SECOND = {0, 59, 1}; static const struct ObOracleTimeLimiter LIMITER_SECS_PAST_MIDNIGHT = {0, 86399, 1}; static const struct ObOracleTimeLimiter LIMITER_TIMEZONE_HOUR_ABS = {0, 15, 1}; //ORA-01874: time zone hour must be between -15 and 15 static const struct ObOracleTimeLimiter LIMITER_TIMEZONE_MIN_ABS = {0, 59, 1}; //ORA-01875: time zone minute must be between -59 and 59 static const struct ObOracleTimeLimiter LIMITER_JULIAN_DATE = {1, 5373484,1}; // -4712-01-01 ~ 9999-12-31 static inline int check_validate(const struct ObOracleTimeLimiter *limiter, int32_t value) { int ret = 0; if (value < limiter->MIN || value > limiter->MAX) { ret = limiter->ERROR_CODE; } return ret; } int get_day_and_month_from_year_day(const int32_t yday, const int32_t year, int32_t *month, int32_t *day) { int ret = 0; int32_t leap_year = IS_LEAP_YEAR(year); if (yday > DAYS_UNTIL_MON[leap_year][12]) { ret = 1; } else { my_bool stop_flag = FALSE; int32_t i = LIMITER_MONTH.MIN; for (; !stop_flag && i <= LIMITER_MONTH.MAX; ++i) { if (yday <= DAYS_UNTIL_MON[leap_year][i]) { *month = i; *day = yday - DAYS_UNTIL_MON[leap_year][i - 1]; stop_flag = TRUE; } } } return ret; } int validate_oracle_date(const struct ObTime *ob_time) { const int32_t *parts = ob_time->parts_; int ret = 0; int i = 0; for (; 0 == ret && i < ORACLE_DATE_PART_CNT; ++i) { if (parts[i] < TZ_PART_MIN[i] || parts[i] > TZ_PART_MAX[i]) { ret = 1; } } if (0 == ret) { int is_leap = IS_LEAP_YEAR(parts[DT_YEAR]); if (parts[DT_MDAY] > DAYS_PER_MON[is_leap][parts[DT_MON]]) { ret = 1; } } return ret; } /* * if format elements contains TZR * hour minuts seconds and fracial second can not omit * Because, I guess, the daylight-saving time may be uncertain * if the time part is omitted. * The day */ inline my_bool is_element_can_omit(const struct ObDFMElem *elem) { int ret_bool = TRUE; int64_t flag = elem->elem_flag_; int64_t conf_group = CONFLICT_GROUP_MAP[flag]; if (YEAR_GROUP == conf_group || WEEK_OF_DAY_GROUP == conf_group || MONTH_GROUP == conf_group || DD == flag || DS == flag || DL == flag) { ret_bool = FALSE; } else { //return true } return ret_bool; } static inline my_bool is_sign_char(const char ch) { return '-' == ch || '+' == ch; } static inline my_bool is_split_char(const char ch) { int ret_bool = FALSE; if (ch == '\n' || ch == '\t' || ((ch >= 0x20 && ch <= 0x7E) && !((ch >= '0' && ch <= '9') || (ch >='a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')))) { ret_bool = TRUE; } return ret_bool; } static int date_to_ob_time(int32_t value, struct ObTime *ob_time) { int ret = 0; int32_t *parts = ob_time->parts_; if (!HAS_TYPE_ORACLE(ob_time->mode_) && ZERO_DATE == value) { memset(parts, 0, sizeof(*parts) * DATETIME_PART_CNT); parts[DT_DATE] = ZERO_DATE; } else { int32_t days = value; int32_t leap_year = 0; int32_t year = EPOCH_YEAR4; const int32_t *cur_days_until_mon = NULL; int32_t month = 1; parts[DT_DATE] = value; // year. while (days < 0 || days >= DAYS_PER_YEAR[leap_year = IS_LEAP_YEAR(year)]) { int32_t new_year = year + days / DAYS_PER_NYEAR; new_year -= (days < 0); days -= (new_year - year) * DAYS_PER_NYEAR + LEAP_YEAR_COUNT(new_year - 1) - LEAP_YEAR_COUNT(year - 1); year = new_year; } parts[DT_YEAR] = year; parts[DT_YDAY] = days + 1; parts[DT_WDAY] = WDAY_OFFSET[value % DAYS_PER_WEEK][EPOCH_WDAY]; // month. cur_days_until_mon = DAYS_UNTIL_MON[leap_year]; for (; month < MONS_PER_YEAR && days >= cur_days_until_mon[month]; ++month) {} parts[DT_MON] = month; days -= cur_days_until_mon[month - 1]; // day. parts[DT_MDAY] = days + 1; } return ret; } int set_ob_time_part_directly(struct ObTime *ob_time, int64_t *conflict_bitset, const int64_t part_offset, const int32_t part_value) { int ret = 0; if (part_offset >= TOTAL_PART_CNT) { ret = 1; } else { ob_time->parts_[part_offset] = part_value; *conflict_bitset |= (1 << part_offset); } return ret; } // DT_YEAR use this interface ,as it should treat special: // select to_date('5', 'YY') from dual; the result for oracle is 2005-09-01 // int set_ob_time_year_may_conflict(struct ObTime *ob_time, int32_t *julian_year_value, int32_t check_year, int32_t set_year, my_bool overwrite) { int ret = 0; if (ZERO_DATE != *julian_year_value) { if (*julian_year_value != check_year) { ret = 1; } else if (overwrite) { ob_time->parts_[DT_YEAR] = set_year; } } else { ob_time->parts_[DT_YEAR] = set_year; *julian_year_value = check_year; } return (ret); } /* * element group may cause conflict on parts in ob_time * 1. SSSSS vs HH, HH24, HH12, MI, SS * 2. DDD vs DD MM/Mon/Month * * while call this function, the part_value must be the final value */ int set_ob_time_part_may_conflict(struct ObTime *ob_time, int64_t *conflict_bitset, const int64_t part_offset, const int32_t part_value) { int ret = 0; if (part_offset >= TOTAL_PART_CNT) { ret = 1; } else { if (0 != (*conflict_bitset & (1 << part_offset))) { //already has data in ob_time.part_[part_name], validate it if (part_value != ob_time->parts_[part_offset]) { ret = 1; } } else { *conflict_bitset |= (1 << part_offset); ob_time->parts_[part_offset] = part_value; } } return (ret); } int check_int_value_length(const struct ObDFMParseCtx *ctx, const int64_t expected_len, const int64_t real_data_len) { int ret = 0; /* * format need separate chars but input omit separate chars like: * to_date('20181225', 'YYYY-MM-DD') input omit '-' * in this situation, the numeric value are matched in fixed length mode. * which means real_data_len should be equal to element expected length, or will return with an error */ if (0 == ret && ctx->is_matching_by_expected_len_) { //is true only in only in str_to_ob_time_oracle_dfm my_bool legal = TRUE; if (RR == ctx->expected_elem_flag_ || RRRR == ctx->expected_elem_flag_) { //one special case legal = (2 == real_data_len || 4 == real_data_len); } else if (expected_len > 0) { //usual case, for numeric value legal = (real_data_len == expected_len); } if (!legal) { ret = 1; } } return ret; } static int match_int_value_with_comma(struct ObDFMParseCtx *ctx, const int64_t expected_len, int64_t *value_len, int32_t *result) { int ret = 0; int32_t temp_value = 0; int64_t real_data_len = 0; int64_t digits_len = 0; int64_t continuous_comma_count = 0; my_bool stop_flag = FALSE; if (!(ctx->cur_ch_ != NULL && ctx->remain_len_ > 0)) { ret = 1; } while (0 == ret && !stop_flag && real_data_len < ctx->remain_len_ && digits_len < expected_len) { //look digits by # of value_len char cur_char = *(ctx->cur_ch_ + real_data_len); if (',' == cur_char) { continuous_comma_count++; if (continuous_comma_count == 2) { --real_data_len; stop_flag = TRUE; } else { ++real_data_len; } } else { continuous_comma_count = 0; if (is_split_char(cur_char)) { stop_flag = TRUE; } else { if (!isdigit(cur_char)) { ret = 1; //ORA-01858: a non-numeric character was found where a numeric was expected } else { temp_value *= 10; temp_value += cur_char - '0'; ++real_data_len; ++digits_len; } } } } if (0 == ret) { if (0 != (ret = check_int_value_length(ctx, expected_len, real_data_len))) { } else { *value_len = real_data_len; *result = temp_value; } } return (ret); } int match_int_value_with_sign(struct ObDFMParseCtx *ctx, const int64_t expected_len, int64_t *value_len, int32_t *result, int32_t value_sign) { //only unsigned int int ret = 0; int32_t temp_value = 0; int64_t real_data_len = 0; int64_t date_max_len = 0; if (!(ctx->cur_ch_ != NULL && ctx->remain_len_ > 0) || (expected_len < 0) || (value_sign != -1 && value_sign != 1)) { ret = 1; } else if (!isdigit(ctx->cur_ch_[0])) { //check the first char ret = 1; //ORA-01858: a non-numeric character was found where a numeric was expected } date_max_len = MIN(ctx->remain_len_, expected_len); while (0 == ret && real_data_len < date_max_len && isdigit(ctx->cur_ch_[real_data_len])) { int32_t cur_digit = (int32_t)(ctx->cur_ch_[real_data_len] - '0'); if (temp_value * 10LL > INT32_MAX - cur_digit) { ret = 1; } else { temp_value = temp_value * 10 + cur_digit; ++real_data_len; } } if (0 == ret) { if (0 != (ret = check_int_value_length(ctx, expected_len, real_data_len))) { } else { *value_len = real_data_len; *result = temp_value * value_sign; } } return (ret); } int match_int_value(struct ObDFMParseCtx *ctx, const int64_t expected_len, int64_t *value_len, int32_t *result) { return match_int_value_with_sign(ctx, expected_len, value_len, result, 1); } int match_chars_until_space(struct ObDFMParseCtx *ctx, char **result, int64_t *result_len, int64_t *value_len) { int ret = 0; int32_t str_len = 0; if (0 == ctx->remain_len_) { ret = 1; } while (0 == ret && str_len < ctx->remain_len_ && !isspace(ctx->cur_ch_[str_len])) { if (str_len >= *value_len) { ret = 1; } else { ++str_len; } } if (0 == ret) { *result = (char *)ctx->cur_ch_; *result_len = str_len; *value_len = str_len; } return (ret); } int32_t ob_time_to_date(struct ObTime *ob_time) { int32_t value = 0; if (ZERO_DATE == ob_time->parts_[DT_DATE] && !HAS_TYPE_ORACLE(ob_time->mode_)) { value = ZERO_DATE; } else { int32_t days_of_years; int32_t leap_year_count; int32_t *parts = ob_time->parts_; parts[DT_YDAY] = DAYS_UNTIL_MON[IS_LEAP_YEAR(parts[DT_YEAR])][parts[DT_MON] - 1] + parts[DT_MDAY]; days_of_years = (parts[DT_YEAR] - EPOCH_YEAR4) * DAYS_PER_NYEAR; leap_year_count = LEAP_YEAR_COUNT(parts[DT_YEAR] - 1) - LEAP_YEAR_COUNT(EPOCH_YEAR4 - 1); value = (int32_t)(days_of_years + leap_year_count + parts[DT_YDAY] - 1); parts[DT_WDAY] = WDAY_OFFSET[value % DAYS_PER_WEEK ][EPOCH_WDAY]; } return value; } static inline my_bool match_pattern_ignore_case(struct ObDFMParseCtx *ctx, const struct ObTimeConstStr *pattern) { my_bool ret_bool = FALSE; if (ctx->remain_len_ >= pattern->len_) { ret_bool = (0 == strncasecmp(ctx->cur_ch_, pattern->ptr_, pattern->len_)); } else { //false } return ret_bool; } static inline my_bool elem_has_meridian_indicator(OB_BITMAP *flag_bitmap) { return ob_bitmap_is_set(flag_bitmap, AM) || ob_bitmap_is_set(flag_bitmap, PM) || ob_bitmap_is_set(flag_bitmap, AM2) || ob_bitmap_is_set(flag_bitmap, PM2); } int64_t skip_separate_chars_with_limit(struct ObDFMParseCtx *ctx, const int64_t limit, const int64_t except_char) { int64_t sep_len = 0; while (sep_len < ctx->remain_len_ && sep_len < limit && is_split_char(ctx->cur_ch_[sep_len]) && (int64_t)(ctx->cur_ch_[sep_len]) != except_char) { sep_len++; } ctx->cur_ch_ += sep_len; ctx->remain_len_ -= sep_len; return sep_len; } int64_t skip_separate_chars(struct ObDFMParseCtx *ctx) { return skip_separate_chars_with_limit(ctx, MAX_VARCHAR_LENGTH, INT64_MAX); } int64_t skip_blank_chars(struct ObDFMParseCtx *ctx) { int64_t blank_char_len = 0; while (blank_char_len < ctx->remain_len_ && ' ' == ctx->cur_ch_[blank_char_len]) { blank_char_len++; } ctx->cur_ch_ += blank_char_len; ctx->remain_len_ -= blank_char_len; return blank_char_len; } const char *find_first_separator(struct ObDFMParseCtx *ctx) { const char *result = NULL; int64_t i = 0; for (; NULL == result && i < ctx->remain_len_; i++) { if (is_split_char(ctx->cur_ch_[i])) { result = ctx->cur_ch_ + i; } } return result; } static int check_semantic(const DYNAMIC_ARRAY *dfm_elements, OB_BITMAP *flag_bitmap, uint64_t mode) { int ret = 0; int64_t i = 0; int64_t conflict_group_bitset = 0; ob_bitmap_clear_all(flag_bitmap); for (; 0 == ret && i < dfm_elements->elements; ++i) { struct ObDFMElem *elem = dynamic_element(dfm_elements, i, struct ObDFMElem *); int64_t flag = elem->elem_flag_; if (!(flag > INVALID_FLAG && flag < MAX_FLAG_NUMBER)) { ret = 1; } //The following datetime format elements can be used in timestamp and interval format models, //but not in the original DATE format model: FF, TZD, TZH, TZM, and TZR if (0 == ret) { if (flag >= FF1 && flag <= FF && !HAS_TYPE_ORACLE(mode)) { ret = 1; } else if (!HAS_TYPE_TIMEZONE(mode) && (TZD == flag || TZR ==flag || TZH == flag || TZM == flag)) { ret = 1; } } //check no duplicate elem first if (0 == ret) { if ( ob_bitmap_is_set(flag_bitmap, flag)) { ret = 1; //ORA-01810: format code appears twice } else { ob_bitmap_set_bit(flag_bitmap, flag); } } //check conflict in group which the element belongs to if (0 == ret) { int64_t conf_group = CONFLICT_GROUP_MAP[flag]; if (conf_group >= 0) { if (0 != (conflict_group_bitset & (1 << conf_group))) { ret = 1; } else { conflict_group_bitset |= (1 << conf_group); } } }//end if }//end for if (0 == ret) { if ( ob_bitmap_is_set(flag_bitmap, TZM) && ! ob_bitmap_is_set(flag_bitmap, TZH)) { ret = 1; } } return (ret); } /* search matched pattern */ int parse_one_elem(struct ObDFMParseCtx *ctx, struct ObDFMElem *elem) { int ret = 0; if (!(ctx->cur_ch_ != NULL && ctx->remain_len_ > 0)) { ret = 1; } else { int64_t winner_flag = INVALID_FLAG; int64_t max_matched_len = 0; int64_t flag = 0; for (;flag < MAX_FLAG_NUMBER; ++flag) { const struct ObTimeConstStr *pattern = &PATTERNEX[flag]; if (max_matched_len < pattern->len_ && match_pattern_ignore_case(ctx, pattern)) { winner_flag = flag; max_matched_len = pattern->len_; } } //uppercase adjust if (0 == ret) { if (winner_flag != INVALID_FLAG) { elem->elem_flag_ = winner_flag; elem->offset_ = ctx->cur_ch_ - ctx->fmt_str_; switch (winner_flag) { case MON: case MONTH: case DAY: case DY: case AM: case PM: case AD: case BC: { if (ctx->remain_len_ < 2) { ret = 1; } else if (isupper(ctx->cur_ch_[0]) && isupper(ctx->cur_ch_[1])) { elem->upper_case_mode_ = ALL_CHARACTER; } else if (isupper(ctx->cur_ch_[0])) { elem->upper_case_mode_ = ONLY_FIRST_CHARACTER; } else { elem->upper_case_mode_ = NON_CHARACTER; } break; } case AM2: case PM2: case AD2: case BC2: { if (ctx->remain_len_ < 4) { ret = 1; } else if (isupper(ctx->cur_ch_[0])) { elem->upper_case_mode_ = ALL_CHARACTER; } else { elem->upper_case_mode_ = NON_CHARACTER; } } default: //do nothing break; } ctx->cur_ch_ += max_matched_len; ctx->remain_len_ -= max_matched_len; } else { ret = 1; } } } return (ret); } int parse_datetime_format_string(const char *fmt_str, const int64_t fmt_len, DYNAMIC_ARRAY *array) { int ret = 0; if (fmt_str == NULL || fmt_len == 0) { //do nothing } else { int64_t skipped_len = 0; struct ObDFMParseCtx parse_ctx; parse_ctx.fmt_str_ = fmt_str; parse_ctx.cur_ch_ = fmt_str; parse_ctx.remain_len_ = fmt_len; parse_ctx.expected_elem_flag_ = INVALID_FLAG; parse_ctx.is_matching_by_expected_len_ = FALSE; while ((0 == ret) && parse_ctx.remain_len_ > 0) { //skip separate chars skipped_len = skip_separate_chars(&parse_ctx); //parse one element from head if (parse_ctx.remain_len_ > 0) { struct ObDFMElem value_elem; value_elem.elem_flag_ = INVALID_FLAG; value_elem.offset_ = -1; value_elem.is_single_dot_before_ = FALSE; value_elem.upper_case_mode_ = NON_CHARACTER; if((int64_t)(parse_ctx.cur_ch_ - parse_ctx.fmt_str_) > 0) { value_elem.is_single_dot_before_ = (skipped_len == 1 && '.' == parse_ctx.cur_ch_[-1]); } if (0 != (ret = (parse_one_elem(&parse_ctx, &value_elem)))) { } else if (ma_insert_dynamic(array, &value_elem)) { ret = 1; } } } } return (ret); } int special_mode_sprintf(char *buf, const int64_t buf_len, int64_t *pos, const struct ObTimeConstStr *str, const enum UpperCaseMode mode, int64_t padding) { int ret = 0; if (str->len_ <= 0 || NULL == str->ptr_ || NULL == buf || (padding > 0 && padding < str->len_)) { ret = 1; } else if (*pos + (padding > 0 ? padding : str->len_) >= buf_len) { ret = 1; } else { int64_t i = 0; for (i = 0; 0 == ret && i < str->len_; ++i) { char cur_char = str->ptr_[i]; if ((cur_char >= 'a' && cur_char <= 'z') || (cur_char >= 'A' && cur_char <= 'Z')) { switch (mode) { case ALL_CHARACTER: { buf[(*pos)++] = (char)(toupper(cur_char)); break; } case ONLY_FIRST_CHARACTER: { if (i == 0) { buf[(*pos)++] = (char)(toupper(cur_char)); } else { buf[(*pos)++] = (char)(tolower(cur_char)); } break; } case NON_CHARACTER: { buf[(*pos)++] = (char)(tolower(cur_char)); break; } default: { ret = 1; break; } } } else { buf[(*pos)++] = cur_char; }//end if }//end for for (i = str->len_; i < padding; ++i) { buf[(*pos)++] = ' '; } } return (ret); } static const char DIGITS[] = "0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" "4041424344454647484950515253545556575859" "6061626364656667686970717273747576777879" "8081828384858687888990919293949596979899"; char *format_unsigned(struct ObFastFormatInt *ffi, uint64_t value) { char *ptr = ffi->buf_ + (MAX_DIGITS10_STR_SIZE - 1); uint32_t index = 0; while (value >= 100) { index = (uint32_t)(value % 100) << 1; value /= 100; *--ptr = DIGITS[index + 1]; *--ptr = DIGITS[index]; } if (value < 10) { *--ptr = (char)('0' + value); } else { index = (uint32_t)(value) << 1; *--ptr = DIGITS[index + 1]; *--ptr = DIGITS[index]; } return ptr; } static void format_signed(struct ObFastFormatInt *ffi, int64_t value) { uint64_t abs_value = (uint64_t)(value); if (value < 0) { abs_value = ~abs_value + 1; ffi->ptr_ = format_unsigned(ffi, abs_value); *--(ffi->ptr_) = '-'; } else { ffi->ptr_ = format_unsigned(ffi, abs_value); } ffi->len_ = ffi->buf_ - ffi->ptr_ + MAX_DIGITS10_STR_SIZE - 1; } int data_fmt_nd(char *buffer, int64_t buf_len, int64_t *pos, const int64_t n, int64_t target) { int ret = 0; if (n <= 0 || target < 0 || target > 999999999) { ret = 1; } else if (n > buf_len - *pos) { ret = 1; } else { struct ObFastFormatInt ffi; memset(&ffi, 0, sizeof(ffi)); format_signed(&ffi, target); if (ffi.len_ > n) { ret = 1; } else { memset(buffer + *pos, '0', n - ffi.len_); memcpy(buffer + *pos + n - ffi.len_, ffi.ptr_, ffi.len_); *pos += n; } } return (ret); } static int32_t ob_time_to_week(const struct ObTime *ob_time, ObDTMode mode) { const int32_t *parts = ob_time->parts_; int32_t is_sun_begin = IS_SUN_BEGIN(mode); int32_t is_zero_begin = IS_ZERO_BEGIN(mode); int32_t is_ge_4_begin = IS_GE_4_BEGIN(mode); int32_t week = 0; if (parts[DT_YDAY] > DAYS_PER_NYEAR - 3 && is_ge_4_begin && !is_zero_begin) { // maybe the day is in week 1 of next year. int32_t days_cur_year = DAYS_PER_YEAR[IS_LEAP_YEAR(parts[DT_YEAR])]; int32_t wday_next_yday1 = WDAY_OFFSET[days_cur_year - parts[DT_YDAY] + 1][parts[DT_WDAY]]; int32_t yday_next_week1 = YDAY_WEEK1[wday_next_yday1][is_sun_begin][is_ge_4_begin]; if (parts[DT_YDAY] >= days_cur_year + yday_next_week1) { week = 1; } } if (0 == week) { int32_t wday_cur_yday1 = WDAY_OFFSET[(1 - parts[DT_YDAY]) % DAYS_PER_WEEK][parts[DT_WDAY]]; int32_t yday_cur_week1 = YDAY_WEEK1[wday_cur_yday1][is_sun_begin][is_ge_4_begin]; if (parts[DT_YDAY] < yday_cur_week1 && !is_zero_begin) { // the day is in last week of prev year. int32_t days_prev_year = DAYS_PER_YEAR[IS_LEAP_YEAR(parts[DT_YEAR] - 1)]; int32_t wday_prev_yday1 = WDAY_OFFSET[(1 - days_prev_year - parts[DT_YDAY]) % DAYS_PER_WEEK][parts[DT_WDAY]]; int32_t yday_prev_week1 = YDAY_WEEK1[wday_prev_yday1][is_sun_begin][is_ge_4_begin]; week = (days_prev_year + parts[DT_YDAY] - yday_prev_week1 + DAYS_PER_WEEK) / DAYS_PER_WEEK; } else { week = (parts[DT_YDAY] - yday_cur_week1 + DAYS_PER_WEEK) / DAYS_PER_WEEK; } } return week; } int calculate_str_oracle_dfm_length(const struct ObTime *ob_time, const char *fmt_str, const int64_t fmt_len, int16_t scale, int64_t *len) { int ret = 0; if (NULL == fmt_str || fmt_len <=0 || scale > MAX_SCALE_FOR_ORACLE_TEMPORAL) { ret = 1; } else { int64_t last_elem_end_pos = 0; int64_t i = 0; int64_t length = 0; DYNAMIC_ARRAY dfm_elems; if (scale < 0 ) { scale = DEFAULT_SCALE_FOR_ORACLE_FRACTIONAL_SECONDS; } //1. parse element from format string if (ma_init_dynamic_array(&dfm_elems, sizeof(struct ObDFMElem), COMMON_ELEMENT_NUMBER, 0)) { ret = 1; } else if (0 != (ret = parse_datetime_format_string(fmt_str, fmt_len, &dfm_elems))) { } //2. print each element for (; 0 == ret && i < dfm_elems.elements; ++i) { struct ObDFMElem *elem = dynamic_element(&dfm_elems, i, struct ObDFMElem *); //element is valid if (!(elem->elem_flag_ > INVALID_FLAG && elem->elem_flag_ < MAX_FLAG_NUMBER && elem->offset_ >= 0)) { ret = 1; } //print separate chars between elements if (0 == ret) { int64_t separate_chars_len = elem->offset_ - last_elem_end_pos; if (separate_chars_len > 0) { length += separate_chars_len; } last_elem_end_pos = elem->offset_ + PATTERNEX[elem->elem_flag_].len_; } //print current elem if (0 == ret) { switch (elem->elem_flag_) { case AD: case BC: { //TODO wjh: NLS_LANGUAGE const struct ObTimeConstStr *target_str = ob_time->parts_[DT_YEAR] > 0 ? &PATTERNEX[AD] : &PATTERNEX[BC]; length += target_str->len_; break; } case AD2: case BC2: { //TODO wjh: NLS_LANGUAGE const struct ObTimeConstStr *str = ob_time->parts_[DT_YEAR] > 0 ? &PATTERNEX[AD2] : &PATTERNEX[BC2]; length += str->len_; break; } case CC: { length += 2; break; } case SCC: { length += 2; break; } case D: { length += 1; break; } case DAY: { //TODO wjh: NLS_LANGUAGE length += MAX_WDAY_NAME_LENGTH; break; } case DD: { length += 2; break; } case DDD: { length += 3; break; } case DY: { //TODO wjh: 1. NLS_LANGUAGE const struct ObTimeConstStr *day_str = &WDAY_ABBR_NAMES[ob_time->parts_[DT_WDAY]]; length += day_str->len_; break; } case DS: { //TODO wjh: 1. NLS_TERRITORY 2. NLS_LANGUAGE length += 2 + 1 + 2 + 1 + 1; break; } case DL: { //TODO wjh: 1. NLS_DATE_FORMAT 2. NLS_TERRITORY 3. NLS_LANGUAGE const struct ObTimeConstStr *wday_str = &WDAY_NAMES[ob_time->parts_[DT_WDAY]]; const struct ObTimeConstStr *mon_str = &MON_NAMES[ob_time->parts_[DT_MON]]; length += wday_str->len_ + 2 + mon_str->len_ + 1 + 2 + 2 + 1; break; } case FF: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else if (0 == scale) { //print nothing } else { length += scale; } break; } case FF1: case FF2: case FF3: case FF4: case FF5: case FF6: case FF7: case FF8: case FF9: { int64_t scale = elem->elem_flag_ - FF1 + 1; if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else { length += scale; } break; } case HH: case HH12: { length += 2; break; } case HH24: { length += 2; break; } case IW: { length += 2; break; } case MI: { length += 2; break; } case MM: { length += 2; break; } case MONTH: { length += MAX_MON_NAME_LENGTH; break; } case MON: { const struct ObTimeConstStr *mon_str = &MON_ABBR_NAMES[ob_time->parts_[DT_MON]]; length += mon_str->len_; break; } case AM: case PM: { const struct ObTimeConstStr *str = ob_time->parts_[DT_HOUR] >= 12 ? &PATTERNEX[PM] : &PATTERNEX[AM]; length += str->len_; break; } case AM2: case PM2: { const struct ObTimeConstStr *str = ob_time->parts_[DT_HOUR] >= 12 ? &PATTERNEX[PM2] : &PATTERNEX[AM2]; length += str->len_; break; } case Q: { length += 1; break; } case RR: { length += 2; break; } case RRRR: { length += 4; break; } case SS: { length += 2; break; } case SSSSS: { length += 5; break; } case WW: { //the first complete week of a year length += 2; break; } case W: { //the first complete week of a month length += 1; break; } case YGYYY: { length += 1 + 1 + 3; break; } case YEAR: { ret = 1; break; } case SYEAR: { ret = 1; break; } case SYYYY: { length += 5; break; } case YYYY: case IYYY: { length += 4; break; break; } case YYY: case IYY: { length += 3; break; } case YY: case IY: { length += 2; break; } case Y: case I: { length += 1; break; } case TZD: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else if (ob_time->time_zone_id_ != -1) { length += strlen(ob_time->tzd_abbr_); } else { //do nothing } break; } case TZR: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else if (ob_time->time_zone_id_ != -1) { length += strlen(ob_time->tz_name_); } else { length += 6; } break; } case TZH: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else { length += 3; } break; } case TZM: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else { length += 2; } break; } case X: { length += 1; break; } default: { ret = 1; break; } } } //end if }//end for if (0 == ret) { //print the rest separate chars int64_t separate_chars_len = fmt_len - last_elem_end_pos; if (separate_chars_len > 0) { length += separate_chars_len; } } ma_delete_dynamic(&dfm_elems); *len = length; }//end if return (ret); } int ob_time_to_str_oracle_dfm(const struct ObTime *ob_time, const char *fmt_str, const int64_t fmt_len, int16_t scale, char *buf, int64_t buf_len, int64_t *pos) { int ret = 0; if (NULL == buf || buf_len <= 0 || NULL == fmt_str || fmt_len <=0 || scale > MAX_SCALE_FOR_ORACLE_TEMPORAL) { ret = 1; } else { int64_t last_elem_end_pos = 0; int64_t i = 0; DYNAMIC_ARRAY dfm_elems; if (scale < 0 ) { scale = DEFAULT_SCALE_FOR_ORACLE_FRACTIONAL_SECONDS; } //1. parse element from format string if (ma_init_dynamic_array(&dfm_elems, sizeof(struct ObDFMElem), COMMON_ELEMENT_NUMBER, 0)) { ret = 1; } else if (0 != (ret = parse_datetime_format_string(fmt_str, fmt_len, &dfm_elems))) { } //2. print each element for (; 0 == ret && i < dfm_elems.elements; ++i) { struct ObDFMElem *elem = dynamic_element(&dfm_elems, i, struct ObDFMElem *); //element is valid if (!(elem->elem_flag_ > INVALID_FLAG && elem->elem_flag_ < MAX_FLAG_NUMBER && elem->offset_ >= 0)) { ret = 1; } //print separate chars between elements if (0 == ret) { int64_t separate_chars_len = elem->offset_ - last_elem_end_pos; if (separate_chars_len > 0) { ret = databuff_printf(buf, buf_len, pos, "%.*s", (int32_t)(separate_chars_len), fmt_str + last_elem_end_pos); } last_elem_end_pos = elem->offset_ + PATTERNEX[elem->elem_flag_].len_; } //print current elem if (0 == ret) { switch (elem->elem_flag_) { case AD: case BC: { //TODO wjh: NLS_LANGUAGE const struct ObTimeConstStr *target_str = ob_time->parts_[DT_YEAR] > 0 ? &PATTERNEX[AD] : &PATTERNEX[BC]; ret = special_mode_sprintf(buf, buf_len, pos, target_str, elem->upper_case_mode_, -1); break; } case AD2: case BC2: { //TODO wjh: NLS_LANGUAGE const struct ObTimeConstStr *str = ob_time->parts_[DT_YEAR] > 0 ? &PATTERNEX[AD2] : &PATTERNEX[BC2]; ret = special_mode_sprintf(buf, buf_len, pos, str, elem->upper_case_mode_, -1); break; } case CC: { ret = databuff_printf(buf, buf_len, pos, "%02d", (abs(ob_time->parts_[DT_YEAR]) + 99) / 100); break; } case SCC: { char symbol = ob_time->parts_[DT_YEAR] > 0 ? ' ' : '-'; ret = databuff_printf(buf, buf_len, pos, "%c%02d", symbol, (abs(ob_time->parts_[DT_YEAR]) + 99) / 100); break; } case D: { ret = databuff_printf(buf, buf_len, pos, "%d", ob_time->parts_[DT_WDAY] % 7 + 1); break; } case DAY: { //TODO wjh: NLS_LANGUAGE const struct ObTimeConstStr *day_str = &WDAY_NAMES[ob_time->parts_[DT_WDAY]]; ret = special_mode_sprintf(buf, buf_len, pos, day_str, elem->upper_case_mode_, MAX_WDAY_NAME_LENGTH); break; } case DD: { ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time->parts_[DT_MDAY]); break; } case DDD: { ret = databuff_printf(buf, buf_len, pos, "%03d", ob_time->parts_[DT_YDAY]); break; } case DY: { //TODO wjh: 1. NLS_LANGUAGE const struct ObTimeConstStr *day_str = &WDAY_ABBR_NAMES[ob_time->parts_[DT_WDAY]]; ret = special_mode_sprintf(buf, buf_len, pos, day_str, elem->upper_case_mode_, -1); break; } case DS: { //TODO wjh: 1. NLS_TERRITORY 2. NLS_LANGUAGE ret = databuff_printf(buf, buf_len, pos, "%02d/%02d/%d", ob_time->parts_[DT_MON], ob_time->parts_[DT_MDAY], ob_time->parts_[DT_YEAR]); break; } case DL: { //TODO wjh: 1. NLS_DATE_FORMAT 2. NLS_TERRITORY 3. NLS_LANGUAGE const struct ObTimeConstStr *wday_str = &WDAY_NAMES[ob_time->parts_[DT_WDAY]]; const struct ObTimeConstStr *mon_str = &MON_NAMES[ob_time->parts_[DT_MON]]; ret = databuff_printf(buf, buf_len, pos, "%.*s, %.*s %02d, %d", wday_str->len_, wday_str->ptr_, mon_str->len_, mon_str->ptr_, ob_time->parts_[DT_MDAY], ob_time->parts_[DT_YEAR]); break; } case FF: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else if (0 == scale) { //print nothing } else { ret = data_fmt_nd(buf, buf_len, pos, scale, ob_time->parts_[DT_USEC] / power_of_10[MAX_SCALE_FOR_ORACLE_TEMPORAL - scale]); } break; } case FF1: case FF2: case FF3: case FF4: case FF5: case FF6: case FF7: case FF8: case FF9: { int64_t scale = elem->elem_flag_ - FF1 + 1; if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else { ret = data_fmt_nd(buf, buf_len, pos, scale, ob_time->parts_[DT_USEC] / power_of_10[MAX_SCALE_FOR_ORACLE_TEMPORAL - scale]); } break; } case HH: case HH12: { int32_t h = ob_time->parts_[DT_HOUR] % 12; if (0 == h) { h = 12; } ret = databuff_printf(buf, buf_len, pos, "%02d", h); break; } case HH24: { int32_t h = ob_time->parts_[DT_HOUR]; ret = databuff_printf(buf, buf_len, pos, "%02d", h); break; } case IW: { ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time_to_week(ob_time, WEEK_MODE[3])); break; } case J: { const int32_t base_julian_day = 2378497; // julian day of 1800-01-01 const int32_t base_date = -62091; //ob_time.parts_[DT_DATE] of 1800-01-01 if (ob_time->parts_[DT_DATE] < base_date) { ret = 1; } else { int32_t julian_day = base_julian_day + ob_time->parts_[DT_DATE] - base_date; ret = databuff_printf(buf, buf_len, pos, "%07d", julian_day); } break; } case MI: { ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time->parts_[DT_MIN]); break; } case MM: { ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time->parts_[DT_MON]); break; } case MONTH: { const struct ObTimeConstStr *mon_str = &MON_NAMES[ob_time->parts_[DT_MON]]; ret = special_mode_sprintf(buf, buf_len, pos, mon_str, elem->upper_case_mode_, MAX_MON_NAME_LENGTH); break; } case MON: { const struct ObTimeConstStr *mon_str = &MON_ABBR_NAMES[ob_time->parts_[DT_MON]]; ret = special_mode_sprintf(buf, buf_len, pos, mon_str, elem->upper_case_mode_, -1); break; } case AM: case PM: { const struct ObTimeConstStr *str = ob_time->parts_[DT_HOUR] >= 12 ? &PATTERNEX[PM] : &PATTERNEX[AM]; ret = special_mode_sprintf(buf, buf_len, pos, str, elem->upper_case_mode_, -1); break; } case AM2: case PM2: { const struct ObTimeConstStr *str = ob_time->parts_[DT_HOUR] >= 12 ? &PATTERNEX[PM2] : &PATTERNEX[AM2]; ret = special_mode_sprintf(buf, buf_len, pos, str, elem->upper_case_mode_, -1); break; } case Q: { ret = databuff_printf(buf, buf_len, pos, "%d", (ob_time->parts_[DT_MON] + 2) / 3); break; } case RR: { ret = databuff_printf(buf, buf_len, pos, "%02d", (ob_time->parts_[DT_YEAR]) % 100); break; } case RRRR: { ret = databuff_printf(buf, buf_len, pos, "%04d", ob_time->parts_[DT_YEAR]); break; } case SS: { ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time->parts_[DT_SEC]); break; } case SSSSS: { ret = databuff_printf(buf, buf_len, pos, "%05d", ob_time->parts_[DT_HOUR] * 3600 + ob_time->parts_[DT_MIN] * 60 + ob_time->parts_[DT_SEC]); break; } case WW: { //the first complete week of a year ret = databuff_printf(buf, buf_len, pos, "%02d", (ob_time->parts_[DT_YDAY] - 1) / 7 + 1); break; } case W: { //the first complete week of a month ret = databuff_printf(buf, buf_len, pos, "%d", (ob_time->parts_[DT_MDAY] - 1) / 7 + 1); break; } case YGYYY: { ret = databuff_printf(buf, buf_len, pos, "%d,%03d", abs(ob_time->parts_[DT_YEAR]) / 1000, abs(ob_time->parts_[DT_YEAR]) % 1000); break; } case YEAR: { ret = 1; break; } case SYEAR: { ret = 1; break; } case SYYYY: { const char* fmt_str = ob_time->parts_[DT_YEAR] < 0 ? "-%04d" : " %04d"; ret = databuff_printf(buf, buf_len, pos, fmt_str, abs(ob_time->parts_[DT_YEAR])); break; } case YYYY: case IYYY: { ret = databuff_printf(buf, buf_len, pos, "%04d", abs(ob_time->parts_[DT_YEAR])); break; } case YYY: case IYY: { ret = databuff_printf(buf, buf_len, pos, "%03d", abs(ob_time->parts_[DT_YEAR] % 1000)); break; } case YY: case IY: { ret = databuff_printf(buf, buf_len, pos, "%02d", abs(ob_time->parts_[DT_YEAR] % 100)); break; } case Y: case I: { ret = databuff_printf(buf, buf_len, pos, "%01d", abs(ob_time->parts_[DT_YEAR] % 10)); break; } case TZD: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else if (ob_time->time_zone_id_ != -1) { ret = databuff_printf(buf, buf_len, pos, "%.*s", (int)strlen(ob_time->tzd_abbr_), ob_time->tzd_abbr_); } else { //do nothing } break; } case TZR: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else if (ob_time->time_zone_id_ != -1) { ret = databuff_printf(buf, buf_len, pos, "%.*s", (int)strlen(ob_time->tz_name_), ob_time->tz_name_); } else { const char* fmt_str = ob_time->parts_[DT_OFFSET_MIN] < 0 ? "-%02d:%02d" : "+%02d:%02d"; ret = databuff_printf(buf, buf_len, pos, fmt_str, abs(ob_time->parts_[DT_OFFSET_MIN]) / 60, abs(ob_time->parts_[DT_OFFSET_MIN]) % 60); } break; } case TZH: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else { const char* fmt_str = ob_time->parts_[DT_OFFSET_MIN] < 0 ? "-%02d" : "+%02d"; ret = databuff_printf(buf, buf_len, pos, fmt_str, abs(ob_time->parts_[DT_OFFSET_MIN]) / 60); } break; } case TZM: { if (!HAS_TYPE_ORACLE(ob_time->mode_)) { ret = 1; } else { ret = databuff_printf(buf, buf_len, pos, "%02d", abs(ob_time->parts_[DT_OFFSET_MIN]) % 60); } break; } case X: { ret = databuff_printf(buf, buf_len, pos, "."); break; } default: { ret = 1; break; } } //end switch if (0 != ret) { } } //end if }//end for if (0 == ret) { //print the rest separate chars int64_t separate_chars_len = fmt_len - last_elem_end_pos; if (separate_chars_len > 0) { if (0 != (ret = databuff_printf(buf, buf_len, pos, "%.*s", (int32_t)(separate_chars_len), fmt_str + last_elem_end_pos))) { } } } ma_delete_dynamic(&dfm_elems); }//end if return (ret); } //TODO be delete, not to use /** * @brief convert string to ob_time struct according to oracle datetime format model * @param in: str input string * @param in: format format string * @param out: ob_time memory struct of datetime * @param out: scale scale of fractional seconds */ int str_to_ob_time_oracle_dfm(const char *str, const int64_t str_len, const char *fmt_str, const int64_t fmt_len, struct ObTime *ob_time, int16_t scale) { int ret = 0; if (NULL == str || str_len <= 0 || NULL == fmt_str || fmt_len <=0 || scale > MAX_SCALE_FOR_ORACLE_TEMPORAL) { ret = 1; } else { memset(ob_time, 0, sizeof(struct ObTime)); ob_time->mode_ |= DT_TYPE_DATETIME; ob_time->mode_ |= DT_TYPE_ORACLE; ob_time->mode_ |= DT_TYPE_TIMEZONE; } if (0 == ret) { DYNAMIC_ARRAY dfm_elems; OB_BITMAP elem_flags = {.bitmap = NULL, .n_bits = 0}; int32_t temp_tzh_value = -1; //positive value is legal int32_t temp_tzm_value = -1; //positive value is legal int32_t temp_tz_factor = 0; int32_t tz_hour = 0; //will be negetive when time zone offset < 0 int32_t tz_min = 0; //will be negetive when time zone offset < 0 //1. parse element from format string if (ma_init_dynamic_array(&dfm_elems, sizeof(struct ObDFMElem), COMMON_ELEMENT_NUMBER, 0)) { ret = 1; } else if (ob_bitmap_init(&elem_flags, MAX_FLAG_NUMBER)) { ret = 1; } else if (0 != (ret = parse_datetime_format_string(fmt_str, fmt_len, &dfm_elems))) { } else if (0 != (ret = check_semantic(&dfm_elems, &elem_flags, ob_time->mode_))) { } else { //3. go through each element, set and check conflict for ob_time parts: Year, Month, Day, Hour, Minute, Second int64_t last_elem_end_pos = 0; int64_t conflict_part_bitset = 0; int64_t elem_idx = 0; int64_t input_sep_len = 0; int64_t part_blank1_len = 0; int64_t part_sep_len = 0; int64_t part_blank2_len = 0; int64_t format_sep_len = 0; //static_assert((1 << TOTAL_PART_CNT) < INT64_MAX, "for time_part_conflict_bitset"); int32_t yday_temp_value = ZERO_DATE; //as invalid value int32_t wday_temp_value = ZERO_DATE; //as invalid value int32_t date_temp_value = ZERO_DATE; //as invalid value int32_t hh12_temp_value = ZERO_TIME; int32_t julian_year_value = ZERO_DATE; //as invalid value my_bool is_after_noon = FALSE; my_bool is_before_christ = FALSE; my_bool has_digit_tz_in_TZR = FALSE; int64_t first_non_space_sep_char = INT64_MAX; int64_t ignore_fs_flag = FALSE; struct ObDFMParseCtx ctx; ctx.fmt_str_ = str; ctx.cur_ch_ = str; ctx.remain_len_ = str_len; ctx.expected_elem_flag_ = INVALID_FLAG; ctx.is_matching_by_expected_len_ = FALSE; for (elem_idx = 0; 0 == ret && elem_idx < dfm_elems.elements; ++elem_idx) { struct ObDFMElem *elem = dynamic_element(&dfm_elems, elem_idx, struct ObDFMElem *); //element is valid if (!(elem->elem_flag_ > INVALID_FLAG && elem->elem_flag_ < MAX_FLAG_NUMBER && elem->offset_ >= 0)) { ret = 1; } else { } //1. check separate chars and skip blank chars first if (0 == ret) { //get format sep chars len format_sep_len = elem->offset_ - last_elem_end_pos; last_elem_end_pos = elem->offset_ + PATTERNEX[elem->elem_flag_].len_; //parse input string and skip them part_blank1_len = skip_blank_chars(&ctx); //The # of skipped non-blank chars is according to format_str first_non_space_sep_char = (0 == ctx.remain_len_) ? INT64_MAX : ctx.cur_ch_[0]; if (X == elem->elem_flag_) { /* 特殊情况, 不考虑X前面的非空白分隔符 */ part_sep_len = 0; } else { part_sep_len = skip_separate_chars_with_limit(&ctx, format_sep_len, INT64_MAX); } part_blank2_len = skip_blank_chars(&ctx); input_sep_len = part_blank1_len + part_sep_len + part_blank2_len; } if (0 == ret && (0 == ctx.remain_len_)) { break; //if all the input chars has beeen processed, break this loop } //2. next, parse the current element if (0 == ret) { int64_t parsed_elem_len = 0; const int64_t expected_elem_len = EXPECTED_MATCHING_LENGTH[elem->elem_flag_]; ctx.expected_elem_flag_ = elem->elem_flag_; ctx.is_matching_by_expected_len_ = (format_sep_len > 0 && input_sep_len == 0); switch (elem->elem_flag_) { case AD: case BC: case AD2: case BC2: { //TODO wjh: NLS_LANGUAGE my_bool is_with_dot = (AD2 == elem->elem_flag_|| BC2 == elem->elem_flag_); my_bool is_ad = FALSE; my_bool is_bc = FALSE; parsed_elem_len = is_with_dot ? PATTERNEX[AD2].len_ : PATTERNEX[AD].len_; is_ad = match_pattern_ignore_case(&ctx, is_with_dot ? &PATTERNEX[AD2] : &PATTERNEX[AD]); is_bc = match_pattern_ignore_case(&ctx, is_with_dot ? &PATTERNEX[BC2] : &PATTERNEX[BC]); if (!is_ad && !is_bc) { ret = 1; } else { is_before_christ = is_bc; } break; } case D: { int32_t wday = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &wday))) { } else if (0 != (ret = check_validate(&LIMITER_WEEK_DAY, wday))) { } else { //oracle numbered sunday as 1 in territory of CHINA //TODO wjh: hard code for now, need look up NLS_TERRITORIES wday_temp_value = (wday + 5) % 7 + 1; } break; } case DY: case DAY: { //TODO wjh: NLS_LANGUAGE NLS_TERRITORIES int32_t wday = 0; for (wday = 1; wday <= DAYS_PER_WEEK; ++wday) { const struct ObTimeConstStr *day_str = (elem->elem_flag_ == DAY) ? &WDAY_NAMES[wday] : &WDAY_ABBR_NAMES[wday]; if (match_pattern_ignore_case(&ctx, day_str)) { parsed_elem_len = day_str->len_; break; } } if (0 != (ret = check_validate(&LIMITER_WEEK_DAY, wday))) { } else { wday_temp_value = wday; } break; } case DD: { int32_t mday = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &mday))) { } else if (0 != (ret = check_validate(&LIMITER_MONTH_DAY, mday))) { } else { //may conflict with DDD ret = set_ob_time_part_directly(ob_time, &conflict_part_bitset, DT_MDAY, mday); } break; } case DDD: { int32_t yday = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &yday))) { } else if (0 != (ret = check_validate(&LIMITER_YEAR_DAY, yday))) { } else { yday_temp_value = yday; } break; } case DS: { //TODO wjh: impl it NEED NLS_DATE_FORMAT NLS_TERRITORY NLS_LANGUAGE ret = 1; break; } case DL: { //TODO wjh: impl it NEED NLS_DATE_FORMAT NLS_TERRITORY NLS_LANGUAGE ret = 1; break; } case FF: case FF1: case FF2: case FF3: case FF4: case FF5: case FF6: case FF7: case FF8: case FF9: { int32_t usec = 0; //format string has '.' or 'X', but input string does not contain '.' //do nothing, skip element FF and revert ctx by the length of parsed chars if (ignore_fs_flag) { ctx.cur_ch_ -= (part_blank1_len + part_sep_len + part_blank2_len); ctx.remain_len_ += (part_blank1_len + part_sep_len + part_blank2_len); } else if (elem->is_single_dot_before_ && '.' != first_non_space_sep_char) { ctx.cur_ch_ -= (part_sep_len + part_blank2_len); ctx.remain_len_ += (part_sep_len + part_blank2_len); } else if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &usec))) { } else { scale = (int16_t)(parsed_elem_len); usec = (int32_t)(usec * power_of_10[MAX_SCALE_FOR_ORACLE_TEMPORAL - parsed_elem_len]); ob_time->parts_[DT_USEC] = usec; } break; } case TZH: case TZM: { if ( ob_bitmap_is_set(&elem_flags, TZR) || ob_bitmap_is_set(&elem_flags, TZD)) { ret = 1; } else { int32_t value = 0; int32_t local_tz_factor = 1; /* * SQL> alter session set NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH.MI.SS AM TZH:TZM'; * SQL> alter session set time_zone='Asia/Shanghai'; * SQL> select cast('01-SEP-20 11.11.11' as timestamp with time zone) from dual; * 01-SEP-20 11.11.11 AM +08:00 */ ob_time->is_tz_name_valid_ = FALSE; if (TZH == elem->elem_flag_) { if (is_sign_char(ctx.cur_ch_[0])) { local_tz_factor = ('-' == ctx.cur_ch_[0] ? -1 : 1); ctx.cur_ch_ += 1; ctx.remain_len_ -= 1; } else { if (((int64_t)(ctx.cur_ch_ - ctx.fmt_str_)) > 0 && input_sep_len > format_sep_len) { //if the input valid separate chars > format separate chars //the superfluous '-' will be regarded as minus sign local_tz_factor = ((int64_t)('-') == ctx.cur_ch_[-1] ? -1 : 1); } } temp_tz_factor = local_tz_factor; //1 or -1, but never be 0 } if (0 == ctx.remain_len_) { //do nothing } else if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &value))) { } else if (0 != (ret = (elem->elem_flag_ == TZH ? check_validate(&LIMITER_TIMEZONE_HOUR_ABS, value) : check_validate(&LIMITER_TIMEZONE_MIN_ABS, value)))) { } else { if (elem->elem_flag_ == TZH) { temp_tzh_value = value; } else { temp_tzm_value = value; } } } break; } case TZR: { if ( ob_bitmap_is_set(&elem_flags, TZH) || ob_bitmap_is_set(&elem_flags, TZM)) { ret = 1; } else { int32_t local_tz_factor = 1; if (isdigit(ctx.cur_ch_[0]) || is_sign_char(ctx.cur_ch_[0])) { //case1: digits int32_t tmp_tz_hour = 0; int32_t tmp_tz_min = 0; char *digits_timezone; int64_t digits_timezone_len; if (is_sign_char(ctx.cur_ch_[0])) { local_tz_factor = ('-' == ctx.cur_ch_[0] ? -1 : 1); ctx.cur_ch_ += 1; ctx.remain_len_ -= 1; } else if (part_blank1_len + part_sep_len > format_sep_len && is_sign_char(ctx.cur_ch_[-1])) { local_tz_factor = ('-' == ctx.cur_ch_[-1] ? -1 : 1); } parsed_elem_len = UNKNOWN_LENGTH_OF_ELEMENT; if (!(0 == ctx.remain_len_)) { ob_time->is_tz_name_valid_ = FALSE; } if (0 == ctx.remain_len_) { //do nothing } else if (0 != (ret = match_chars_until_space(&ctx, &digits_timezone, &digits_timezone_len, &parsed_elem_len))) { //do nothing } else { const char *local_sep = NULL; struct ObDFMParseCtx local_ctx; local_ctx.fmt_str_ = digits_timezone; local_ctx.cur_ch_ = digits_timezone; local_ctx.remain_len_ = digits_timezone_len; local_ctx.expected_elem_flag_ = INVALID_FLAG; local_ctx.is_matching_by_expected_len_ = FALSE; local_sep = find_first_separator(&local_ctx); if (NULL == local_sep) { } else { int64_t hour_expected_len = local_sep - digits_timezone; int64_t local_parsed_len = 0; if (0 != (ret = match_int_value_with_sign(&local_ctx, hour_expected_len, &local_parsed_len, &tmp_tz_hour, local_tz_factor))) { } else if (0 != (ret = check_validate(&LIMITER_TIMEZONE_HOUR_ABS, tmp_tz_hour))) { } else if (hour_expected_len != local_parsed_len) { ret = 1; //invalid time zone } else { local_ctx.cur_ch_ += hour_expected_len + 1; local_ctx.remain_len_ -= hour_expected_len + 1; if (0 == local_ctx.remain_len_) { has_digit_tz_in_TZR = TRUE; tz_hour = tmp_tz_hour; tz_min = tmp_tz_min; } else if (0 != (ret = match_int_value_with_sign(&local_ctx, parsed_elem_len - hour_expected_len - 1, &local_parsed_len, &tmp_tz_min, local_tz_factor))) { } else if (0 != (ret = check_validate(&LIMITER_TIMEZONE_MIN_ABS, tmp_tz_min))) { } else if (parsed_elem_len != hour_expected_len + local_parsed_len + 1) { ret = 1; //invalid time zone } else { has_digit_tz_in_TZR = TRUE; tz_hour = tmp_tz_hour; tz_min = tmp_tz_min; } } } } } else { //case2: strings char *tzr_str; int64_t tzr_str_len; parsed_elem_len = OB_MAX_TZ_NAME_LEN - 1; if (0 != (ret = match_chars_until_space(&ctx, &tzr_str, &tzr_str_len, &parsed_elem_len))) { } else { memcpy(ob_time->tz_name_, tzr_str, tzr_str_len); ob_time->tz_name_[tzr_str_len] = '\0'; ob_time->is_tz_name_valid_ = TRUE; } } } break; } case TZD: { if ( ob_bitmap_is_set(&elem_flags, TZH) || ob_bitmap_is_set(&elem_flags, TZM)) { ret = 1; } else if (has_digit_tz_in_TZR) { ret = 1; } else { char *tzd_str; int64_t tzd_str_len; parsed_elem_len = OB_MAX_TZ_ABBR_LEN - 1; if (0 != (ret = match_chars_until_space(&ctx, &tzd_str, &tzd_str_len, &parsed_elem_len))) { } else { memcpy(ob_time->tzd_abbr_, tzd_str, tzd_str_len); ob_time->tzd_abbr_[tzd_str_len] = '\0'; } } break; } case J: { const int32_t base_julian_day = 2378497; // julian day of 1800-01-01 const int32_t base_date = -62091; //ob_time.parts_[DT_DATE] of 1800-01-01 int32_t julian_day = 0; int32_t ob_time_date = 0; struct ObTime tmp_ob_time; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &julian_day))) { } else if (0 != (ret = check_validate(&LIMITER_JULIAN_DATE, julian_day))) { } else if (julian_day < base_julian_day) { ret = 1; } else { ob_time_date = julian_day - base_julian_day + base_date; if (0 != (ret = date_to_ob_time(ob_time_date, &tmp_ob_time))) { } else if (0 != (ret = set_ob_time_year_may_conflict(ob_time, &julian_year_value, tmp_ob_time.parts_[DT_YEAR], tmp_ob_time.parts_[DT_YEAR], TRUE /* overwrite */))) { } else { yday_temp_value = tmp_ob_time.parts_[DT_YDAY]; } } break; } case HH: case HH12: { int32_t hour = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &hour))) { } else if (0 != (ret = check_validate(&LIMITER_HOUR12, hour))) { } else if (!elem_has_meridian_indicator(&elem_flags)) { ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hour); } else { hh12_temp_value = hour; } break; } case HH24: { int32_t hour = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &hour))) { } else if (0 != (ret = check_validate(&LIMITER_HOUR24, hour))) { } else if (elem_has_meridian_indicator(&elem_flags)) { ret = 1; } else { //may conflict with SSSSS ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hour); } break; } case MI: { int32_t min = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &min))) { } else if (0 != (ret = check_validate(&LIMITER_MINUTE, min))) { } else { //may conflict with SSSSS ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_MIN, min); } break; } case MM: { int32_t mon = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &mon))) { } else if (0 != (ret = check_validate(&LIMITER_MONTH_DAY, mon))) { } else { //may conflict with DDD ret = set_ob_time_part_directly(ob_time, &conflict_part_bitset, DT_MON, mon); } break; } case MON: case MONTH: { int32_t mon = 0; for (mon = LIMITER_MONTH.MIN; mon <= LIMITER_MONTH.MAX; ++mon) { const struct ObTimeConstStr *mon_str = (elem->elem_flag_ == MONTH) ? &MON_NAMES[mon] : &MON_ABBR_NAMES[mon]; if (match_pattern_ignore_case(&ctx, mon_str)) { parsed_elem_len = mon_str->len_; break; } } if (0 != (ret = check_validate(&LIMITER_MONTH, mon))) { } else { //may conflict with DDD ret = set_ob_time_part_directly(ob_time, &conflict_part_bitset, DT_MON, mon); } break; } case AM: case PM: case AM2: case PM2: { my_bool is_with_dot = (AM2 == elem->elem_flag_|| PM2 == elem->elem_flag_); my_bool is_am = FALSE; my_bool is_pm = FALSE; parsed_elem_len = is_with_dot ? PATTERNEX[AM2].len_ : PATTERNEX[AM].len_; is_am = match_pattern_ignore_case(&ctx, is_with_dot ? &PATTERNEX[AM2] : &PATTERNEX[AM]); is_pm = match_pattern_ignore_case(&ctx, is_with_dot ? &PATTERNEX[PM2] : &PATTERNEX[PM]); if (!is_am && !is_pm) { ret = 1; } else { is_after_noon = is_pm; } break; } case RR: case RRRR: { int32_t round_year = 0; int32_t conflict_check_year = 0; if (0 != (ret = match_int_value(&ctx, 4, &parsed_elem_len, &round_year))) { } else if (parsed_elem_len > 2) { conflict_check_year = round_year; //do nothing } else { int32_t first_two_digits_of_current_year = (ob_time->parts_[DT_YEAR] / 100) % 100; int32_t last_two_digits_of_current_year = ob_time->parts_[DT_YEAR] % 100; conflict_check_year = round_year; if (round_year < 50) { //0~49 if (last_two_digits_of_current_year < 50) { round_year += first_two_digits_of_current_year * 100; } else { round_year += (first_two_digits_of_current_year + 1) * 100; } } else if (round_year < 100) { //50~99 if (last_two_digits_of_current_year < 50) { round_year += (first_two_digits_of_current_year - 1) * 100; } else { round_year += first_two_digits_of_current_year * 100; } } } if (0 == ret) { if (0 != (ret = check_validate(&LIMITER_YEAR, round_year))) { //TODO wjh: negetive year number } else if (0 != (ret = set_ob_time_year_may_conflict(ob_time, &julian_year_value, conflict_check_year, round_year, FALSE /* overwrite */))) { } } break; } case SS: { int32_t sec = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &sec))) { } else if (0 != (ret = check_validate(&LIMITER_SECOND, sec))) { } else { ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_SEC, sec); } break; } case SSSSS: { int32_t sec_past_midnight = 0; if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &sec_past_midnight))) { } else if (0 != (ret = check_validate(&LIMITER_SECS_PAST_MIDNIGHT, sec_past_midnight))) { } else { int32_t secs = sec_past_midnight % (int32_t)(SECS_PER_MIN); int32_t mins = (sec_past_midnight / (int32_t)(SECS_PER_MIN)) % (int32_t)(MINS_PER_HOUR); int32_t hours = sec_past_midnight / (int32_t)(SECS_PER_MIN) / (int32_t)(MINS_PER_HOUR); if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_SEC, secs))) { } else if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_MIN, mins))) { } else if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hours))) { } } break; } case YGYYY: { int32_t years = 0; if (0 != (ret = match_int_value_with_comma(&ctx, expected_elem_len, &parsed_elem_len, &years))) { } else if (0 != (ret = check_validate(&LIMITER_YEAR, years))) { } else if (0 != (ret = set_ob_time_year_may_conflict(ob_time, &julian_year_value, years, years, FALSE /* overwrite */))) { } break; } case SYYYY: case YYYY: case YYY: case YY: case Y: { int32_t years = 0; int32_t conflict_check_year = 0; int32_t sign = 1; if (SYYYY == elem->elem_flag_) { if (is_sign_char(ctx.cur_ch_[0])) { sign = ('-' == ctx.cur_ch_[0] ? -1 : 1); ctx.cur_ch_ += 1; ctx.remain_len_ -= 1; } else if (part_blank1_len + part_sep_len > format_sep_len && is_sign_char(ctx.cur_ch_[-1])) { sign = ('-' == ctx.cur_ch_[-1] ? -1 : 1); } } if (!(ctx.cur_ch_ != NULL && ctx.remain_len_ > 0)) { } else if (0 != (ret = match_int_value_with_sign(&ctx, expected_elem_len, &parsed_elem_len, &years, sign))) { } if (0 == ret) { conflict_check_year = years; if (expected_elem_len < 4) { years += (ob_time->parts_[DT_YEAR] / (int32_t)(power_of_10[parsed_elem_len])) * (int32_t)(power_of_10[parsed_elem_len]); } if (0 != (ret = check_validate(&LIMITER_YEAR, years))) { } else if (0 != (ret = set_ob_time_year_may_conflict(ob_time, &julian_year_value, conflict_check_year, years, FALSE /* overwrite */))) { } } break; } case X: { if ('.' != ctx.cur_ch_[0]) { ignore_fs_flag = TRUE; ctx.cur_ch_ -= (part_blank1_len + part_sep_len + part_blank2_len); ctx.remain_len_ += (part_blank1_len + part_sep_len + part_blank2_len); } else { parsed_elem_len = 1; } break; } case CC: case SCC: case IW: case W: case WW: case YEAR: case SYEAR: case Q: case I: case IY: case IYY: case IYYY: { ret = 1; break; } default: { ret = 1; break; } } //end switch if (0 == ret) { ctx.cur_ch_ += parsed_elem_len; ctx.remain_len_ -= parsed_elem_len; } }//end if if (0 != ret) { } else { } } //end for //check if the unprocessed elems has permission to be omitted if (0 == ret) { for (; elem_idx < dfm_elems.elements; ++elem_idx) { struct ObDFMElem *elem = dynamic_element(&dfm_elems, elem_idx, struct ObDFMElem *); if (!is_element_can_omit(elem)) { ret = 1; } } } if (0 == ret) { //all elems has finished, is there anything else in str? the rest must be separators, do check. int64_t str_remain_sep_len = 0; while (str_remain_sep_len < ctx.remain_len_ && is_split_char(ctx.cur_ch_[str_remain_sep_len])) { str_remain_sep_len++; } ctx.cur_ch_ += str_remain_sep_len; ctx.remain_len_ -= str_remain_sep_len; if (ctx.remain_len_ > 0) { ret = 1; } } //after noon conflict: AM PM vs HH12 HH if (0 == ret) { if (hh12_temp_value != ZERO_TIME) { //when hour value, varied by meridian indicators //if HH12 = 12, when meridian indicator 'AM' exists, the real time is hour = 0 //if HH12 = 12, when meridian indicator 'PM' exists, the real time is hour = 12 if (is_after_noon) { ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hh12_temp_value % 12 + 12); } else { ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hh12_temp_value % 12); } } } //before christ conflict: BC AD vs YEAR //TODO wjh: change this when ob_time support negetive years if (0 == ret) { if (is_before_christ) { ret = 1; } } //year cannot changed after this line //feed/validate yday + YEAR to MON and DAY if (0 == ret) { if (yday_temp_value != ZERO_DATE) { int32_t month = 0; int32_t day = 0; if (0 != (ret = get_day_and_month_from_year_day(yday_temp_value, ob_time->parts_[DT_YEAR], &month, &day))) { } else if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_MON, month))) { } else if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_MDAY, day))) { } } } //calc and validate: YDAY WDAY vs YEAR MON DAY if (0 == ret) { if (0 != (ret = validate_oracle_date(ob_time))) { } else { //ob_time_to_date func is to calc YDAY and WDAY and return DATE date_temp_value = ob_time_to_date(ob_time); //TODO: shanting if (yday_temp_value != ZERO_DATE && ob_time->parts_[DT_YDAY] != yday_temp_value) { ret = 1; } else if (wday_temp_value != ZERO_DATE && ob_time->parts_[DT_WDAY] != wday_temp_value) { ret = 1; } else { ob_time->parts_[DT_DATE] = date_temp_value; } } } //for time zone info if (0 == ret && !ob_time->is_tz_name_valid_) { //B. timezone defined by time zone hour and minute int32_t tz_offset_value = 0; if ( ob_bitmap_is_set(&elem_flags, TZH)) { my_bool has_tzh_value = (temp_tzh_value >= 0); my_bool has_tzm_value = (temp_tzm_value >= 0); if (!has_tzh_value && has_tzm_value) { ret = 1; } else if (!has_tzh_value && !has_tzm_value) { //do nothing } else if (has_tzh_value && !has_tzm_value) { tz_hour = temp_tz_factor * temp_tzh_value; tz_min = temp_tz_factor * abs(tz_min); } else { tz_hour = temp_tz_factor * temp_tzh_value; tz_min = temp_tz_factor * temp_tzm_value; } } else if ( ob_bitmap_is_set(&elem_flags, TZR)) { //do nothing } else { //no time zone info in elem_flags } //calc offset if (0 == ret) { if (tz_hour * tz_min < 0) { ret = 1; } else { tz_offset_value = (int32_t)(tz_hour * MINS_PER_HOUR + tz_min); } } //final validate if (0 == ret) { if (!(MIN_OFFSET_MINUTES <= tz_offset_value && tz_offset_value <= MAX_OFFSET_MINUTES)) { ret = 1; } else { ob_time->parts_[DT_OFFSET_MIN] = tz_offset_value; } } } if (0 != ret) { } else { } }//end if ob_bitmap_free(&elem_flags); ma_delete_dynamic(&dfm_elems); } return ret; }