2513 lines
85 KiB
C
2513 lines
85 KiB
C
/*
|
|
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 <ma_global.h>
|
|
#include <ma_sys.h>
|
|
#include "ob_oralce_format_models.h"
|
|
#include "mariadb_ctype.h"
|
|
#include <ma_string.h>
|
|
#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;
|
|
}
|