* This file is part of the oGRAC project.
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* oGRAC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* cm_interval.c
*
*
* IDENTIFICATION
* src/common/cm_interval.c
*
* -------------------------------------------------------------------------
*/
#include "cm_defs.h"
#include "cm_text.h"
#include "cm_interval.h"
#include "var_inc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CM_ZERO_INTVL_DETAIL(detail) \
do { \
(detail)->year = 0; \
(detail)->mon = 0; \
(detail)->day = 0; \
(detail)->hour = 0; \
(detail)->min = 0; \
(detail)->sec = 0; \
(detail)->fsec = 0; \
} while (0)
static inline uint32 cm_skip_digits(text_t *text)
{
uint32 len = 0;
CM_POINTER(text);
while (text->len > 0 && CM_IS_DIGIT(CM_TEXT_FIRST(text))) {
++len;
CM_REMOVE_FIRST(text);
}
return len;
}
* for reducing cyclic complexity, but also for maintenance. */
typedef enum en_itvl_errno {
IERR_SUCCESS = 0,
IERR_ERROR = 1,
IERR_NO_DIGIT,
IERR_FIELD_TOO_LONG,
IERR_UNEXPECTED_DOT,
IERR_UNEXPECTED_CHAR,
IERR_NO_FRAC_DIGIT,
IERR_NO_INDICATOR,
IERR_INVALID_INDICATOR,
IERR_DISORDERED_FIELD,
IERR_TEXT_TOO_SHORT,
IERR_ILLEGAL_YEAR_FIELD,
IERR_ILLEGAL_MONTH_FIELD,
IERR_ILLEGAL_DAY_FIELD,
IERR_ILLEGAL_HOUR_FIELD,
IERR_ILLEGAL_MINUTE_FIELD,
IERR_ILLEGAL_SECOND_FIELD,
IERR_INVALID_FORMAT,
IEER_SHORT_TIME_FIELDS,
IEER_UNDESIRED_CHARS,
IERR_YMINTERVAL_OVERFLOW,
IERR_DSINTERVAL_OVERFLOW,
IERR__NOT_USED__
} itvl_errno_t;
static const char *g_itvl_errinfos[IERR__NOT_USED__] = {
[IERR_SUCCESS] = "",
[IERR_ERROR] = "",
[IERR_NO_DIGIT] = "-- digit(s) is expected",
[IERR_FIELD_TOO_LONG] = "-- field text is too long (<=" OG_STR(ITVL_MAX_ISO_FIELD_LEN) ")",
[IERR_UNEXPECTED_DOT] = "-- unexpected decimal point",
[IERR_UNEXPECTED_CHAR] = "-- unexpected character",
[IERR_NO_FRAC_DIGIT] = "-- no fractional digits",
[IERR_NO_INDICATOR] = "-- no field indicator",
[IERR_INVALID_INDICATOR] = "-- invalid field indicator",
[IERR_DISORDERED_FIELD] = "-- trailing fields must be less significant than the previous field",
[IERR_TEXT_TOO_SHORT] = "-- text is too short",
[IERR_ILLEGAL_YEAR_FIELD] = "-- illegal YEAR field",
[IERR_ILLEGAL_MONTH_FIELD] = "-- illegal MONTH field",
[IERR_ILLEGAL_DAY_FIELD] = "-- illegal DAY field",
[IERR_ILLEGAL_HOUR_FIELD] = "-- illegal HOUR field",
[IERR_ILLEGAL_MINUTE_FIELD] = "-- illegal MINUTE field",
[IERR_ILLEGAL_SECOND_FIELD] = "-- illegal SECOND field",
[IERR_INVALID_FORMAT] = "-- format error",
[IEER_SHORT_TIME_FIELDS] = "-- time field is too short",
[IEER_UNDESIRED_CHARS] = "-- undesired character(s) followed the SECOND field",
[IERR_YMINTERVAL_OVERFLOW] = "-- out of range",
[IERR_DSINTERVAL_OVERFLOW] = "-- out of range",
};
static inline const char *cm_get_itvl_errinfo(uint32 err_no)
{
CM_ASSERT(err_no < IERR__NOT_USED__);
CM_ASSERT(err_no != IERR_SUCCESS);
return (g_itvl_errinfos[err_no] != NULL) ? g_itvl_errinfos[err_no] : "";
}
* Note: this Macro used to reduce Circle Complexity */
#define OG_THROW_ITVL_ERROR(cond, err_no) \
if (cond) { \
OG_THROW_ERROR(ERR_INVALID_INTERVAL_TEXT, \
cm_get_itvl_errinfo(err_no)); \
return OG_ERROR; \
}
* less than the current unit */
#define VERIFY_INTVL_UNIT(rank, val) \
do { \
if ((rank) <= (val)) { \
OG_THROW_ERROR(ERR_INVALID_INTERVAL_TEXT, \
cm_get_itvl_errinfo(IERR_DISORDERED_FIELD)); \
return OG_ERROR; \
} \
(rank) = (val); \
} while (0)
#define VERIFY_INTVL_UNIT_VALUE(unit_val, max_val, info) \
if ((unit_val) > (max_val)) { \
OG_THROW_ERROR(ERR_INTERVAL_FIELD_OVERFLOW, (info), (max_val)); \
return OG_ERROR; \
}
typedef union un_itvl_item {
text_t value;
struct {
char *str;
uint32 len;
bool32 has_dot;
};
} itvl_item_t;
static inline itvl_errno_t cm_fetch_iso8601_item(text_t *iso_text, itvl_item_t *item, bool32 in_datepart)
{
if (CM_IS_EMPTY(iso_text)) {
OG_THROW_ERROR(ERR_ASSERT_ERROR, "iso_text is not empty");
return IERR_ERROR;
}
item->str = iso_text->str;
item->len = cm_skip_digits(iso_text);
OG_RETVALUE_IFTRUE((item->len == 0), IERR_UNEXPECTED_CHAR);
OG_RETVALUE_IFTRUE((iso_text->len == 0), IERR_NO_INDICATOR);
cm_text_ltrim_zero((text_t *)item);
OG_RETVALUE_IFTRUE((item->len > ITVL_MAX_ISO_FIELD_LEN), IERR_FIELD_TOO_LONG);
if (!CM_IS_DOT(CM_TEXT_FIRST(iso_text))) {
item->has_dot = OG_FALSE;
return IERR_SUCCESS;
}
OG_RETVALUE_IFTRUE(in_datepart, IERR_UNEXPECTED_DOT);
CM_REMOVE_FIRST(iso_text);
uint32 fsec_prec = cm_skip_digits(iso_text);
OG_RETVALUE_IFTRUE((fsec_prec == 0), IERR_NO_FRAC_DIGIT);
OG_RETVALUE_IFTRUE((iso_text->len == 0), IERR_NO_INDICATOR);
OG_RETVALUE_IFTRUE((!IS_SECOND_INDICATOR(CM_TEXT_FIRST(iso_text))), IERR_UNEXPECTED_CHAR);
OG_RETVALUE_IFTRUE((iso_text->len > 1), IEER_UNDESIRED_CHARS);
item->len = (uint32)(iso_text->str - item->str);
item->has_dot = OG_TRUE;
return IERR_SUCCESS;
}
* @note The field text must be an unsigned integer, and the number of
* significant digits is limited to ITVL_MAX_FIELD_LEN */
static inline uint32 cm_field2value(const text_t *item)
{
uint32 value = (uint32)CM_C2D(CM_TEXT_FIRST(item));
CM_ASSERT(item->len <= ITVL_MAX_ISO_FIELD_LEN);
for (uint32 i = 1; i < item->len; i++) {
value *= 10;
value += (uint32)CM_C2D(item->str[i]);
}
return value;
}
static inline status_t cm_parse_iso_dateitem(interval_detail_t *idetail,
uint32 *mask, text_t *isotext, itvl_item_t *item)
{
if (CM_IS_EMPTY(isotext)) {
OG_THROW_ERROR(ERR_ASSERT_ERROR, "isotext is not empty");
return OG_ERROR;
}
switch (CM_TEXT_FIRST(isotext)) {
case 'Y':
VERIFY_INTVL_UNIT(*mask, IU_YEAR);
idetail->year = cm_field2value((text_t *)item);
VERIFY_INTVL_UNIT_VALUE(idetail->year, ITVL_MAX_ISO_YEAR, "YEAR");
break;
case 'M':
VERIFY_INTVL_UNIT(*mask, IU_MONTH);
idetail->mon = cm_field2value((text_t *)item);
VERIFY_INTVL_UNIT_VALUE(idetail->mon, ITVL_MAX_ISO_MONTH, "MONTH");
break;
case 'D':
VERIFY_INTVL_UNIT(*mask, IU_DAY);
idetail->day = cm_field2value((text_t *)item);
VERIFY_INTVL_UNIT_VALUE(idetail->day, ITVL_MAX_ISO_DAY, "DAY");
break;
default:
OG_THROW_ERROR(ERR_INVALID_INTERVAL_TEXT, cm_get_itvl_errinfo(IERR_UNEXPECTED_CHAR));
return OG_ERROR;
}
return OG_SUCCESS;
}
* @note The SECOND field text must be an unsigned integer, and the number of
* significant digits is also limited to ITVL_MAX_FIELD_LEN. */
static inline status_t cm_second_field2value(const itvl_item_t *item, uint32 *sec, uint32 *frac_sec)
{
double val;
if (!item->has_dot) {
*sec = cm_field2value((text_t *)item);
*frac_sec = 0;
return OG_SUCCESS;
}
if (cm_text2real_ex((text_t *)item, &val) != NERR_SUCCESS) {
return OG_ERROR;
}
CM_ASSERT(val >= 0.0 && val <= (ITVL_MAX_ISO_SECOND + 1));
*sec = (uint32)(int32)val;
*frac_sec = (uint32)(int32)rint((val - (*sec)) * (double)(ITVL_MAX_SQL_FRAC_SEC + 1));
return OG_SUCCESS;
}
static inline status_t cm_parse_iso_timeitem(interval_detail_t *idetail,
uint32 *mask, text_t *isotext, itvl_item_t *item)
{
if (CM_IS_EMPTY(isotext)) {
OG_THROW_ERROR(ERR_ASSERT_ERROR, "isotext is not empty");
return OG_ERROR;
}
switch (CM_TEXT_FIRST(isotext)) {
case 'H':
VERIFY_INTVL_UNIT(*mask, IU_HOUR);
idetail->hour = cm_field2value((text_t *)item);
break;
case 'M':
VERIFY_INTVL_UNIT(*mask, IU_MINUTE);
idetail->min = cm_field2value((text_t *)item);
break;
case 'S':
VERIFY_INTVL_UNIT(*mask, IU_SECOND);
OG_RETURN_IFERR(cm_second_field2value(item, &idetail->sec, &idetail->fsec));
break;
default:
OG_THROW_ERROR(ERR_INVALID_INTERVAL_TEXT, cm_get_itvl_errinfo(IERR_UNEXPECTED_CHAR));
return OG_ERROR;
}
return OG_SUCCESS;
}
* interval fields. The ISO 8601:2004 format requires that the trailing
* fields must be less significant than the previous field. For example,
* YEAR field is more significant than MONTH field, therefore, the YEAR
* field can not be allowed after MONTH field, e.g., P12M00Y is illegal.
* The significance of interval fields refer to the definition of
* *interval_unit_t*. The macro *VERIFY_INTVL_UNIT* is used to ensure this
* requirement.
*
* For OG_TYPE_INTERVAL_YM type, all fields can be allowed in the interval text.
* However, if the DAY or TIME fields are specified in YM_INTERVAL, they will
* be ignored. For OG_TYPE_INTERVAL_DS type, YEAR and MONTH fields are not allowed.
* Therefore, the MASK is set by IU_DS_INTERVAL.
* */
static inline uint32 cm_init_interval_rank(og_type_t itype)
{
return (itype == OG_TYPE_INTERVAL_YM) ? (uint32)IU_ALL : (uint32)IU_DS_INTERVAL;
}
* The syntax of the standard is:
* [-] P [years Y] [months M] [days D] [T [hours H] [minutes M] [seconds [. frac_secs] S ] ] */
static inline status_t cm_isotext2intvl_detail(text_t *iso_text,
og_type_t itype, interval_detail_t *idetail)
{
itvl_item_t item;
uint32 rank;
bool32 in_datepart = OG_TRUE;
itvl_errno_t err_no;
CM_POINTER2(iso_text, idetail);
OG_THROW_ITVL_ERROR((iso_text->len < 3), IERR_TEXT_TOO_SHORT);
rank = cm_init_interval_rank(itype);
CM_REMOVE_FIRST(iso_text);
while (iso_text->len > 0) {
if (IS_TIME_INDICATOR(CM_TEXT_FIRST(iso_text))) {
VERIFY_INTVL_UNIT(rank, IU_TIME);
in_datepart = OG_FALSE;
* or seconds values must be specified. */
OG_THROW_ITVL_ERROR((iso_text->len < 3), IEER_SHORT_TIME_FIELDS);
CM_REMOVE_FIRST(iso_text);
continue;
}
err_no = cm_fetch_iso8601_item(iso_text, &item, in_datepart);
if (err_no != IERR_SUCCESS) {
OG_THROW_ERROR(ERR_INVALID_INTERVAL_TEXT, cm_get_itvl_errinfo(err_no));
return OG_ERROR;
}
if (in_datepart) {
OG_RETURN_IFERR(cm_parse_iso_dateitem(idetail, &rank, iso_text, &item));
} else {
OG_RETURN_IFERR(cm_parse_iso_timeitem(idetail, &rank, iso_text, &item));
}
CM_REMOVE_FIRST(iso_text);
}
return OG_SUCCESS;
}
static inline status_t cm_fetch_sqlfmt_item(text_t *sqltext, itvl_item_t *item)
{
cm_trim_text(sqltext);
OG_THROW_ITVL_ERROR(CM_IS_EMPTY(sqltext), IERR_INVALID_FORMAT);
item->str = sqltext->str;
item->len = cm_skip_digits(sqltext);
cm_text_ltrim_zero((text_t *)item);
if (item->len == 0) {
OG_THROW_ITVL_ERROR((sqltext->len > 0), IERR_UNEXPECTED_CHAR);
OG_THROW_ITVL_ERROR((sqltext->len == 0), IERR_INVALID_FORMAT);
}
OG_THROW_ITVL_ERROR((item->len > ITVL_MAX_ISO_FIELD_LEN), IERR_FIELD_TOO_LONG);
return OG_SUCCESS;
}
static status_t cm_fetch_sqlfmt_field(text_t *sqltext, char split_char, uint32 *val)
{
itvl_item_t item;
uint32 i;
bool32 is_found = OG_FALSE;
OG_RETURN_IFERR(cm_fetch_sqlfmt_item(sqltext, &item));
if (sqltext->len == 0) {
*val = cm_field2value((text_t *)(&item));
return OG_SUCCESS;
} else {
for (i = 0; i < sqltext->len; i++) {
if (sqltext->str[i] == split_char) {
is_found = OG_TRUE;
break;
}
OG_THROW_ITVL_ERROR((sqltext->str[i] > ' '), IERR_UNEXPECTED_CHAR);
}
OG_THROW_ITVL_ERROR(!is_found, IERR_INVALID_FORMAT);
*val = cm_field2value((text_t *)(&item));
i++;
sqltext->str += i;
sqltext->len -= i;
}
return OG_SUCCESS;
}
static status_t cm_sqltext2ymintvl_detail(text_t *sqltext, interval_detail_t *idetail, uint32 fmt)
{
text_t month;
if (fmt & IU_YEAR) {
OG_RETURN_IFERR(cm_fetch_sqlfmt_field(sqltext, '-', &idetail->year));
VERIFY_INTVL_UNIT_VALUE(idetail->year, ITVL_MAX_SQL_YEAR, "YEAR");
}
if (fmt & IU_MONTH) {
cm_trim_text(sqltext);
month.str = sqltext->str;
month.len = cm_skip_digits(sqltext);
cm_text_ltrim_zero(&month);
OG_THROW_ITVL_ERROR((sqltext->len != 0), IERR_UNEXPECTED_CHAR);
OG_THROW_ITVL_ERROR((month.len == 0), IERR_INVALID_FORMAT);
OG_THROW_ITVL_ERROR((month.len > ITVL_MAX_ISO_FIELD_LEN), IERR_ILLEGAL_MONTH_FIELD);
idetail->mon = cm_field2value(&month);
}
cm_trim_text(sqltext);
OG_THROW_ITVL_ERROR(!CM_IS_EMPTY(sqltext), IERR_INVALID_FORMAT);
if (fmt & IU_YEAR) {
VERIFY_INTVL_UNIT_VALUE(idetail->mon, ITVL_MAX_SQL_MONTH, "MONTH");
}
if (idetail->mon > ITVL_MAX_SQL_MONTH) {
idetail->year += idetail->mon / 12;
idetail->mon %= 12;
VERIFY_INTVL_UNIT_VALUE(idetail->year, ITVL_MAX_SQL_YEAR, "YEAR");
}
return OG_SUCCESS;
}
static inline status_t cm_fetch_sqlfmt_second(text_t *sqltext, interval_detail_t *idetail)
{
itvl_item_t item;
OG_RETURN_IFERR(cm_fetch_sqlfmt_item(sqltext, &item));
if (sqltext->len == 0) {
item.has_dot = OG_FALSE;
} else {
uint32 fsec_prec;
OG_THROW_ITVL_ERROR((!CM_IS_DOT(CM_TEXT_FIRST(sqltext))), IERR_UNEXPECTED_CHAR);
CM_REMOVE_FIRST(sqltext);
fsec_prec = cm_skip_digits(sqltext);
OG_THROW_ITVL_ERROR((fsec_prec == 0), IERR_NO_FRAC_DIGIT);
OG_THROW_ITVL_ERROR((sqltext->len != 0), IEER_UNDESIRED_CHARS);
item.len = (uint32)(sqltext->str - item.str);
item.has_dot = OG_TRUE;
}
OG_RETURN_IFERR(cm_second_field2value(&item, &idetail->sec, &idetail->fsec));
return OG_SUCCESS;
}
static status_t cm_sqltext2dsintvl_detail(text_t *sqltext, interval_detail_t *idetail, uint32 fmt)
{
cm_trim_text(sqltext);
OG_THROW_ITVL_ERROR(CM_IS_EMPTY(sqltext), IERR_INVALID_FORMAT);
if (fmt & IU_DAY) {
OG_RETURN_IFERR(cm_fetch_sqlfmt_field(sqltext, ' ', &idetail->day));
VERIFY_INTVL_UNIT_VALUE(idetail->day, ITVL_MAX_SQL_DAY, "DAY");
}
if (fmt & IU_HOUR) {
OG_RETURN_IFERR(cm_fetch_sqlfmt_field(sqltext, ':', &idetail->hour));
}
if (fmt & IU_MINUTE) {
OG_RETURN_IFERR(cm_fetch_sqlfmt_field(sqltext, ':', &idetail->min));
}
if (fmt & IU_SECOND) {
OG_RETURN_IFERR(cm_fetch_sqlfmt_second(sqltext, idetail));
}
cm_trim_text(sqltext);
OG_THROW_ITVL_ERROR(!CM_IS_EMPTY(sqltext), IERR_INVALID_FORMAT);
if (fmt & IU_DAY) {
VERIFY_INTVL_UNIT_VALUE(idetail->hour, ITVL_MAX_SQL_HOUR, "HOUR");
}
if (fmt & (IU_DAY | IU_HOUR)) {
VERIFY_INTVL_UNIT_VALUE(idetail->min, ITVL_MAX_SQL_MINUTE, "MINUTE");
}
if (fmt & (IU_DAY | IU_HOUR | IU_MINUTE)) {
VERIFY_INTVL_UNIT_VALUE(idetail->sec, ITVL_MAX_SQL_SECOND, "SECOND");
}
if (idetail->sec > ITVL_MAX_SQL_SECOND) {
idetail->min += idetail->sec / 60;
idetail->sec %= 60;
}
if (idetail->min > ITVL_MAX_SQL_MINUTE) {
idetail->hour += idetail->min / 60;
idetail->min %= 60;
}
if (idetail->hour > ITVL_MAX_SQL_HOUR) {
idetail->day += idetail->hour / 24;
idetail->hour %= 24;
}
VERIFY_INTVL_UNIT_VALUE(idetail->day, ITVL_MAX_SQL_DAY, "DAY");
return OG_SUCCESS;
}
static inline status_t cm_sqltext2intvl_detail(text_t *sqltext, og_type_t itype, interval_detail_t *idetail,
uint32 fmt)
{
OG_THROW_ITVL_ERROR((sqltext->len == 0), IERR_TEXT_TOO_SHORT);
if (itype == OG_TYPE_INTERVAL_YM) {
return cm_sqltext2ymintvl_detail(sqltext, idetail, fmt);
} else {
return cm_sqltext2dsintvl_detail(sqltext, idetail, fmt);
}
}
status_t cm_text2intvl_detail(const text_t *text, og_type_t itype, interval_detail_t *idetail, uint32 fmt)
{
text_t itv_text = *text;
cm_trim_text(&itv_text);
OG_THROW_ITVL_ERROR((itv_text.len == 0), IERR_TEXT_TOO_SHORT);
idetail->is_neg = OG_FALSE;
if (CM_IS_SIGN_CHAR(CM_TEXT_FIRST(&itv_text))) {
idetail->is_neg = (CM_TEXT_FIRST(&itv_text) == '-');
CM_REMOVE_FIRST(&itv_text);
}
CM_ZERO_INTVL_DETAIL(idetail);
if (IS_ISO_INDICATOR(CM_TEXT_FIRST(&itv_text))) {
return cm_isotext2intvl_detail(&itv_text, itype, idetail);
} else {
return cm_sqltext2intvl_detail(&itv_text, itype, idetail, fmt);
}
}
status_t cm_text2yminterval(const text_t *text, interval_ym_t *itvl)
{
interval_detail_t itvl_detail;
OG_RETURN_IFERR(cm_text2intvl_detail(text, OG_TYPE_INTERVAL_YM, &itvl_detail, IU_YM_INTERVAL));
return cm_encode_yminterval(&itvl_detail, itvl);
}
status_t cm_text2dsinterval(const text_t *text, interval_ds_t *itvl)
{
interval_detail_t itvl_detail;
OG_RETURN_IFERR(cm_text2intvl_detail(text, OG_TYPE_INTERVAL_DS, &itvl_detail, IU_DS_INTERVAL));
return cm_encode_dsinterval(&itvl_detail, itvl);
}
status_t cm_encode_yminterval(const interval_detail_t *idetail, interval_ym_t *itvl)
{
CM_POINTER(idetail);
*itvl = (interval_ym_t)(idetail->mon + idetail->year * ITVL_MONTHS_PER_YEAR);
OG_THROW_ITVL_ERROR((*itvl > OG_MAX_YMINTERVAL), IERR_YMINTERVAL_OVERFLOW);
if (idetail->is_neg) {
*itvl = -(*itvl);
}
return OG_SUCCESS;
}
status_t cm_encode_dsinterval(const interval_detail_t *idetail, interval_ds_t *itvl)
{
interval_ds_t unit;
CM_POINTER(idetail);
*itvl = (interval_ds_t)((uint64)idetail->day * ITVL_UNITS_PER_DAY);
unit = (interval_ds_t)((uint64)idetail->hour * ITVL_UNITS_PER_HOUR);
OG_RETURN_IFERR(cm_dsinterval_add(*itvl, unit, itvl));
unit = (interval_ds_t)((uint64)idetail->min * ITVL_UNITS_PER_MINUTE);
OG_RETURN_IFERR(cm_dsinterval_add(*itvl, unit, itvl));
unit = (interval_ds_t)((uint64)idetail->sec * ITVL_UNITS_PER_SECOND);
OG_RETURN_IFERR(cm_dsinterval_add(*itvl, unit, itvl));
OG_RETURN_IFERR(cm_dsinterval_add(*itvl, idetail->fsec, itvl));
if (idetail->is_neg) {
*itvl = -(*itvl);
}
return OG_SUCCESS;
}
void cm_decode_yminterval(interval_ym_t ymitvl_input, interval_detail_t *idetail)
{
int32 ymitvl = ymitvl_input;
CM_POINTER(idetail);
CM_ZERO_INTVL_DETAIL(idetail);
idetail->is_neg = OG_FALSE;
if (ymitvl < 0) {
idetail->is_neg = OG_TRUE;
ymitvl = -ymitvl;
}
idetail->year = (uint32)ymitvl / (uint32)ITVL_MONTHS_PER_YEAR;
idetail->mon = (uint32)ymitvl - idetail->year * (uint32)ITVL_MONTHS_PER_YEAR;
}
static inline uint32 cm_decode_dsunit(uint64 ds_val, uint64 unit, interval_ds_t *ds_res)
{
if (unit == 0) {
OG_THROW_ERROR(ERR_ZERO_DIVIDE);
return OG_DEFAULT_NULL_VALUE;
}
uint32 uval = (uint32)(ds_val / unit);
*ds_res = (interval_ds_t)(ds_val - uval * unit);
return uval;
}
void cm_decode_dsinterval(interval_ds_t dsitvl_input, interval_detail_t *idetail)
{
int64 dsitvl = dsitvl_input;
CM_POINTER(idetail);
CM_ZERO_INTVL_DETAIL(idetail);
idetail->is_neg = OG_FALSE;
if (dsitvl < 0) {
idetail->is_neg = OG_TRUE;
dsitvl = -dsitvl;
}
CM_ASSERT(dsitvl <= OG_MAX_DSINTERVAL);
idetail->day = cm_decode_dsunit((uint64)dsitvl, ITVL_UNITS_PER_DAY, &dsitvl);
idetail->hour = cm_decode_dsunit((uint64)dsitvl, ITVL_UNITS_PER_HOUR, &dsitvl);
idetail->min = cm_decode_dsunit((uint64)dsitvl, ITVL_UNITS_PER_MINUTE, &dsitvl);
idetail->sec = cm_decode_dsunit((uint64)dsitvl, ITVL_UNITS_PER_SECOND, &dsitvl);
idetail->fsec = (uint32)dsitvl;
CM_ASSERT(idetail->day <= ITVL_MAX_SQL_DAY);
CM_ASSERT(idetail->hour <= ITVL_MAX_SQL_HOUR);
CM_ASSERT(idetail->min <= ITVL_MAX_SQL_MINUTE);
CM_ASSERT(idetail->sec <= ITVL_MAX_SQL_SECOND);
CM_ASSERT(idetail->fsec <= ITVL_MAX_SQL_FRAC_SEC);
}
static void cm_yminterval2text_ex(interval_ym_t ymitvl, uint32 year_prec, text_t *text)
{
interval_detail_t idetail;
int iret_sprintf;
CM_POINTER2(text, text->str);
cm_decode_yminterval(ymitvl, &idetail);
CM_TEXT_CLEAR(text);
CM_TEXT_APPEND(text, (idetail.is_neg ? '-' : '+'));
iret_sprintf = snprintf_s(CM_GET_TAIL(text), OG_MAX_YM_INTERVAL_STRLEN, OG_MAX_YM_INTERVAL_STRLEN - 1, "%0*u-%02u",
year_prec, idetail.year, idetail.mon);
PRTS_RETVOID_IFERR(iret_sprintf);
text->len += (uint32)iret_sprintf;
}
static void cm_dsinterval2text_ex(interval_ds_t dsitvl, uint32 day_prec, uint32 frac_prec, text_t *text)
{
interval_detail_t idetail;
int iret_sprintf;
CM_POINTER2(text, text->str);
cm_decode_dsinterval(dsitvl, &idetail);
CM_TEXT_CLEAR(text);
CM_TEXT_APPEND(text, (idetail.is_neg ? '-' : '+'));
if (frac_prec == 0) {
iret_sprintf = snprintf_s(CM_GET_TAIL(text), OG_MAX_DS_INTERVAL_STRLEN, OG_MAX_DS_INTERVAL_STRLEN - 1,
"%0*u %02u:%02u:%02u", day_prec, idetail.day, idetail.hour, idetail.min, idetail.sec);
PRTS_RETVOID_IFERR(iret_sprintf);
text->len += (uint32)iret_sprintf;
} else {
iret_sprintf = snprintf_s(CM_GET_TAIL(text), OG_MAX_DS_INTERVAL_STRLEN, OG_MAX_DS_INTERVAL_STRLEN - 1,
"%0*u %02u:%02u:%02u.%06u", day_prec, idetail.day, idetail.hour, idetail.min,
idetail.sec, idetail.fsec);
PRTS_RETVOID_IFERR(iret_sprintf);
text->len += (uint32)iret_sprintf;
text->len -= (ITVL_MAX_SECOND_PREC - frac_prec);
}
}
void cm_yminterval2text(interval_ym_t ymitvl, text_t *text)
{
cm_yminterval2text_ex(ymitvl, ITVL_DEFAULT_YEAR_PREC, text);
}
void cm_yminterval2text_prec(interval_ym_t ymitvl, uint8 year_prec, text_t *text)
{
cm_yminterval2text_ex(ymitvl, (uint32)year_prec, text);
}
void cm_dsinterval2text(interval_ds_t dsitvl, text_t *text)
{
cm_dsinterval2text_ex(dsitvl, ITVL_DEFAULT_DAY_PREC, ITVL_MAX_SECOND_PREC, text);
}
void cm_dsinterval2text_prec(interval_ds_t dsitvl, uint8 day_prec, uint8 sec_prec, text_t *text)
{
cm_dsinterval2text_ex(dsitvl, (uint32)day_prec, (uint32)sec_prec, text);
}
uint32 cm_yminterval2str_ex(interval_ym_t ymitvl, uint32 year_prec, char *str)
{
text_t text;
CM_POINTER(str);
text.str = str;
text.len = 0;
cm_yminterval2text_ex(ymitvl, year_prec, &text);
return text.len;
}
uint32 cm_dsinterval2str_ex(interval_ds_t dsitvl, uint32 day_prec, uint32 frac_prec, char *str, uint32 str_max_sz)
{
text_t text;
CM_POINTER(str);
text.str = str;
text.len = 0;
cm_dsinterval2text_ex(dsitvl, day_prec, frac_prec, &text);
if (text.len >= str_max_sz) {
OG_THROW_ERROR(ERR_BUFFER_OVERFLOW, text.len, str_max_sz);
return 0;
}
CM_NULL_TERM(&text);
return text.len;
}
uint32 cm_yminterval2str(interval_ym_t ymitvl, char *str)
{
return cm_yminterval2str_ex(ymitvl, ITVL_DEFAULT_YEAR_PREC, str);
}
uint32 cm_dsinterval2str(interval_ds_t dsitvl, char *str, uint32 str_max_sz)
{
return cm_dsinterval2str_ex(dsitvl, ITVL_DEFAULT_DAY_PREC, ITVL_DEFAULT_SECOND_PREC, str, str_max_sz);
}
interval_unit_t cm_get_ymitvl_unit(const text_t *text)
{
static const text_t ytext = { .str = "YEAR", .len = 4u };
static const text_t mtext = { .str = "MONTH", .len = 5u };
text_t utext;
CM_POINTER(text);
utext = *text;
cm_trim_text(&utext);
if (cm_text_equal_ins(&utext, &ytext)) {
return IU_YEAR;
}
if (cm_text_equal_ins(&utext, &mtext)) {
return IU_MONTH;
}
return IU_NONE;
}
interval_unit_t cm_get_dsitvl_unit(const text_t *text)
{
static const text_t text_day = { .str = "DAY", .len = 3u };
static const text_t text_hour = { .str = "HOUR", .len = 4u };
static const text_t text_min = { .str = "MINUTE", .len = 6u };
static const text_t text_sec = { .str = "SECOND", .len = 6u };
text_t utext;
CM_POINTER(text);
utext = *text;
cm_trim_text(&utext);
if (cm_text_equal_ins(&utext, &text_day)) {
return IU_DAY;
}
if (cm_text_equal_ins(&utext, &text_hour)) {
return IU_HOUR;
}
if (cm_text_equal_ins(&utext, &text_min)) {
return IU_MINUTE;
}
if (cm_text_equal_ins(&utext, &text_sec)) {
return IU_SECOND;
}
return IU_NONE;
}
status_t cm_yminterval_add_date(interval_ym_t ymitvl_input, date_t date, date_t *res)
{
date_detail_t detail;
int32 year;
int32 month;
int32 ymitvl = ymitvl_input;
cm_decode_date(date, &detail);
year = (int32)detail.year;
month = detail.mon;
year += (ymitvl / ITVL_MONTHS_PER_YEAR);
ymitvl %= ITVL_MONTHS_PER_YEAR;
month += ymitvl;
if (month > ITVL_MONTHS_PER_YEAR) {
year++;
month -= ITVL_MONTHS_PER_YEAR;
} else if (month <= 0) {
year--;
month += ITVL_MONTHS_PER_YEAR;
}
if (!CM_IS_VALID_YEAR(year)) {
OG_THROW_ERROR(ERR_TYPE_OVERFLOW, "DATETIME");
return OG_ERROR;
}
detail.day = MIN(detail.day, CM_MONTH_DAYS(year, month));
detail.year = (uint16)year;
detail.mon = (uint8)month;
*res = cm_encode_date(&detail);
return OG_SUCCESS;
}
status_t cm_adjust_yminterval(interval_ym_t *ymitvl, uint32 year_prec)
{
uint32 year = CM_EXTRACT_YEAR(*ymitvl);
if (year_prec > (uint32)ITVL_MAX_YEAR_PREC) {
OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "year_prec(%u) <= ITVL_MAX_YEAR_PREC(%u)", year_prec,
(uint32)ITVL_MAX_YEAR_PREC);
return OG_ERROR;
}
if (year < g_1ten_powers[year_prec]) {
return OG_SUCCESS;
}
OG_THROW_ERROR(ERR_INVALID_INTERVAL_FIELD, "YEAR", year_prec);
return OG_ERROR;
}
#define CM_VERIFY_DAY_FIELD(dsitvl, day_prec) \
do { \
if ((uint32)CM_EXTRACT_DAY(dsitvl) >= g_1ten_powers[day_prec]) { \
OG_THROW_ERROR(ERR_INVALID_INTERVAL_FIELD, "DAY", day_prec); \
return OG_ERROR; \
} \
} while (OG_FALSE)
status_t cm_adjust_dsinterval(interval_ds_t *dsitvl, uint32 day_prec, uint32 fsec_prec)
{
if (day_prec > (uint32)ITVL_MAX_DAY_PREC) {
OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "day_prec(%u) <= ITVL_MAX_DAY_PREC(%u)",
day_prec, (uint32)ITVL_MAX_DAY_PREC);
return OG_ERROR;
}
if (fsec_prec > (uint32)ITVL_MAX_SECOND_PREC) {
OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "fsec_prec(%u) <= ITVL_MAX_SECOND_PREC(%u)", fsec_prec,
(uint32)ITVL_MAX_SECOND_PREC);
return OG_ERROR;
}
if (*dsitvl == 0) {
return OG_SUCCESS;
}
CM_VERIFY_DAY_FIELD(*dsitvl, day_prec);
if (fsec_prec == ITVL_MAX_SECOND_PREC) {
return OG_SUCCESS;
}
*dsitvl = (interval_ds_t)cm_truncate_bigint(*dsitvl, (uint32)(ITVL_MAX_SECOND_PREC - fsec_prec));
CM_VERIFY_DAY_FIELD(*dsitvl, day_prec);
return OG_SUCCESS;
}
#ifdef __cplusplus
}
#endif