Skip to content

File db_date.c

File List > compat > db_date.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * db_date.c - Julian date conversion routines and added functions for
 *             handling relative time values.
 */

#ident "$Id$"

#include <stdio.h>
#include <math.h>
#include <time.h>

#include "system.h"
#include "tz_support.h"
#include "db_date.h"

#include <assert.h>

#include "error_manager.h"
#include "chartype.h"
#include "tz_support.h"
#include "numeric_opfunc.h"
#include "object_representation.h"
#include "dbtype.h"
#include "string_opfunc.h"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#if defined (SUPPRESS_STRLEN_WARNING)
#define strlen(s1)  ((int) strlen(s1))
#endif /* defined (SUPPRESS_STRLEN_WARNING) */

/* used in conversion to julian */
#define IGREG1      (15 + 31L * (10 + 12L * 1582))
/* used in conversion from julian */
#define IGREG2      2299161 /* 10/15/1582 */
/* used in special zero date */
#define IGREG_SPECIAL   0   /* 01/01/-4713 DATE || 01/01/1970 00:00:00 TIMESTAMP */

#define FLOOR(d1) (int) (d1 + 4000000.0) - 4000000

#define YBIAS       1900

#define MMDDYYYY        0
#define YYYYMMDD        1

/* affects DATE/TIME parsing (from string) for mysql compatibility */
#define DATETIME_FIELD_LIMIT 1000000

typedef struct ampm_buf
{
  char str[10];
  int len;
} AMPM_BUF;

/* should be regarded as const */
static char local_am_str[10] = "am", local_pm_str[10] = "pm";
static int local_am_strlen = 2, local_pm_strlen = 2;

static void fill_local_ampm_str (char str[10], bool am);
static void decode_time (int timeval, int *hourp, int *minutep, int *secondp);
static int encode_time (int hour, int minute, int second);
static void decode_mtime (int mtimeval, int *hourp, int *minutep, int *secondp, int *millisecondp);
static unsigned int encode_mtime (int hour, int minute, int second, int millisecond);
static int init_tm (struct tm *);
static int get_current_year (void);
static const char *parse_date (const char *buf, int buf_len, DB_DATE * date);
static const char *parse_time (const char *buf, int buf_len, DB_TIME * mtime);
static const char *parse_mtime (const char *buf, int buf_len, unsigned int *mtime, bool * is_msec, bool * is_explicit);
static const char *parse_for_timestamp (const char *buf, int buf_len, DB_DATE * date, DB_TIME * time, bool allow_msec);
static const char *parse_datetime (const char *buf, int buf_len, DB_DATETIME * datetime);
static char const *parse_date_separated (char const *str, char const *strend, DB_DATE * date, char const **syntax_check,
                     int *year_digits, char *sep_ch);
static char const *parse_mtime_separated (char const *str, char const *strend, unsigned int *mtime,
                      char const **syntax_check, int *time_parts, char *sep_ch,
                      bool * has_explicit_msec, bool is_datetime);
static char const *parse_explicit_mtime_separated (char const *str, char const *strend, unsigned int *mtime,
                           char const **syntax_check, bool * has_explicit_msec);
static char const *parse_explicit_mtime_compact (char const *str, char const *strend, unsigned int *mtime);
static char const *parse_timestamp_compact (char const *str, char const *strend, DB_DATE * date, unsigned int *mtime,
                        bool * has_explicit_time, bool * has_explicit_msec);
static char const *parse_timedate_separated (char const *str, char const *strend, DB_DATE * date, unsigned int *mtime,
                         char const **syntax_check, bool * has_explicit_msec);
static int get_end_of_week_one_of_year (int year, int mode);
static bool is_local_am_str (const char *p, const char *p_end);
static bool is_local_pm_str (const char *p, const char *p_end);
static int db_timestamp_encode_w_reg (const DB_DATE * date, const DB_TIME * timeval, const TZ_REGION * tz_region,
                      DB_TIMESTAMP * utime, TZ_ID * dest_tz_id);

/*
 * julian_encode() - Generic routine for calculating a julian date given
 *    separate month/day/year values.
 * return : encoded julian date
 * m(in): month (1 - 12)
 * d(in): day(1 - 31)
 * y(in): year
 */
int
julian_encode (int m, int d, int y)
{
  int jul;
  int ja, jy, jm;

  if (y == 0)
    {
      return (0L);
    }

  if (m == 1 && d == 1 && y == -4713)
    {
      /* used for special meaning (IGREG_SPECIAL) */
      return (0L);
    }

  if (y < 0)
    {
      ++y;
    }

  if (m > 2)
    {
      jy = y;
      jm = m + 1;
    }
  else
    {
      jy = y - 1;
      jm = m + 13;
    }

  jul = (int) (FLOOR (365.25 * jy) + FLOOR (30.6001 * jm) + d + 1720995);

  /*
   * Test whether to convert to gregorian calander, started Oct 15, 1582
   */
  if ((d + 31L * (m + 12L * y)) >= IGREG1)
    {
      ja = (int) (0.01 * jy);
      jul += 2 - ja + (int) (0.25 * ja);
    }

  return (jul);
}

/*
 * day_of_week() - Returns the day of the week, relative to Sunday, which
 *                 this day occurred on.
 * return :
 *                 return value          day of the week
 *                 ------------          ---------------
 *                     0                  Sunday
 *                     1                  Monday
 *                     2                  Tuesday
 *                     3                  Wednesday
 *                     4                  Thursday
 *                     5                  Friday
 *                     6                  Saturday
 *
 * jul_day(in): the julian day to reason over
 */
int
day_of_week (int jul_day)
{
  if (jul_day == IGREG_SPECIAL)
    {
      return IGREG_SPECIAL;
    }
  return ((int) ((jul_day + 1) % 7));
}

/*
 * julian_decode() - Generic function for decoding a julian date into
 *    interesting pieces.
 * return : void
 * jul(in): encoded julian value
 * monthp(out): pointer to month value
 * dayp(out): pointer to day value
 * yearp(out): pointer to year value
 * weekp(out): pointer to weekday value
 */
void
julian_decode (int jul, int *monthp, int *dayp, int *yearp, int *weekp)
{
  int ja, jalpha, jb, jc, jd, je;
  int day, month, year;

  if (jul >= IGREG2)
    {
      /* correction if Gregorian conversion occurred */
      jalpha = (int) (((float) (jul - 1867216) - 0.25) / 36524.25);
      ja = jul + 1 + jalpha - (int) (0.25 * jalpha);
    }
  else
    {
      ja = jul;         /* else no conversion necessary */
    }

  if (ja == IGREG_SPECIAL)
    {
      month = day = year = 0;
    }
  else
    {
      jb = ja + 1524;
      jc = (int) (6680.0 + ((float) (jb - 2439870) - 122.1) / 365.25);
      jd = (int) (365 * jc + (0.25 * jc));
      je = (int) ((jb - jd) / 30.6001);

      day = jb - jd - (int) (30.6001 * je);
      month = je - 1;
      if (month > 12)
    {
      month -= 12;
    }
      year = jc - 4715;
      if (month > 2)
    {
      --year;
    }
      if (year <= 0)
    {
      --year;
    }
    }

  if (monthp != NULL)
    {
      *monthp = month;
    }
  if (dayp != NULL)
    {
      *dayp = day;
    }
  if (yearp != NULL)
    {
      *yearp = year;
    }
  if (weekp != NULL)
    {
      if (jul == IGREG_SPECIAL)
    {
      *weekp = 0;
    }
      *weekp = (int) ((jul + 1) % 7);
    }
}

/*
 * DB_DATE FUNCTIONS
 */

/*
 * db_date_encode() -
 * return : error code
 * date(out):
 * month(in): month (1 - 12)
 * day(in): day (1 - 31)
 * year(in):
 */
int
db_date_encode (DB_DATE * date, int month, int day, int year)
{
  DB_DATE tmp;
  int tmp_month, tmp_day, tmp_year;

  if (date == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  *date = 0;
  if (year < 0 || year > 9999 || month < 0 || month > 12 || day < 0 || day > 31)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }
  else
    {
      tmp = julian_encode (month, day, year);
      /*
       * Now turn around and decode the produced date; if it doesn't
       * jive with the parameters that came in, someone tried to give
       * us some bogus data.
       */
      julian_decode (tmp, &tmp_month, &tmp_day, &tmp_year, NULL);
      if (month == tmp_month && day == tmp_day && year == tmp_year)
    {
      *date = tmp;
    }
      else
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }
    }

  return NO_ERROR;
}

/*
 * db_date_weekday() - Please refer to the day_of_week() function
 * return : weekday
 * date(in): pointer to DB_DATE
 */
int
db_date_weekday (DB_DATE * date)
{
  int retval;

  retval = (day_of_week (*date));
  return (retval);
}

/*
 * db_date_decode() - Decodes a DB_DATE value into interesting sub fields.
 * return : void
 * date(in): pointer to DB_DATE
 * monthp(out): pointer to month
 * dayp(out): pointer to day
 * yearp(out): pointer to year
 */
void
db_date_decode (const DB_DATE * date, int *monthp, int *dayp, int *yearp)
{
  julian_decode (*date, monthp, dayp, yearp, NULL);
}

/*
 * DB_TIME FUNCTIONS
 */

/*
 * encode_time() -
 * return : time value
 * hour(in): hour
 * minute(in): minute
 * second(in): second
 */
static int
encode_time (int hour, int minute, int second)
{
  return ((((hour * 60) + minute) * 60) + second);
}

/*
 * db_time_encode() - Converts hour/minute/second into an encoded relative
 *    time value.
 * return : error code
 * timeval(out) : time value
 * hour(in): hour
 * minute(in): minute
 * second(in): second
 */
int
db_time_encode (DB_TIME * timeval, int hour, int minute, int second)
{
  if (timeval == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
      return ER_TIME_CONVERSION;
    }

  if (hour >= 0 && minute >= 0 && second >= 0 && hour < 24 && minute < 60 && second < 60)
    {
      *timeval = (((hour * 60) + minute) * 60) + second;
    }
  else
    {
      *timeval = -1;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
      return ER_TIME_CONVERSION;
    }

  return NO_ERROR;
}

/*
 * db_time_decode() - Converts encoded time into hour/minute/second values.
 * return : void
 * time(in): encoded relative time value
 * hourp(out): hour pointer
 * minutep(out) : minute pointer
 * secondp(out) : second pointer
 */
static void
decode_time (int timeval, int *hourp, int *minutep, int *secondp)
{
  int minutes, hours, seconds;

  seconds = timeval % 60;
  minutes = (timeval / 60) % 60;
  hours = (timeval / 3600) % 24;

  if (hourp != NULL)
    {
      *hourp = hours;
    }
  if (minutep != NULL)
    {
      *minutep = minutes;
    }
  if (secondp != NULL)
    {
      *secondp = seconds;
    }
}

/*
 * db_time_decode() - Converts encoded time into hour/minute/second values.
 * return: void
 * timeval(in) : encoded relative time value
 * hourp(out) : hour pointer
 * minutep(out) : minute pointer
 * secondp(out) : second pointer
 */
void
db_time_decode (DB_TIME * timeval, int *hourp, int *minutep, int *secondp)
{
  decode_time (*timeval, hourp, minutep, secondp);
}

/*
 * DB_TIMESTAMP FUNCTIONS
 */

/*
 * tm_encode() - This function is used in conjunction with Unix mktime to
 *    convert julian/time pairs into a universal time constant.
 *    Be careful not to pass pointers to the tm_ structure to the decoding
 *    functions.  When these go through the PC int-to-long filter, they
 *    expect long* pointers which isn't what the fields in tm_ are.
 * return : error code
 * c_time_struct(out): c time structure from time.h
 * retval(out): time value (time_t)
 * date(in) : database date structure
 * time(in) : database time structure
 *
 * Note:  This function is kept only for DB Interface (backward compatibility)
 *    Do not use it in CUBRID code since is using libc library. Prefer
 *    db_timestamp_encode... functions.
 */
static int
tm_encode (struct tm *c_time_struct, time_t * retval, DB_DATE * date, DB_TIME * timeval)
{
  int mon, day, year, hour, min, sec;
  struct tm loc;

  *retval = -1;

  if (c_time_struct == NULL || ((date == NULL) && (timeval == NULL)) || init_tm (c_time_struct) == -1)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  if (date != NULL)
    {
      julian_decode (*date, &mon, &day, &year, NULL);
      c_time_struct->tm_mon = mon;
      c_time_struct->tm_mday = day;
      c_time_struct->tm_year = year;

      if (c_time_struct->tm_year < 1900)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }
      c_time_struct->tm_year -= 1900;
      c_time_struct->tm_mon -= 1;
      c_time_struct->tm_isdst = -1; /* Don't know if DST applies or not */
    }

  if (timeval != NULL)
    {
      decode_time (*timeval, &hour, &min, &sec);
      c_time_struct->tm_hour = hour;
      c_time_struct->tm_min = min;
      c_time_struct->tm_sec = sec;
    }

  loc = *c_time_struct;

  /* mktime() on Sun anomalously returns negative values other than -1. */
  *retval = mktime (&loc);
  if (*retval < (time_t) 0) /* get correct tm_isdst */
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  /* If tm_isdst equals to zero, we do not need to convert the broken-down time (loc) again. */
  if (loc.tm_isdst == 0)
    {
      return NO_ERROR;
    }

  c_time_struct->tm_isdst = loc.tm_isdst;

  *retval = mktime (c_time_struct);
  if (*retval < (time_t) 0)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  return NO_ERROR;
}

/*
 * db_tm_encode() - This function is used in conjunction with Unix mktime to
 *    convert julian/time pairs into a universal time constant.
 *    Be careful not to pass pointers to the tm_ structure to the decoding
 *    functions.  When these go through the PC int-to-long filter, they
 *    expect long* pointers which isn't what the fields in tm_ are.
 * return : error code
 * c_time_struct(out): c time structure from time.h
 * date(in) : database date structure
 * time(in) : database time structure
 */
int
db_tm_encode (struct tm *c_time_struct, DB_DATE * date, DB_TIME * timeval)
{
  time_t retval;
  return tm_encode (c_time_struct, &retval, date, timeval);
}

/*
 * db_mktime() -  Converts the date and time arguments into the representation
 *              used by time() and returns it.
 * return : time_t
 * date(in): database date structure
 * time(in): database time structure
 *
 * note : This obeys the same convention as mktime(), returning a
 *        value of (time_t)-1 if an error occurred.  Consult errid()
 *        for the db error code.
 * Note2: This function is kept only for DB Interface (backward compatibility)
 *    Do not use it in CUBRID code since is using libc library. Prefer
 *    db_timestamp_encode... functions.
 */
time_t
db_mktime (DB_DATE * date, DB_TIME * timeval)
{
  time_t retval;
  struct tm temp;

  if (tm_encode (&temp, &retval, date, timeval) != NO_ERROR)
    {
      return -1;
    }

  return (retval);
}

/*
 * db_timestamp_encode() - This function is used to construct DB_TIMESTAMP
 *    from DB_DATE and DB_TIME.
 * return : error code
 * utime(out): pointer to universal time value
 * date(in): encoded julian date
 * time(in): relative time
 */
int
db_timestamp_encode (DB_TIMESTAMP * utime, DB_DATE * date, DB_TIME * timeval)
{
  return db_timestamp_encode_ses (date, timeval, utime, NULL);
}


/*
 * db_timestamp_encode_ses() - This function is used to construct DB_TIMESTAMP
 *    from DB_DATE and DB_TIME, considering the time and date in session
 *    local timezone
 * return : error code
 * date(in): encoded julian date
 * timeval(in): relative time
 * utime(out): pointer to universal time value
 * dest_tz_id(out): pointer to packed timezone identifier of the result
 *          (can be NULL, in which case no identifier is provided)
 */
int
db_timestamp_encode_ses (const DB_DATE * date, const DB_TIME * timeval, DB_TIMESTAMP * utime, TZ_ID * dest_tz_id)
{
  TZ_REGION ses_tz_region;
  tz_get_session_tz_region (&ses_tz_region);

  return db_timestamp_encode_w_reg (date, timeval, &ses_tz_region, utime, dest_tz_id);
}

/*
 * db_timestamp_encode_sys() - This function is used to construct DB_TIMESTAMP
 *    from DB_DATE and DB_TIME, considering the time and date in system timezone
 *
 * return : error code
 * date(in): encoded julian date
 * timeval(in): relative time
 * utime(out): pointer to universal time value
 * dest_tz_id(out): pointer to packed timezone identifier of the result
 *          (can be NULL, in which case no identifier is provided)
 */
int
db_timestamp_encode_sys (const DB_DATE * date, const DB_TIME * timeval, DB_TIMESTAMP * utime, TZ_ID * dest_tz_id)
{
  TZ_REGION sys_tz_region;
  tz_get_system_tz_region (&sys_tz_region);

  return db_timestamp_encode_w_reg (date, timeval, &sys_tz_region, utime, dest_tz_id);
}

/*
 * db_timestamp_encode_utc() - This function is used to construct DB_TIMESTAMP
 *                 from DB_DATE and DB_TIME considering the date and
 *                 time in UTC
 * return : error code
 * date(in): encoded julian date
 * time(in): relative time
 * utime(out): pointer to universal time value
 */
int
db_timestamp_encode_utc (const DB_DATE * date, const DB_TIME * timeval, DB_TIMESTAMP * utime)
{
  DB_BIGINT t = 0;
  int mon, day, year, hour, min, sec;
  const int year_century = 1900;
  const int year_max_epoch = 2040;
  const int secs_per_day = 24 * 3600;
  const int secs_in_a_year = secs_per_day * 365;
  const int days_up_to_month[] = { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

  assert (date != NULL && timeval != NULL);

  if (*date == IGREG_SPECIAL && *timeval == 0)
    {
      *utime = IGREG_SPECIAL;
      return NO_ERROR;
    }

  julian_decode (*date, &mon, &day, &year, NULL);

  year -= year_century;
  if (year < 70 || year > year_max_epoch - year_century)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  mon -= 1;
  decode_time (*timeval, &hour, &min, &sec);

  /* The first item adds the days off all the years between 1970 and the given year considering that each year has 365
   * days. The second item adds a day every 4 years starting from 1973. The third item subtracts a day back out every
   * 100 years starting with 2001. The fourth item adds a day back every 400 years starting with 2001 */
  t = ((year - 70) * secs_in_a_year + ((year - 69) / 4) * secs_per_day - ((year - 1) / 100) * secs_per_day
       + ((year + 299) / 400) * secs_per_day);

  if (mon > TZ_MON_JAN)
    {
      t += days_up_to_month[mon - 1] * secs_per_day;
      if (IS_LEAP_YEAR (year_century + year) && mon > TZ_MON_FEB)
    {
      t += secs_per_day;
    }
    }

  t += (day - 1) * secs_per_day;
  t += hour * 3600;
  t += min * 60;
  t += sec;

  t += tz_timestamp_encode_leap_sec_adj (year_century, year, mon, day);

  if (t < 0 || OR_CHECK_INT_OVERFLOW (t))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }
  else
    {
      *utime = (DB_TIMESTAMP) t;
    }
  return NO_ERROR;
}

/*
 * db_timestamp_encode_w_reg() - This function is used to construct
 *    DB_TIMESTAMP from DB_DATE and DB_TIME considering the date and time in
 *    timezone tz_region
 * return : error code
 * date(in): encoded julian date
 * time(in): relative time
 * tz_region(in): timezone region of date and time
 * utime(out): pointer to universal time value
 * dest_tz_id(out): pointer to packed timezone identifier of the result
*           (can be NULL, in which case no identifier is provided)
 */
static int
db_timestamp_encode_w_reg (const DB_DATE * date, const DB_TIME * timeval, const TZ_REGION * tz_region,
               DB_TIMESTAMP * utime, TZ_ID * dest_tz_id)
{
  int err = NO_ERROR;
  DB_DATETIME datetime, utc_datetime;
  DB_DATE utc_date;
  DB_TIME utc_time;

  if (*date == IGREG_SPECIAL && *timeval == 0)
    {
      *utime = IGREG_SPECIAL;
      return NO_ERROR;
    }

  datetime.date = *date;
  datetime.time = *timeval * 1000;

  if (!TZ_IS_UTC_TZ_REGION (tz_region))
    {
      /* convert datetime from source timezone to UTC */
      err =
    tz_conv_tz_datetime_w_region (&datetime, tz_region, tz_get_utc_tz_region (), &utc_datetime, dest_tz_id, NULL);
      if (err != NO_ERROR)
    {
      return err;
    }
    }
  else
    {
      utc_datetime = datetime;
      if (dest_tz_id != NULL)
    {
      *dest_tz_id = *tz_get_utc_tz_id ();
    }
    }

  utc_date = utc_datetime.date;
  utc_time = utc_datetime.time / 1000;

  return db_timestamp_encode_utc (&utc_date, &utc_time, utime);
}

/*
 * db_timestamp_decode_ses() - This function converts a DB_TIMESTAMP into
 *                 a DB_DATE and DB_TIME pair, considering the
 *                             session local timezone
 * return : void
 * time(in): universal time
 * date(out): return julian date or zero date
 * time(out): return relative time
 */
int
db_timestamp_decode_ses (const DB_TIMESTAMP * utime, DB_DATE * date, DB_TIME * timeval)
{
  TZ_REGION ses_tz_region;

  tz_get_session_tz_region (&ses_tz_region);
  return db_timestamp_decode_w_reg (utime, &ses_tz_region, date, timeval);
}

/*
 * db_timestamp_decode_utc() - This function converts a DB_TIMESTAMP into
 *    a DB_DATE and DB_TIME pair using UTC time reference
 * return : void
 * time(in): universal time
 * date(out): return julian date or zero date
 * time(out): return relative time
 */
void
db_timestamp_decode_utc (const DB_TIMESTAMP * utime, DB_DATE * date, DB_TIME * timeval)
{
  int timestamp;
  int year, months, day;
  int hours, minutes, seconds;

  assert (utime != NULL);

  if (*utime == IGREG_SPECIAL)
    {
      if (date != NULL)
    {
      *date = IGREG_SPECIAL;
    }
      if (timeval != NULL)
    {
      *timeval = 0;
    }
      return;
    }

  timestamp = *utime;

  tz_timestamp_decode_sec (timestamp, &year, &months, &day, &hours, &minutes, &seconds);

  if (date != NULL)
    {
      *date = julian_encode (months + 1, day, year);
    }

  if (timeval != NULL)
    {
      *timeval = encode_time (hours, minutes, seconds);
    }
}

/*
 * db_timestamp_decode_w_reg() - This function converts a DB_TIMESTAMP into
 *    a DB_DATE and DB_TIME pair, directly into a time zone specified by
 *    tz_region
 * return : error code
 * utime(in): universal time
 * tz_region(in): timezone region of destination date and time
 * date(out): return julian date or zero date
 * time(out): return relative time
 */
int
db_timestamp_decode_w_reg (const DB_TIMESTAMP * utime, const TZ_REGION * tz_region, DB_DATE * date, DB_TIME * timeval)
{
  int err = NO_ERROR;
  DB_DATETIME datetime, utc_datetime;
  DB_DATE tmp_date;
  DB_TIME tmp_time;

  db_timestamp_decode_utc (utime, &tmp_date, &tmp_time);

  if (tmp_date == IGREG_SPECIAL)
    {
      if (date != NULL)
    {
      *date = IGREG_SPECIAL;
    }
      if (timeval != NULL)
    {
      *timeval = 0;
    }
      return err;
    }

  utc_datetime.date = tmp_date;
  utc_datetime.time = tmp_time * 1000;

  if (!TZ_IS_UTC_TZ_REGION (tz_region))
    {
      /* convert datetime from UTC to destination timezone */
      err = tz_conv_tz_datetime_w_region (&utc_datetime, tz_get_utc_tz_region (), tz_region, &datetime, NULL, NULL);
    }
  else
    {
      datetime = utc_datetime;
    }

  if (err != NO_ERROR)
    {
      /* error condition */
      if (date != NULL)
    {
      *date = 0;
    }
      if (timeval != NULL)
    {
      *timeval = 0;
    }
    }
  else
    {
      if (date != NULL)
    {
      *date = datetime.date;
    }

      if (timeval != NULL)
    {
      *timeval = datetime.time / 1000;
    }
    }

  return err;
}

/*
 * db_timestamp_decode_w_tz_id() - This function converts a DB_TIMESTAMP into
 *    a DB_DATE and DB_TIME pair, directly into a time zone specified by
 *    tz_id
 * return : error code
 * utime(in): universal time
 * tz_id(in): timezone id of destination date and time
 * date(out): return julian date or zero date
 * time(out): return relative time
 */
int
db_timestamp_decode_w_tz_id (const DB_TIMESTAMP * utime, const TZ_ID * tz_id, DB_DATE * date, DB_TIME * timeval)
{
  int err = NO_ERROR;
  DB_DATETIME datetime, utc_datetime;
  DB_DATE v_date;
  DB_TIME v_time;

  db_timestamp_decode_utc (utime, &v_date, &v_time);

  if (v_date == IGREG_SPECIAL)
    {
      if (date != NULL)
    {
      *date = IGREG_SPECIAL;
    }
      if (timeval != NULL)
    {
      *timeval = 0;
    }
      return err;
    }

  utc_datetime.date = v_date;
  utc_datetime.time = v_time * 1000;

  err = tz_utc_datetimetz_to_local (&utc_datetime, tz_id, &datetime);

  if (err != NO_ERROR)
    {
      /* error condition */
      if (date != NULL)
    {
      *date = 0;
    }
      if (timeval != NULL)
    {
      *timeval = 0;
    }
    }
  else
    {
      if (date != NULL)
    {
      *date = datetime.date;
    }

      if (timeval != NULL)
    {
      *timeval = datetime.time / 1000;
    }
    }

  return err;
}

/*
 * UNIX COMPATIBILITY FUNCTIONS
 */

/*
 * db_strftime() - This is a database interface for the standard C library
 *    routine strftime().  Either date or time can be NULL, depending on the
 *    desired conversion.  The format string is the same as used by strftime()
 * return : error code
 * s(out): string to print to
 * smax(in): maximum size of this the string s
 * fmt(in): format string
 * date(in): date
 * time(in): time
 */
int
db_strftime (char *s, int smax, const char *fmt, DB_DATE * date, DB_TIME * timeval)
{
  int retval;
  struct tm date_and_time;
  int conversion_error;

  conversion_error = db_tm_encode (&date_and_time, date, timeval);
  if (conversion_error != NO_ERROR)
    {
      return ((int) conversion_error);
    }

  retval = ((int) strftime (s, (size_t) smax, fmt, &date_and_time));

  return (retval);
}

/*
 * db_localtime() - Converts the time since epoch (jan 1 1970) into database
 *    date and time structure.  date or time can be NULL if conversion to that
 *    structure is not desired.
 * return : void
 * epoch_time(in): number of seconds since epoch
 * date(out): database date structure
 * timeval(out): database time structure
 */
void
db_localtime (time_t * epoch_time, DB_DATE * date, DB_TIME * timeval)
{
  struct tm *temp;
  struct tm t;

  temp = localtime_r (epoch_time, &t);
  if (temp == NULL)
    {
      return;
    }

  if (date != NULL)
    {
      *date = julian_encode (temp->tm_mon + 1, temp->tm_mday, temp->tm_year + 1900);
    }
  if (timeval != NULL)
    {
      *timeval = encode_time (temp->tm_hour, temp->tm_min, temp->tm_sec);
    }
}


/*
 * db_localdatetime() - Converts the time since epoch (jan 1 1970) into database
 *    datetime structure.
 * return : void
 * epoch_time(in): number of seconds since epoch
 * datetime(out): database datetime structure
 */
void
db_localdatetime (time_t * epoch_time, DB_DATETIME * datetime)
{
  db_localdatetime_msec (epoch_time, 0, datetime);
}

/*
 * db_localdatetime_msec() - Converts the time include milliseconds since
 *    epoch (jan 1 1970) into database datetime structure.
 * return : void
 * epoch_time(in): number of seconds since epoch
 * millisecond(in): part of extra milliseconds
 * datetime(out): database datetime structure
 */
void
db_localdatetime_msec (time_t * epoch_time, int millisecond, DB_DATETIME * datetime)
{
  struct tm *temp;
  struct tm t;

  temp = localtime_r (epoch_time, &t);
  if (temp == NULL)
    {
      return;
    }

  if (datetime != NULL)
    {
      db_datetime_encode (datetime, temp->tm_mon + 1, temp->tm_mday, temp->tm_year + 1900, temp->tm_hour, temp->tm_min,
              temp->tm_sec, millisecond);
    }
}

/*
 * DATE/TIME/TIMESTAMP PARSING FUNCTIONS
 */

/*
 * init_tm() - This function uses time() and localtime() to initialize a struct
 *   tm with the current time.
 * return : zero or -1(on error)
 * tm(in) : a pointer to a struct tm to be modified.
 */
static int
init_tm (struct tm *tm)
{
  time_t tloc;
  struct tm *tmp;
  struct tm t;

  if (time (&tloc) == -1)
    {
      return -1;
    }

  tmp = localtime_r (&tloc, &t);
  if (tmp == NULL)
    {
      return -1;
    }

  *tm = *tmp;

  return 0;
}

/*
 * get_current_year() - This function returns current year.
 * return : current year or -1(on error)
 */
static int
get_current_year (void)
{
  struct tm tm;
  return (init_tm (&tm) == -1) ? -1 : tm.tm_year + YBIAS;
}

/*
 * parse_date() - Parse an ordinary date string (e.g., '10/15/86').
 *    Whitespace is permitted between components.  If the year is omitted, the
 *    current year will be discovered and used.
 * return : NULL on error
 * buf(in): a buffer containing a date to be parsed
 * buf_len(in): the length of the string to be parsed
 * date(out): a pointer to a DB_DATE to be modified
 */
static const char *
parse_date (const char *buf, int buf_len, DB_DATE * date)
{
  int part[3] = { 0, 0, 0 };
  int part_char_len[3] = { 0, 0, 0 };
  int month, day, year;
  int julian_date;
  unsigned int i;
  const char *p;
  int date_style = -1;
  int year_part, month_part, day_part;
  int separator = '\0';
  const char *strend = buf + buf_len;

  if (buf == NULL)
    {
      return NULL;
    }

  /* for each date part (year, month, day) */
  for (i = 0, p = buf; i < DIM (part); i++)
    {
      /* skip leading space */
      for (; p < strend && char_isspace (*p); ++p)
    {
      ;
    }

      /* read found decimal field value */
      for (; p < strend && char_isdigit (*p); ++p)
    {
      part[i] = part[i] * 10 + (*p - '0');
      part_char_len[i]++;
    }

      if (i < DIM (part) - 1)
    {
      /* skip inter-field space */
      for (; p < strend && char_isspace (*p); ++p)
        {
          ;
        }

      if (p == strend)
        {
          break;
        }

      /* check date separator ('/' or '-'), if any */
      if (separator != '\0' && *p != '\0' && *p != separator)
        {
          return NULL;
        }

      /* find and skip the separator */
      if (*p == '/')
        {
          separator = *p;
          ++p;
          date_style = MMDDYYYY;
        }
      else if (*p == '-')
        {
          separator = *p;
          ++p;
          date_style = YYYYMMDD;
        }
      else
        {
          break;
        }
    }
    }

  /* skip trailing space */
  for (; p < strend && char_isspace (*p); ++p)
    {
      ;
    }

  if (date_style == MMDDYYYY)
    {
      if (i == 1)
    {
      year_part = -1;
    }
      else
    {
      year_part = 2;
    }
      month_part = 0;
      day_part = 1;
    }
  else if (date_style == YYYYMMDD)
    {
      if (i == 1)
    {
      year_part = -1;
      month_part = 0;
      day_part = 1;
    }
      else
    {
      year_part = 0;
      month_part = 1;
      day_part = 2;
    }
    }
  else
    {
      return NULL;
    }

  /* stop parsing if year is present and over 10000 */
  if (0 <= year_part && 10000 <= part[year_part])
    {
      return NULL;
    }

  /* fill the year if not present */
  if (year_part == -1)
    {
      year = get_current_year ();
    }
  else
    {
      year = part[year_part];
      if (part_char_len[year_part] == 2)
    {
      if (year < 70)
        {
          year += 2000;
        }
      else
        {
          year += 1900;
        }
    }
    }

  month = part[month_part];
  day = part[day_part];

  /*
   * 0000-00-00 00:00:00 means timestamp 1970-01-01 00:00:00
   * but 0000-00-00 XX:XX:XX (one of X is not zero) means error
   * so in this parse_date return OK but set date as 0
   * (It should be treated differ with '1970-01-01')
   */
  if (year == 0 && month == 0 && day == 0)
    {
      *date = IGREG_SPECIAL;
      return p;
    }

  /*
   * Now encode it and then decode it again and see if we get the same
   * day; if not, it was a bogus specification, like 2/29 on a non-leap
   * year.
   */
  julian_date = julian_encode (month, day, year);
  julian_decode (julian_date, &part[0], &part[1], &part[2], NULL);

  if (month == part[0] && day == part[1] && year == part[2])
    {
      *date = julian_date;
      return p;
    }
  else
    {
      return NULL;
    }

}

/*
 * parse_time() - If parse_time() succeeds, it returns a pointer to the
 *   rest of the buffer following the parsed time expr. This accepts both
 *   12 and 24 hour times, with optional am/pm designators.
 *   An am designator on a 24 hour time after 12pm is considered an
 *   error, e.g., "13:45am" will fail.
 *   Minutes and seconds can be omitted; they will default to 0.
 * return : const char or NULL on error
 * buf(in): pointer to time expression
 * buf_len(in): the length of the string to be parsed
 * time(out): pointer to DB_TIME to be updated with the parsed time
 */
static const char *
parse_time (const char *buf, int buf_len, DB_TIME * time)
{
  unsigned int mtime;
  const char *p;
  bool is_msec;

  p = parse_mtime (buf, buf_len, &mtime, &is_msec, NULL);

  if (p != NULL)
    {
      if (is_msec == true)
    {
      return NULL;
    }

      *time = mtime / 1000;
    }

  return p;
}

/*
 * db_string_check_explicit_time() -
 * return : true if explicit time expression
 * str(in): pointer to time expression
 * str_len(in): the length of the string to be checked
 */
bool
db_string_check_explicit_time (const char *str, int str_len)
{
  unsigned int mtime;
  bool is_explicit;

  const char *result = parse_mtime (str, str_len, &mtime, NULL, &is_explicit);
  if (result == NULL || *result != '\0')
    {
      return false;
    }
  return is_explicit;
}

/*
 * parse_mtime() -
 * return : const char or NULL on error
 * buf(in): pointer to time expression
 * buf_len(in): the length of the string to be parsed
 * mtime(out): pointer to unsigned int to be updated with the parsed time
 */
static const char *
parse_mtime (const char *buf, int buf_len, unsigned int *mtime, bool * is_msec, bool * is_explicit)
{
  int part[4] = { 0, 0, 0, 0 };
  unsigned int i;
  const char *p;
  double fraction = 100;
  const char *strend = buf + buf_len;

  if (buf == NULL)
    {
      return NULL;
    }

  if (is_msec != NULL)
    {
      *is_msec = false;
    }

  if (is_explicit != NULL)
    {
      *is_explicit = true;
    }

  for (i = 0, p = buf; i < DIM (part); i++)
    {
      for (; p < strend && char_isspace (*p); ++p)
    ;
      for (; p < strend && char_isdigit (*p); ++p)
    {
      if (i != 3)
        {
          part[i] = part[i] * 10 + (*p - '0');
        }
      else
        {
          part[i] += (int) (fraction * (*p - '0') + 0.5);
          fraction /= 10;
        }
    }
      if (i < DIM (part) - 1)
    {
      for (; p < strend && char_isspace (*p); ++p)
        ;

      if (p < strend && *p == ':')
        {
          if (i == 2)
        {
          return NULL;
        }
          ++p;
        }
      else if (p < strend && *p == '.' && i == 2)
        {
          ++p;
          if (is_msec != NULL)
        {
          *is_msec = true;
        }
        }
      else
        {
          /* This allows time' ' to be interpreted as 0 which means 12:00:00 AM. */
          ++i;

          /* This means time string format is not completed (like 0, 01:00) * This flag will be used by operate
           * like [select 1 + '1'] which should not be converted to time */
          if (is_explicit != NULL && i < 3)
        {
          *is_explicit = false;
        }

          break;
        }
    }
    }

  for (; p < strend && char_isspace (*p); ++p)
    ;
  if (is_local_am_str (p, strend) && ((*(p + local_am_strlen) == ' ') || p + local_am_strlen == strend))
    {
      p += local_am_strlen;
      if (part[0] == 12)
    {
      part[0] = 0;
    }
      else if (part[0] > 12)
    {
      part[0] = -1;
    }
    }
  else if (is_local_pm_str (p, strend) && ((*(p + local_pm_strlen) == ' ') || p + local_pm_strlen == strend))
    {
      p += local_pm_strlen;
      if (part[0] < 12)
    {
      part[0] += 12;
    }
      else if (part[0] == 0)
    {
      part[0] = -1;
    }
    }
  else if (i == 0 && *buf)
    {
      /* buf is "[0-9]*" */
      return NULL;
    }

  if (part[0] < 0 || part[0] > 23)
    {
      return NULL;
    }
  if (part[1] < 0 || part[1] > 59)
    {
      return NULL;
    }
  if (part[2] < 0 || part[2] > 59)
    {
      return NULL;
    }
  if (part[3] < 0 || part[3] > 999)
    {
      return NULL;
    }

  *mtime = encode_mtime (part[0], part[1], part[2], part[3]);

  return p;
}

/*
 * fill_local_ampm_str() - writes the "am" or "pm" string as by the current
 *              locale into the given string
 * str(out):    string buffer to receive the "am" or the "pm" string from the
 *      current locale
 * am(in):  true to return the current "am" string, "false" to return the
 *      current "pm" string from the current locale
 */
static void
fill_local_ampm_str (char str[10], bool am)
{
  struct tm tm;
  if (init_tm (&tm) == -1)
    {
      if (am)
    {
      strcpy (str, "am");
    }
      else
    {
      strcpy (str, "pm");
    }
    }
  else
    {
      /*
       * Use strftime() to try to find out this locale's idea of
       * the am/pm designators.
       */
      if (am)
    {
      tm.tm_hour = 0;
      strftime (str, 10, "%p", &tm);
    }
      else
    {
      tm.tm_hour = 12;
      strftime (str, 10, "%p", &tm);
    }
    }
}

/*
 * db_date_locale_init() - Initializes the am/pm strings from the current
 *              locale, to be used when parsing TIME values.
 *              Should be invoked when CUBRID starts up
 */
void
db_date_locale_init (void)
{
  fill_local_ampm_str (local_am_str, true);
  local_am_strlen = strlen (local_am_str);

  fill_local_ampm_str (local_pm_str, false);
  local_pm_strlen = strlen (local_pm_str);
}

/*
 * is_local_am_str() - checks if a string is the local "am" string
 *
 * returns : 0 if matches the local string, non-zero otherwise
 * p(in):    null terminated string
 */
static bool
is_local_am_str (const char *p, const char *p_end)
{
  return ((p + local_am_strlen - 1 < p_end) && (intl_mbs_ncasecmp (p, local_am_str, local_am_strlen) == 0));
}

/*
 * is_local_pm_str() - checks if a string is the local "pm" string
 *
 * returns : 0 if matches the local string, non-zero otherwise
 * p(in):    null terminated string
 */
static bool
is_local_pm_str (const char *p, const char *p_end)
{
  return ((p + local_pm_strlen - 1 < p_end) && (intl_mbs_ncasecmp (p, local_pm_str, local_pm_strlen) == 0));
}

/*
 * parse_date_separated() - Reads  DATE in '09-10-12' format
 *
 * returns: position in str where parsing stopped, if successfull,
 *      NULL otherwise
 * str(in): string with the date to be parsed
 * strend(in):  the end of the string to be parsed
 * date(out):   resulting date, after parsing, if successfull
 * syntax_check(out):
 *      if not null and conversion fails, will be set to
 *      the last read character from the input string, when the
 *      syntax (parsing) was successfull, but resulting
 *      value is incorrect (for example for 2009-120-21). Should be
 *      set to NULL before making the function call.
 * year_digits(out):
 *      if not null will receive the number of digits that the year in
 *      the date string was specified with. The number can be received
 *      even if conversion fails. If not null it will also turn any
 *      date part found larger than DATETIME_FIELD_LIMIT into a syntax
 *      error (as opposed to an invalid, but correctly delimited, date
 *      value).
 * sep_ch(out): separator character that the parsed date is using, or '\0' if
 *      parsed date uses more than one separator. Can be modified even
 *          if parsing fails.
 */
static char const *
parse_date_separated (char const *str, char const *strend, DB_DATE * date, char const **syntax_check, int *year_digits,
              char *sep_ch)
{
  DB_DATE cdate;
  unsigned char separator = 0;  /* 0 - yet to be read 1 - only single slashes read 2 - non-slashes (or multiple
                 * slashes) read */

  int date_parts[3] = { 0, 0, 0 };
  unsigned char date_parts_len[3] = { 0, 0, 0 };
  unsigned char parts_found = 0, year_part, month_part, day_part;

  char const *p = str, *q;

  assert (!syntax_check || *syntax_check == NULL);

  /* skip leading spaces in string */
  while (p < strend && char_isspace (*p))
    {
      p++;
    }

  if (p == strend || !char_isdigit (*p))
    {
      return NULL;      /* date should begin with a digit */
    }

  /* read up to three date parts (year, month, day) from string, separated by non-blank, non-alphanumeric characters */
  if (sep_ch)
    {
      *sep_ch = '0';        /* a digit can not be a separator */
    }

  q = p;

  while (p < strend && q == p && !char_isspace (*p) && !char_isalpha (*p) && parts_found < DIM (date_parts))
    {
      unsigned char new_separator = separator;

      /* read any separator */
      while (p < strend && !char_isspace (*p) && !char_isdigit (*p) && !char_isalpha (*p))
    {
      if (*p == '/')
        {
          /* '/' separator found */
          if (separator == 0)
        {
          new_separator = 1;
        }
        }
      else
        {
          /* non-slash separator found */
          new_separator = 2;
        }

      if (sep_ch)
        {
          if (*sep_ch)
        {
          if (*sep_ch == '0')
            {
              *sep_ch = *p;
            }
          else
            {
              if (*sep_ch != *p)
            {
              /* more than one separator found */
              *sep_ch = '\0';
            }
            }
        }
        }

      p++;
    }

      /* read the number (date part value) */
      if (p < strend && char_isdigit (*p))
    {
      separator = new_separator;

      while (p < strend && char_isdigit (*p))
        {
          if (date_parts[parts_found] < DATETIME_FIELD_LIMIT)
        {
          date_parts[parts_found] *= 10;
          date_parts[parts_found] += *p - '0';
        }

          if (date_parts_len[parts_found] < 4)
        {
          date_parts_len[parts_found]++;
        }

          p++;
        }

      if (year_digits && date_parts[parts_found] >= DATETIME_FIELD_LIMIT)
        {
          /* In a context where the number of digits specified for the year is requested (when reading a time from
           * a datetime string), having any date part larger than the field limit is not only an invalid value, but
           * also a parsing error. */
          return NULL;
        }

      parts_found++;
      q = p;
    }
    }

  p = q;

  if (parts_found < 2)
    {
      /* Insufficient number of year/month/day fields could be read */
      return NULL;
    }

  /* Infer the order of date fields: MM/DD[/YY] or [YY-]MM-DD */
  if (separator == 1)
    {
      /* mm/dd[/yy] date format */
      year_part = 2;
      month_part = 0;
      day_part = 1;

    }
  else
    {
      /* [yy-]mm-dd format */
      year_part = 0;
      month_part = 1;
      day_part = 2;

      if (parts_found == 2)
    {
      date_parts[2] = date_parts[1];
      date_parts[1] = date_parts[0];
      date_parts_len[2] = date_parts_len[1];
      date_parts_len[1] = date_parts_len[0];
    }
    }

  /* Fill in the year or the century, if omitted */
  if (parts_found == 2)
    {
      if (year_digits)
    {
      *year_digits = 0;
    }
      date_parts[year_part] = get_current_year ();
    }
  else
    {
      if (year_digits)
    {
      *year_digits = date_parts_len[year_part];
    }

      if (date_parts_len[year_part] == 2)
    {
      if (date_parts[year_part] < 70)
        {
          date_parts[year_part] += 2000;
        }
      else
        {
          date_parts[year_part] += 1900;
        }
    }
    }

  if (date_parts[month_part] == 0 && date_parts[day_part] == 0 && date_parts[year_part] == 0)
    {
      *date = IGREG_SPECIAL;
      return p;
    }

  /* Check and return the resulting date */
  if (date_parts[year_part] >= 10000 || date_parts[month_part] > 12 || date_parts[month_part] < 1
      || date_parts[day_part] > 31 || date_parts[day_part] < 1)
    {
      /* malformed or invalid date string */
      if (syntax_check)
    {
      /* parsing successfull, but unexpected value */
      *syntax_check = p;
    }
      return NULL;
    }
  else
    {
      int year, month, day;

      cdate = julian_encode (date_parts[month_part], date_parts[day_part], date_parts[year_part]);
      julian_decode (cdate, &month, &day, &year, NULL);

      if (day == date_parts[day_part] && month == date_parts[month_part] && year == date_parts[year_part])
    {
      *date = cdate;
      return p;
    }
      else
    {
      /* parsing successfull, but unexpected value */
      if (syntax_check)
        {
          *syntax_check = p;
        }

      return NULL;
    }
    }
}

/*
 * parse_mtime_separated() - Reads a TIME from a string, reading hours first
 *              and accepting any separators
 * returns: pointer to the next character in the input string that fallows the
 *      parsed time value if successfull, NULL otherwise
 * str(in): string with the time value to be parsed
 * strend(in):  the end of the string to be parsed
 * mtime(out): the converted time
 * syntax_check(out): if not null, upon conversion failure it will be filled
 *          with the pointer to the last character read if the
 *          time could be parsed but the time value
 *          found is invalid (like 10:65:24). Should be set to
 *          NULL before the function invocation
 * time_parts(out): if not NULL will receive the number of time components
 *          (hour, minute, second) found in the string. The number can
 *          be received even if parsing later fails. If not NULL this
 *          parameter will also turn any TIME part larger than
 *          DATETIME_FIELD_LIMIT into a syntax error (as opposed to an
 *          invalid, but correctly delimited, time value).
 * sep_ch(out):     if not NULL will receive the separator character used for
 *          the time, or '\0' if more separators are used.
 * has_explicit_msec(out):
 *          If not NULL and parsing successfull, will receive true if
 *          the input string explicitly sepcifies a number of
 *          milliseconds or the decimal point for time
 */
static char const *
parse_mtime_separated (char const *str, char const *strend, unsigned int *mtime, char const **syntax_check,
               int *time_parts, char *sep_ch, bool * has_explicit_msec, bool is_datetime)
{
  int h = 0, m = 0, s = 0, msec = 0;
  char const *p = str, *q;

  assert (!syntax_check || !*syntax_check);

  if (sep_ch)
    {
      *sep_ch = '0';        /* a digit can not be a separator */
    }

  if (p == strend || !char_isdigit (*p))
    {
      /* no time value written in this string */
      return NULL;
    }

  /* read hours value */
  while (p < strend && char_isdigit (*p))
    {
      if (h < DATETIME_FIELD_LIMIT)
    {
      h *= 10;
      h += *p - '0';
    }

      p++;
    }

  /* numeric datetime field overflowed and failed parsing */
  if (time_parts && h >= DATETIME_FIELD_LIMIT)
    {
      return NULL;
    }


  /* skip separators */
  q = p;
  while (p < strend && !char_isspace (*p) && !char_isalpha (*p) && !char_isdigit (*p))
    {
      if (sep_ch)
    {
      if (*sep_ch)
        {
          if (*sep_ch == '0')
        {
          *sep_ch = *p;
        }
          else
        {
          if (*sep_ch != *p)
            {
              *sep_ch = '\0';
            }
        }
        }
    }

      p++;
    }

  if (is_datetime && p < strend && char_isspace (*p))
    {
      *syntax_check = p;
      return NULL;
    }

  /* read minutes value */
  if (p < strend && char_isdigit (*p))
    {
      while (p < strend && char_isdigit (*p) && DATETIME_FIELD_LIMIT - (*p - '0') / 10 > m)
    {
      if (m < DATETIME_FIELD_LIMIT)
        {
          m *= 10;
          m += *p - '0';
        }

      p++;
    }

      if (time_parts && m >= DATETIME_FIELD_LIMIT)
    {
      return NULL;
    }

      /* skip any separators */
      q = p;

      while (p < strend && !char_isspace (*p) && !char_isalpha (*p) && !char_isdigit (*p))
    {
      if (sep_ch)
        {
          if (*sep_ch)
        {
          if (*sep_ch == '0')
            {
              *sep_ch = *p;
            }
          else
            {
              if (*sep_ch != *p)
            {
              *sep_ch = '\0';
            }
            }
        }
        }
      p++;
    }

      if (is_datetime && p < strend && char_isspace (*p))
    {
      *syntax_check = p;
      return NULL;
    }

      /* read seconds value */
      if (p < strend && char_isdigit (*p))
    {
      while (p < strend && char_isdigit (*p) && (DATETIME_FIELD_LIMIT - (*p - '0') / 10 > s))
        {
          if (s < DATETIME_FIELD_LIMIT)
        {
          s *= 10;
          s += *p - '0';
        }
          p++;
        }

      /* numeric datetime field overflowed and failed parsing */
      if (time_parts && s >= DATETIME_FIELD_LIMIT)
        {
          return NULL;
        }


      if (time_parts)
        {
          *time_parts = 3;  /* hh:mm:ss */
        }

      /* read milliseconds value */
      if (p < strend && *p == '.')
        {
          p++;
          if (p < strend && char_isdigit (*p))
        {
          msec += (*p - '0') * 100;
          p++;

          if (p < strend && char_isdigit (*p))
            {
              msec += (*p - '0') * 10;
              p++;

              if (p < strend && char_isdigit (*p))
            {
              msec += *p - '0';
              p++;

              /* attempting to round, instead of truncate, the number of milliseconds will result in
               * userspace problems when '11-02-24 23:59:59.9999' is converted to DATETIME, and then
               * '11-02-24' is converted to DATE and '23:59:59:9999' is converted to TIME User might expect
               * to get the same results in both cases, when in fact the wrap-around for the time value
               * will give different results if the number of milliseconds is rounded. */
              while (p < strend && char_isdigit (*p))
                {
                  p++;
                }
            }
            }
        }
          if (has_explicit_msec)
        {
          *has_explicit_msec = true;
        }
        }
    }
      else
    {
      /* No seconds present after the minutes and the separator. */

      if (time_parts)
        {
          *time_parts = 2;
        }
    }
    }
  else
    {
      /* No minutes value present after the hour and separator */

      if (time_parts)
    {
      if (char_isdigit (*(p - 1)))
        {
          *time_parts = 1;  /* hh */
        }
      else
        {
          *time_parts = 2;  /* { hh sep } is just like { hh sep mm } */
        }
    }
    }

  /* look for the am/pm string following the time */
  q = p;            /* save p just in case there is no am/pm string */

  while (p < strend && char_isspace (*p))
    {
      p++;
    }

  /* look for either the local or the English am/pm strings */
  if (is_local_am_str (p, strend) && (p + local_am_strlen == strend || !char_isalpha (p[local_am_strlen])))
    {
      p += local_am_strlen;
      if (h == 12)
    {
      /* 12:01am means 00:01 */
      h = 0;
    }
    }
  else if (is_local_pm_str (p, strend) && (p + local_pm_strlen == strend || !char_isalpha (p[local_pm_strlen])))
    {
      p += local_pm_strlen;
      if (h < 12)
    {
      /* only a 12-hour clock uses the am/pm string */
      h += 12;
    }
    }
  else
    {
      /* no "am"/"pm" string found */
      p = q;

      /* check if an incomplete time is followed by a space */
      if (time_parts && *time_parts < 3)
    {
      if (p < strend && char_isspace (*p))
        {
          /* turn off parsing just the time off the timestamp */
          *time_parts = 1;
        }
    }
    }

  /* check the numeric values allowing an hours value of 24, to be treated as 00, would no longer be mysql-compatible,
   * since mysql actually stores the value '24', not 00 */
  if (h > 23 || m > 59 || s > 59)
    {
      /* time parsed successfully, but the found value was unexpected */
      if (syntax_check)
    {
      *syntax_check = p;
    }
      return NULL;
    }

  /* encode (from the components) and return the parsed time */
  *mtime = encode_mtime (h, m, s, msec);
  return p;
}

/*
 * parse_explicit_mtime_separated() - Reads a TIME in '10:22:10.45 am' format.
 *                  Hours (and minutes) field may be omitted,
 *                  but only together with the separator.
 * returns: pointer to the next character in the input string that follows
 *      the parsed time
 * str(in): string with the time to be parsed
 * strend(in):  the end of the string to be parsed
 * mtime(out):  the converted time
 * syntax_check(out):
 *      if not NULL, upon conversion failure it will be set to
 *      the pointer in the input string to the last character parsed
 *      if the time could be parsed but the time value
 *      found is invalid (like 10:65:24). Should be set to
 *      false before the function invocation
 * has_explicit_msec(out):
 *      if not NULL and converstion successfull, will receive true if
 *      the input string explicitly specifies the number of
 *      milliseconds or the decimal point for time
 */
static char const *
parse_explicit_mtime_separated (char const *str, char const *strend, unsigned int *mtime, char const **syntax_check,
                bool * has_explicit_msec)
{
  int h = 0, m = 0, s = 0, msec = 0, time_parts[3] = { 0, 0, 0 };
  unsigned time_parts_found = 0;
  char const *p = str, *q;
  int msec_digit_order = 100;

  /* skip leading spaces */
  while (p < strend && char_isspace (*p))
    {
      p++;
    }

  if (p >= strend)
    {
      return NULL;
    }

  /* read up to 3 time parts (hours, minutes, seconds) from the string */
  if (p[0] == ':')
    {
      /* allow for ':MM:SS.ssss' format */
      time_parts_found++;
    }

  while (((time_parts_found && p[0] == ':' && (++p < strend && char_isdigit (*p)))
      || (!time_parts_found && char_isdigit (p[0]))) && time_parts_found < DIM (time_parts))
    {
      do
    {
      time_parts[time_parts_found] *= 10;
      time_parts[time_parts_found] += *p - '0';
      p++;
    }
      while (time_parts[time_parts_found] < DATETIME_FIELD_LIMIT && p < strend && char_isdigit (*p));

      if (p < strend && char_isdigit (*p))
    {
      /* invalid number of seconds/minutes/hours */
      return NULL;
    }
      else
    {
      time_parts_found++;
    }

      if (p >= strend)
    {
      break;
    }
    }

  /* Allow trailing ':' separator if time is incomplete */
  if (time_parts_found == DIM (time_parts) && *(p - 1) == ':')
    {
      p--;
    }

  if (p < strend && *p == '.')
    {
      p++;

      while (msec_digit_order && p < strend && char_isdigit (*p))
    {
      msec += (*p++ - '0') * msec_digit_order;
      msec_digit_order /= 10;
    }

      /* skip remaining digits in the fractional seconds part, if any trying to round, instead of truncate, the
       * milliseconds part can lead to user space problems if wrap-around is needed. */
      while (p < strend && char_isdigit (*p))
    {
      p++;
    }

      if (has_explicit_msec)
    {
      *has_explicit_msec = true;
    }
    }

  /* infer the hours, minutes and seconds from the number of time parts read */
  switch (time_parts_found)
    {
    case 3:
      h = time_parts[0];
      m = time_parts[1];
      s = time_parts[2];
      break;
    case 2:
      h = time_parts[0];
      m = time_parts[1];
      break;
    case 1:
    case 0:
      return NULL;
    }

  /* skip optional whitespace before the am/pm string following the numeric time value */
  q = p;            /* save p in case there is no am/pm string */

  while (p < strend && char_isspace (*p))
    {
      p++;
    }

  /* look for either the local or the English am/pm strings */
  if (is_local_am_str (p, strend) && (p + local_am_strlen == strend || !char_isalpha (p[local_am_strlen])))
    {
      p += local_am_strlen;
      if (h == 12)
    {
      h = 0;        /* 12:01am means 00:01 */
    }
      else if (h > 12)
    {
      if (syntax_check)
        {
          *syntax_check = p;
        }
      return NULL;
    }
    }
  else if (is_local_pm_str (p, strend) && (p + local_pm_strlen == strend || !char_isalpha (p[local_pm_strlen])))
    {
      p += local_pm_strlen;
      if (h < 12)
    {
      /* only a 12-hour clock uses the am/pm string */
      h += 12;
    }
    }
  else
    {
      p = q;
    }

  /* check the time parts read */
  if (h > 23 || m > 59 || s > 59)
    {
      if (syntax_check)
    {
      *syntax_check = p;
    }
      return NULL;
    }

  *mtime = encode_mtime (h, m, s, msec);

  return p;
}

/*
 * parse_explicit_mtime_compact()   - Reads a time in
 *                   [[[[YY MM DD] HH] MM] SS[[.][sss]
 *                  format
 *                  '.0' is a valid time that can also be
 *                  written as '.'
 * returns: pointer in the input string to the character after
 *      the converted time, if successfull, or NULL on error
 * str(in): the [DATE]TIME string, in compact form, to be converted
 * strend(in):  the end of the string to be parsed
 * mtime(out):  pointer to the converted time
 *
 */
static char const *
parse_explicit_mtime_compact (char const *str, char const *strend, unsigned int *mtime)
{
  char const *p = str, *q = str, *r = str;
  int y = 0, mo = 0, d = 0, h = 0, m = 0, s = 0, msec = 0;
  int ndigits = 0;

  /* skip leading whitespace */
  while (p < strend && char_isspace (*p))
    {
      p++;
    }

  /* count number of decimal digits in the string */
  q = p;
  r = p;

  while (q < strend && char_isdigit (*q))
    {
      q++;
    }

  if (p != q || q[0] == '.')
    {
      if (q - p > 14)
    {
      /* YYYY MM DD HH MM SS [.] [ssss..] */
      /* if (q[0] != '.') */
      {
        y = DECODE (p[0]) * 1000 + DECODE (p[1]) * 100 + DECODE (p[2]) * 10 + DECODE (p[3]);
        mo = DECODE (p[4]) * 10 + DECODE (p[5]);
        d = DECODE (p[6]) * 10 + DECODE (p[7]);
        h = DECODE (p[8]) * 10 + DECODE (p[9]);
        m = DECODE (p[10]) * 10 + DECODE (p[11]);
        s = DECODE (p[12]) * 10 + DECODE (p[13]);

        p += 14;
        msec += DECODE (*p++) * 100;
        if (p < strend && char_isdigit (*p))
          {
        msec += DECODE (*p++) * 10;
        if (p < strend && char_isdigit (*p))
          {
            /* reading 3 digits after decimal point is not mysql compatible (which only reads 2 digits) but is
             * the expected CUBRID behaviour */
            msec += DECODE (*p++);

            /* skil all other digits in the milliseconds field. */
            p = q;
          }
          }
      }
    }
      else
    {
      switch (q - p)
        {
        case 14:
          /* YYYY-MM-DD HH:MM:SS */
          y += DECODE (*p++);
          [[fallthrough]];
        case 13:
          y *= 10;
          y += DECODE (*p++);
          [[fallthrough]];
        case 12:
          /* YY-MM-DD HH:MM:SS */
          y *= 10;
          y += DECODE (*p++);
          [[fallthrough]];
        case 11:
          y *= 10;
          y += DECODE (*p++);
          [[fallthrough]];
        case 10:
          /* MM-DD HH:MM:SS */
          mo += DECODE (*p++);
          [[fallthrough]];
        case 9:
          /* M-DD HH:MM:SS */
          mo *= 10;
          mo += DECODE (*p++);
          d += DECODE (*p++) * 10;
          d += DECODE (*p++);
          [[fallthrough]];
        case 6:
          /* HH:MM:SS */
          h += DECODE (*p++);
          [[fallthrough]];
        case 5:
          /* H:MM:SS */
          h *= 10;
          h += DECODE (*p++);
          [[fallthrough]];
        case 4:
          /* MM:SS */
          m += DECODE (*p++);
          [[fallthrough]];
        case 3:
          /* M:SS */
          m *= 10;
          m += DECODE (*p++);
          [[fallthrough]];
        case 2:
          /* SS */
          s += DECODE (*p++);
          [[fallthrough]];
        case 1:
          /* S */
          s *= 10;
          s += DECODE (*p++);
          [[fallthrough]];
        case 0:
          if (*p == '.')
        {
          /* read number of milliseconds */

          p++;
          if (p < strend && char_isdigit (*p))
            {
              msec += DECODE (*p++) * 100;
              if (p < strend && char_isdigit (*p))
            {
              msec += DECODE (*p++) * 10;
              if (p < strend && char_isdigit (*p))
                {
                  msec += DECODE (*p++);

                  while (p < strend && char_isdigit (*p))
                {
                  p++;
                }
                }
            }
            }
        }
          break;
        case 7:
          /* YY-MM-DD H */
          y = DECODE (p[0]) * 10 + DECODE (p[1]);
          mo = DECODE (p[2]) * 10 + DECODE (p[3]);
          d = DECODE (p[4]) * 10 + DECODE (p[5]);
          h = DECODE (p[6]);
          p += 7;
          break;
        default:
          /* should not be reached */
        case 8:
          /* DD HH:MM:SS */
          return NULL;
        }
    }

      /* year, month, day, hour, minute, seconds have been read */
      ndigits = CAST_BUFLEN (q - r);
      if (ndigits > 6)
    {
      /* a date precedes the time in the input string */

      DB_DATE cdate;
      int year, month, day;

      /* year is also specified in the date */
      if ((ndigits == 12) || (ndigits == 7))
        {
          /* 2-digits year specified, fill in the century */
          if (y < 70)
        {
          y += 2000;
        }
          else
        {
          y += 1900;
        }
        }
      else if (ndigits <= 10)
        {
          /* No year specified with the date, fill in the current year */
          y = get_current_year ();
        }

      /* check for a valid date preceding the time in the input string */
      cdate = julian_encode (mo, d, y);
      julian_decode (cdate, &month, &day, &year, NULL);

      if (y != year || mo != month || d != day)
        {
          /* invalid date specified in front of the time in the input string */
          return NULL;
        }
    }

      /* look for either the local or the English am/pm strings */
      /* skip optional whitespace before the am/pm string following the numeric time value */
      q = p;            /* save p just in case there is no am/pm string */

      while (p < strend && char_isspace (*p))
    {
      p++;
    }

      if (is_local_am_str (p, strend) && (p + local_am_strlen == strend || !char_isalpha (p[local_am_strlen])))
    {
      p += local_am_strlen;
      if (h == 12)
        {
          h = 0;        /* 12:01am means 00:01 */
        }
    }
      else if (is_local_pm_str (p, strend) && (p + local_pm_strlen == strend || !char_isalpha (p[local_pm_strlen])))
    {
      p += local_pm_strlen;
      if (h < 12)
        {
          /* only a 12-hour clock uses the am/pm string */
          h += 12;
        }
    }
      else
    {
      p = q;
    }

      /* check and return the parsed time value */
      if (h > 23 || m > 59 || s > 59)
    {
      return NULL;
    }
      else
    {
      *mtime = encode_mtime (h, m, s, msec);
      return p;
    }
    }
  else
    {
      /* No time field nor a decimal point are present */
      return NULL;
    }
}

/* parse_timestamp_compact() - Reads a DATETIME in
 *                  [YYYY] MM DD [HH [MM [SS ["."] [msec]]]]
 *                  format
 * returns:     pointer into the input string pointer to the char after
 *          the read timestamp, if successfull, or NULL otherwise
 * str(in):     the input string with the compact timestamp to be
 *          converted
 * strend(in):  the end of the string to be parsed
 * date(out):       the parsed julian date, if successfull
 * mtime(out):      the parsed time of day (with milliseconds) if successfull
 * has_explicit_time(out):
 *          If not NULL and parsing sucessfull, will receive true if
 *          the input string explicitly specifies the time part
 * has_explicit_msec(out):
 *          If not NULL and parsing successfull, will receive true if
 *          the input string explicitly specifies the number of
 *          milliseconds or the decimal point for time
 *
 */
static char const *
parse_timestamp_compact (char const *str, char const *strend, DB_DATE * date, unsigned int *mtime,
             bool * has_explicit_time, bool * has_explicit_msec)
{
  int y = 0, mo = 0, d = 0, h = 0, m = 0, s = 0, msec = 0, ndigits = 0;
  char const *p = str, *q = str;

  /* skip leading whitespace */
  while (p < strend && char_isspace (*p))
    {
      p++;
    }

  /* cound number of continous digits in the string */
  q = p;

  while (q < strend && char_isdigit (*q))
    {
      q++;
    }

  ndigits = CAST_BUFLEN (q - p);

  if (ndigits < 3)
    {
      /* no, or too few, datetime fields present */
      return NULL;
    }

  switch (ndigits)
    {
    case 7:
      /* YY MM DD H */
      y = DECODE (p[0]) * 10 + DECODE (p[1]);
      mo = DECODE (p[2]) * 10 + DECODE (p[3]);
      d = DECODE (p[4]) * 10 + DECODE (p[5]);
      h = DECODE (p[6]);
      p += 7;
      break;
    case 8:
      /* YYYY MM DD */
      y += DECODE (*p++);
      /* YYY MM DD */
      y *= 10;
      y += DECODE (*p++);
      [[fallthrough]];
    case 6:
      /* YY MM DD */
      y *= 10;
      y += DECODE (*p++);
      [[fallthrough]];
    case 5:
      /* Y MM DD */
      y *= 10;
      y += DECODE (*p++);
      [[fallthrough]];
    case 4:
      /* MM DD */
      mo += DECODE (*p++);
      [[fallthrough]];
    case 3:
      /* M DD */
      mo *= 10;
      mo += DECODE (*p++);

      d += DECODE (*p++);
      d *= 10;
      d += DECODE (*p++);

      /* HH:MM:SS remain 00:00:00 */
      break;
    default:
      /* YYYY MM DD HH [MM [SS ["."] [sssss]]] */

      /* read year */
      if (ndigits < 14)
    {
      y = DECODE (p[0]) * 10 + DECODE (p[1]);
      if (y < 70)
        {
          y += 2000;
        }
      else
        {
          y += 1900;
        }

      p += 2;
    }
      else
    {
      y = DECODE (p[0]) * 1000 + DECODE (p[1]) * 100 + DECODE (p[2]) * 10 + DECODE (p[3]);

      p += 4;
    }

      /* read month, day, hour, minute */
      mo = DECODE (p[0]) * 10 + DECODE (p[1]);
      d = DECODE (p[2]) * 10 + DECODE (p[3]);
      h = DECODE (p[4]) * 10 + DECODE (p[5]);
      p += 6;

      m = DECODE (*p++);
      if (p < strend && char_isdigit (*p))
    {
      m *= 10;
      m += DECODE (*p++);

      if (p < strend && char_isdigit (*p))
        {
          s += DECODE (*p++);

          if (p < strend && char_isdigit (*p))
        {
          s *= 10;
          s += DECODE (*p++);
        }

          /* read milliseconds */
          if (*p == '.')
        {
          if (has_explicit_msec)
            {
              *has_explicit_msec = true;
            }
          p++;
        }

          if (p < strend && char_isdigit (*p))
        {
          if (has_explicit_msec)
            {
              *has_explicit_msec = true;
            }

          msec += DECODE (*p++) * 100;

          if (p < strend && char_isdigit (*p))
            {
              msec += DECODE (*p++) * 10;

              if (p < strend && char_isdigit (*p))
            {
              msec += DECODE (*p);

              /* skip remaining digits */
              while (p < strend && char_isdigit (*p))
                {
                  p++;
                }
            }
            }
        }
        }
    }
    }

  if (has_explicit_time)
    {
      *has_explicit_time = (ndigits == 7 || ndigits > 8);
    }

  if (ndigits < 5)
    {
      /* [M]M DD format */
      y = get_current_year ();
    }
  else
    {
      if (ndigits == 6 || ndigits == 7)
    {
      /* YY MM DD */
      if (y < 70)
        {
          y += 2000;
        }
      else
        {
          y += 1900;
        }
    }
    }


  /* y, mo, d, h, m, s and msec are now read from string, p is pointing past last digit read */

  if (ndigits > 8 || ndigits == 7)
    {
      /* the hour or the time-of-day are included in the compact string look for either the local or the English am/pm
       * strings */

      /* skip optional whitespace before the am/pm string following the numeric time value */
      q = p;            /* save p just in case there is no am/pm string */

      while (p < strend && char_isspace (*p))
    {
      p++;
    }

      if (is_local_am_str (p, strend) && (p + local_am_strlen == strend || !char_isalpha (p[local_am_strlen])))
    {
      p += local_am_strlen;
      if (h == 12)
        {
          h = 0;        /* 12:01am means 00:01 */
        }
    }
      else if (is_local_pm_str (p, strend) && (p + local_pm_strlen == strend || !char_isalpha (p[local_pm_strlen])))
    {
      p += local_pm_strlen;
      if (h < 12)
        {
          /* only a 12-hour clock uses the am/pm string */
          h += 12;
        }
    }
      else
    {
      p = q;
    }
    }

  /* check and return the date and time read */
  if (mo <= 12 && d <= 31 && h <= 23 && m <= 59 && s <= 59)
    {
      DB_DATE cdate = julian_encode (mo, d, y);
      int year, month, day;

      julian_decode (cdate, &month, &day, &year, NULL);

      if (y != year || mo != month || d != day)
    {
      /* invalid date in the input string */
      return NULL;
    }

      *date = cdate;
      *mtime = encode_mtime (h, m, s, msec);

      return (p == strend ? "" : p);
    }
  else
    {
      /* Invalid date or time specified */
      return NULL;
    }
}


/*
 * parse_timedate_separated() - Reads a time and a date from a time-date
 *              string. Note that there is no compact timedate
 *              possible, only separated timedate strings.
 * returns:     pointer to the character in str immediately following the
 *          parsed time and date
 * str(in):     the time-date string with the time and the date to be
 *          parsed (read)
 * strend(in):      the end of the string to be parsed
 * date(out):       the parsed date from the input string str
 * mtime(out):      the parsed time from the input string str
 * syntax_check(out):
 *          If not NULL and if parsing was successfull but the found
 *          value invalid (like '10:80:12 2011-12-18') it will receive
 *          the pointer to the character in str immediately following
 *          the parsed time and date.
 * has_explicit_msec(out):
 *          If not NULL and parsing successfull will receive the value
 *          true if the input string explicitly specifies the number
 *          of milliseconds for time or specifies the decimal point
 *          for milliseconds
 */
static char const *
parse_timedate_separated (char const *str, char const *strend, DB_DATE * date, unsigned int *mtime, char const **syntax,
              bool * has_explicit_msec)
{
  char sep_ch = '0';
  char const *p = str, *syntax_check = NULL;

  /* attempts to read time in explicit format */
  p = parse_explicit_mtime_separated (p, strend, mtime, &syntax_check, has_explicit_msec);

  if (!p && !syntax_check)
    {
      /* attempt to read time in the relaxed format if explicit format failed */
      if (has_explicit_msec)
    {
      *has_explicit_msec = false;
    }

      p = parse_mtime_separated (str, strend, mtime, &syntax_check, NULL, &sep_ch, has_explicit_msec, false);

      if (p || syntax_check)
    {
      if (sep_ch != '0' && sep_ch != ':')
        {
          /* the parsed time uses no ':' separator, so fallback to reading a date-time string instead of a
           * time-date string */
          p = NULL;
          syntax_check = NULL;
        }
    }
    }

  if (p || syntax_check)
    {
      bool space_separator = false;

      if (!syntax_check)
    {
      syntax_check = p;
    }

      while (syntax_check < strend && !char_isalpha (*syntax_check) && !char_isdigit (*syntax_check))
    {
      if (char_isspace (*syntax_check++))
        {
          space_separator = true;
        }
    }

      if (space_separator)
    {
      char const *q = syntax_check;

      syntax_check = NULL;
      sep_ch = '0';

      if (p)
        {
          p = parse_date_separated (q, strend, date, &syntax_check, NULL, &sep_ch);

          if (p || syntax_check)
        {
          if (sep_ch == '-' || sep_ch == '/')
            {
              if (p)
            {
              /* parsed a time-date string with valid values */
              return p;
            }
              else
            {
              /* parsed a time-date string, with an invalid date value */
              *syntax = syntax_check;
              return NULL;
            }
            }
        }
        }
      else
        {
          p = parse_date_separated (q, strend, date, &syntax_check, NULL, &sep_ch);

          if ((p || syntax_check) && (sep_ch == '-' || sep_ch == '/'))
        {
          if (p)
            {
              *syntax = p;
            }
          else
            {
              *syntax = syntax_check;
            }

          /* parsed a time-date string, with an invalid time value */
          return NULL;
        }
        }
    }
    }

  return NULL;
}

/*
 * db_date_parse_time() - Reads a TIME from a time or date-time (or
 *            time-date) string, in any of the separated
 *            or compact formats the string is in.
 * returns: 0 on success, ER_DATE_CONVERSION on error.
 * str(in): the time or date-time string to be converted
 * str_len(in): the length of the string to be converted
 * time(out):   the converted time.
 * millisecond(out):   the milliseconds part of the converted time
 */
int
db_date_parse_time (char const *str, int str_len, DB_TIME * time, int *millisecond)
{
  char const *syntax_check = NULL;
  int year_digits = 0;
  DB_DATE date = 0;
  unsigned int mtime = 0;
  char sep_ch = '0';
  char const *strend = str + str_len;
  /* attempt to read a time-date string first */
  char const *p = parse_timedate_separated (str, strend, &date, &mtime, &syntax_check,
                        NULL);

  if (p)
    {
      *time = mtime / 1000;
      *millisecond = mtime % 1000;
      return NO_ERROR;
    }
  else
    {
      if (syntax_check)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
      return ER_TIME_CONVERSION;
    }
      else
    {
      date = 0;
      mtime = 0;
    }
    }

  /* attempt to read a separated DATETIME string after parsing a time-date string failed */

  p = parse_date_separated (str, strend, &date, &syntax_check, &year_digits, &sep_ch);

  if (p || syntax_check)
    {
      bool space_separator = false;
      bool has_explicit_date_part = false;

      /* check whether string has explicit date part */
      if (sep_ch == '-' || sep_ch == '/')
    {
      has_explicit_date_part = true;
    }

      if (!syntax_check)
    {
      syntax_check = p;
    }

      while (syntax_check < strend && !char_isalpha (*syntax_check) && !char_isdigit (*syntax_check))
    {
      if (char_isspace (*syntax_check++))
        {
          space_separator = true;
        }
    }

      if (space_separator)
    {
      if (p)
        {
          /* Valid date followed by separator */
          int time_parts = 0;

          p = syntax_check;
          syntax_check = NULL;
          p = parse_mtime_separated (p, strend, &mtime, &syntax_check, &time_parts, NULL, NULL, false);

          if (p)
        {
          while (p < strend && char_isspace (*p))
            {
              p++;
            }

          /* if there is one non-space character in remaining characters */
          if (p != strend)
            {
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
              return ER_TIME_CONVERSION;
            }

          if (year_digits >= 3 || (time_parts >= 2 && year_digits))
            {
              *time = mtime / 1000;
              *millisecond = mtime % 1000;
              return NO_ERROR;
            }
        }
          else
        {
          if (syntax_check)
            {
              /* date-time string with an invalid time */
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
              return ER_TIME_CONVERSION;
            }
          else
            {
              /* no time value found following the date and separator check whether is a Date string with space
               * suffix for example,"2012-8-15 " */
              if (has_explicit_date_part)
            {
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
              return ER_TIME_CONVERSION;
            }
            }
        }
        }
      else
        {
          /* Invalid date followed by separator, */
          int time_parts = 0;

          p = syntax_check;
          syntax_check = NULL;
          p = parse_mtime_separated (p, strend, &mtime, &syntax_check, &time_parts, NULL, NULL, false);
          if (p || syntax_check)
        {
          /* date-time string with an invalid date and/or time */
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
          return ER_TIME_CONVERSION;
        }
          else
        {
          if (has_explicit_date_part)
            {
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
              return ER_TIME_CONVERSION;
            }
        }
        }
    }
      else
    {
      if (p && has_explicit_date_part)
        {
          /* only explicit Date type, should return an error. */
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
          return ER_TIME_CONVERSION;
        }
    }
    }

  /* attempt to read an explicit separated TIME string (a time-only string) */
  mtime = 0;
  syntax_check = NULL;

  p = parse_explicit_mtime_separated (str, strend, &mtime, &syntax_check, NULL);

  if (p)
    {
      if (p < strend && p[0] == ' ')
    {
      while (p < strend && char_isspace (*p))
        {
          p++;
        }

      /* if there is one non-space character in remaining characters */
      if (p != strend)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
          return ER_TIME_CONVERSION;
        }
    }

      *time = mtime / 1000;
      *millisecond = mtime % 1000;
      return NO_ERROR;
    }
  else
    {
      if (syntax_check)
    {
      /* Time could be parsed as [[HH : ]MM] : SS[.sss], but is an invalid time value */
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
      return ER_TIME_CONVERSION;
    }
      else
    {
      /* read a compact TIME string */
      p = parse_explicit_mtime_compact (str, strend, &mtime);

      if (p)
        {
          while (p < strend && char_isspace (*p))
        {
          p++;
        }

          /* if remaining characters includes one non-space character */
          if (p != strend)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
          return ER_TIME_CONVERSION;
        }

          *time = mtime / 1000;
          *millisecond = mtime % 1000;
          return NO_ERROR;
        }
      else
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIME_CONVERSION, 0);
          return ER_TIME_CONVERSION;
        }
    }
    }
}

/*
 * db_date_parse_datetime_parts() - Reads a DATETIME from a DATE, DATETIME or
 *              time-date string, in any of the separated or
 *              compact formats the string might be in.
 * returns: 0 on success, ER_DATE_CONVERSION on error.
 * str(in): the date or date-time string to be read and converted
 * str_len(in): the length of the string to be converted
 * datetime(out):
 *      the read and converted datetime
 * is_explicit_time(out):
 *      If not NULL and parsing successfull will receive true if the
 *      input string explicitly specifies the time part
 * has_explicit_msec(out):
 *      If not NULL and parsing successfull will receive true if the
 *      input string explicitly specifies the number of milliseconds
 *      or the decimal point for the time part
 * fits_as_timestamp(out):
 *      If not NULL and has_explicit_msec is not NULL and parsing
 *      is successfull will receive true if the value parsed from
 *      the given input string can be represented exactly as a
 *      TIMESTAMP value
 * endp(out):   if the datetime is found to have compact format (110918, as
 *      opposed to 11-09-18), endp receives the pointer to the end of
 *      the source string or to the trailing character in the source
 *      string that was no longer part of the datetime value to be
 *      read. if given, the pointed value should be NULL before entry
 *      to the function
 */
int
db_date_parse_datetime_parts (char const *str, int str_len, DB_DATETIME * datetime, bool * has_explicit_time,
                  bool * has_explicit_msec, bool * fits_as_timestamp, char const **endp)
{
  DB_DATE date = 0;
  unsigned int mtime = 0;
  char const *strend = str + str_len;
  char const *syntax_check = NULL, *p;

  /* read a separated time-date string first */
  p = parse_timedate_separated (str, strend, &date, &mtime, &syntax_check, has_explicit_msec);

  if (p)
    {
      if (has_explicit_time)
    {
      *has_explicit_time = true;
    }

      if (has_explicit_msec && !*has_explicit_msec && fits_as_timestamp)
    {
      DB_TIMESTAMP timestamp;
      DB_TIME time = mtime / 1000;

      *fits_as_timestamp = db_timestamp_encode_utc (&date, &time, &timestamp);
      if (*fits_as_timestamp != NO_ERROR)
        {
          er_clear ();
        }
    }

      datetime->date = date;
      datetime->time = mtime;

      goto finalcheck;
    }
  else
    {
      if (syntax_check)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
      return ER_TIMESTAMP_CONVERSION;
    }
    }

  /* read a separated DATETIME string */
  if (has_explicit_msec)
    {
      *has_explicit_msec = true;
    }

  p = parse_date_separated (str, strend, &datetime->date, &syntax_check, NULL, NULL);
  if (p)
    {
      char const *q, *r;
      char sep_ch = '0';

      syntax_check = NULL;

      /* skip the date and time separator from the string */
      while (p < strend && !char_isalpha (*p) && !char_isdigit (*p))
    {
      p++;
    }

      /* parse the time portion in the string */
      q = parse_mtime_separated (p, strend, &datetime->time, &syntax_check, NULL, &sep_ch, has_explicit_msec, true);
      if (q)
    {
      if (has_explicit_time)
        {
          r = q;
          while (r < strend && ((*r == sep_ch) || (char_isdigit (*r))))
        {
          r++;
        }
          if ((r < strend) && (!char_isspace (*r)))
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
          return ER_TIMESTAMP_CONVERSION;
        }
          *has_explicit_time = true;
        }

      if (has_explicit_msec && !*has_explicit_msec && fits_as_timestamp)
        {
          DB_TIMESTAMP timestamp;
          DB_TIME time = datetime->time / 1000;

          *fits_as_timestamp = db_timestamp_encode_utc (&datetime->date, &time, &timestamp);
          if (*fits_as_timestamp != NO_ERROR)
        {
          er_clear ();
        }
        }

      goto finalcheck;
    }
      else
    {
      if (syntax_check)
        {
          /* Invalid time value present in the string */
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
          return ER_TIMESTAMP_CONVERSION;
        }
      else
        {
          /* no time value present */
          if (has_explicit_time)
        {
          *has_explicit_time = false;
        }

          if (has_explicit_msec)
        {
          *has_explicit_msec = false;
        }

          if (fits_as_timestamp)
        {
          DB_TIMESTAMP timestamp;
          DB_TIME time = 0;

          *fits_as_timestamp = db_timestamp_encode_utc (&datetime->date, &time, &timestamp);
          if (*fits_as_timestamp != NO_ERROR)
            {
              er_clear ();
            }
        }

          datetime->time = 0;

          goto finalcheck;
        }
    }
    }
  else
    {
      char const *r;

      if (syntax_check)
    {
      /* try to parse the first date portion as YY MM DD (or more) */
      DB_DATETIME cdatetime;

      while (str < strend && char_isspace (*str))
        {
          str++;
        }

      r =
        parse_timestamp_compact (str, strend, &cdatetime.date, &cdatetime.time, has_explicit_time,
                     has_explicit_msec);

      /* mysql prefers a large value here like 14, and returns NULL otherwise */
      if (r && (*r == 0 || (*r && (r - str >= 6))))
        {
          if (has_explicit_msec && !*has_explicit_msec && fits_as_timestamp)
        {
          DB_TIMESTAMP timestamp;
          DB_TIME time = cdatetime.time / 1000;

          *fits_as_timestamp = db_timestamp_encode_utc (&cdatetime.date, &time, &timestamp);
          if (*fits_as_timestamp != NO_ERROR)
            {
              er_clear ();
            }
        }

          if (endp)
        {
          *endp = r;
        }
          *datetime = cdatetime;

          goto finalcheck;
        }
      else
        {
          /* invalid date value present in the string */
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
          return ER_TIMESTAMP_CONVERSION;
        }
    }
      else
    {
      /* read a compact DATETIME string */
      r =
        parse_timestamp_compact (str, strend, &datetime->date, &datetime->time, has_explicit_time,
                     has_explicit_msec);

      if (r)
        {
          if (has_explicit_msec && !*has_explicit_msec && fits_as_timestamp)
        {
          DB_TIMESTAMP timestamp;
          DB_TIME time = datetime->time / 1000;

          *fits_as_timestamp = db_timestamp_encode_utc (&datetime->date, &time, &timestamp);
          if (*fits_as_timestamp != NO_ERROR)
            {
              er_clear ();
            }
        }

          if (endp)
        {
          *endp = r;
        }

          goto finalcheck;
        }
      else
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
          return ER_TIMESTAMP_CONVERSION;
        }
    }
    }

  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
  return ER_TIMESTAMP_CONVERSION;

finalcheck:
  if (datetime->date == IGREG_SPECIAL)
    {
      if (datetime->time != 0)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
      return ER_TIMESTAMP_CONVERSION;
    }
    }

  return NO_ERROR;
}

/*
 * db_date_parse_datetime() - Reads a DATETIME from a DATE or DATETIME string, in any
 *              of the separated or compact formats the string
 *              might be in.
 * returns: 0 on success, ER_DATE_CONVERSION on error.
 * str(in): the date or date-time string to be read and converted
 * str_len(in): the length of the string to be converted
 * datetime(out):
 *      the read and converted datetime
 */
int
db_date_parse_datetime (char const *str, int str_len, DB_DATETIME * datetime)
{
  return db_date_parse_datetime_parts (str, str_len, datetime, NULL, NULL, NULL, NULL);
}

/*
 * db_date_parse_timestamp() - Reads a TIMESTAMP from a DATE or DATETIME
 *              string, in any of the separated or compact formats
 *              the string might be in.
 * returns: 0 on success, ER_DATE_CONVERSION on error.
 * str(in): the date string or datetime string to be read and converted
 * str_len(in): the length of the string to be converted
 * utime(out):  the converted timestamp read from string
 */
int
db_date_parse_timestamp (char const *str, int str_len, DB_TIMESTAMP * utime)
{
  DB_DATETIME datetime;
  DB_TIME time;
  int err;

  err = db_date_parse_datetime (str, str_len, &datetime);
  if (err == NO_ERROR)
    {
      if (datetime.date == IGREG_SPECIAL && datetime.time == 0)
    {
      *utime = IGREG_SPECIAL;
      return NO_ERROR;
    }

      time = datetime.time / 1000;
      if (db_timestamp_encode_ses (&datetime.date, &time, utime, NULL) == NO_ERROR)
    {
      return NO_ERROR;
    }
      else
    {
      er_clear ();
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
      return ER_TIMESTAMP_CONVERSION;
    }
    }

  er_clear ();
  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TIMESTAMP_CONVERSION, 0);
  return ER_TIMESTAMP_CONVERSION;
}

/*
 * db_date_parse_date() - Reads a DATE from a DATE string or a DATETIME
 *              string, in any of the separated or compact formats
 *              the string might be in.
 * returns: 0 on success, ER_DATE_CONVERSION on error.
 * str(in): the date or datetime string to be read and converted
 * str_len(in): the length of the string to be converted
 * date(out):   the read and converted date
 */
int
db_date_parse_date (char const *str, int str_len, DB_DATE * date)
{
  DB_DATETIME datetime = { 0, 0 };
  int err;

  err = db_date_parse_datetime (str, str_len, &datetime);
  if (err == NO_ERROR)
    {
      *date = datetime.date;
    }
  else
    {
      er_clear ();
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      err = ER_DATE_CONVERSION;
    }

  return err;
}

/*
 * parse_for_timestamp() - Tries to parse a timestamp by finding a date and a
 *             time, in order.
 * Returns: const char or NULL on error
 * buf(in): pointer to a date-time expression
 * buf_len(in): the length of the string to be parsed
 * date(out): pointer to a DB_DATE
 * time(out): pointer to a DB_TIME
 * allow_msec(in): tells if milliseconds format is allowed
 */
static const char *
parse_for_timestamp (const char *buf, int buf_len, DB_DATE * date, DB_TIME * time, bool allow_msec)
{
  const char *p;

  /* First try to parse a date followed by a time. */
  p = parse_date (buf, buf_len, date);
  if (p)
    {
      if (allow_msec)
    {
      p = parse_mtime (p, buf_len - CAST_BUFLEN (p - buf), time, NULL, NULL);
      *time /= 1000;
    }
      else
    {
      p = parse_time (p, buf_len - CAST_BUFLEN (p - buf), time);
    }
      if (p)
    {
      goto finalcheck;
    }
    }

  /* If that fails, try to parse a time followed by a date. */
  if (allow_msec)
    {
      p = parse_mtime (buf, buf_len, time, NULL, NULL);
      *time /= 1000;
    }
  else
    {
      p = parse_time (buf, buf_len, time);
    }

  if (p)
    {
      p = parse_date (p, buf_len - CAST_BUFLEN (p - buf), date);
      if (p)
    {
      goto finalcheck;
    }
    }

  return NULL;

finalcheck:
  if (*date == IGREG_SPECIAL)
    {
      if (*time != 0)
    {
      return NULL;
    }
    }

  return p;
}

/*
 * parse_datetime() -
 * Returns: const char or NULL on error
 * buf(in): pointer to a date-time expression
 * buf_len(in): the length of the string to be parsed
 * datetime(out): pointer to a DB_DATETIME to be modified
 */
static const char *
parse_datetime (const char *buf, int buf_len, DB_DATETIME * datetime)
{
  DB_DATE date = 0;
  unsigned int mtime;
  const char *p;

  /* First try to parse a date followed by a time. */
  p = parse_date (buf, buf_len, &date);
  if (p)
    {
      p = parse_mtime (p, buf_len - CAST_BUFLEN (p - buf), &mtime, NULL, NULL);
      if (p)
    {
      goto finalcheck;
    }
    }

  p = parse_mtime (buf, buf_len, &mtime, NULL, NULL);
  if (p)
    {
      p = parse_date (p, buf_len - CAST_BUFLEN (p - buf), &date);
      if (p)
    {
      goto finalcheck;
    }
    }

  return NULL;

finalcheck:
  if (date == IGREG_SPECIAL)
    {
      if (mtime != 0)
    {
      return NULL;
    }
    }

  datetime->date = date;
  datetime->time = mtime;

  return p;
}

/*
 * db_string_check_explicit_date() - check if a string is formated as a date
 * return : true if str is formated exactly as a date
 *      (not a datetime or a timestamp)
 * str(in): the string to be checked
 * str_len(in): the length of the string to be parsed
 */
bool
db_string_check_explicit_date (const char *str, int str_len)
{
  DB_DATE date;
  const char *result = NULL;

  result = parse_date (str, str_len, &date);
  if (result)
    {
      while (char_isspace (result[0]))
    {
      result++;
    }
    }
  if (result == NULL || result[0] != '\0')
    {
      return false;
    }

  return true;
}

/*
 * db_string_to_date_ex() - Parse an ordinary date string (e.g., '10/15/86').
 *    Whitespace is not permitted between slashed components. If the year is
 *    omitted, the current year is assumed.  Dates are currently accepted
 *    only in the slashified US style.
 * returns: 0 on success, ER_DATE_CONVERSION on error
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * date(out): a pointer to a DB_DATE to be modified
 */
int
db_string_to_date_ex (const char *str, int str_len, DB_DATE * date)
{
  const char *p;
  const char *p_end = str + str_len;

  p = parse_date (str, str_len, date);
  if (p != NULL)
    {
      while (p < p_end && char_isspace (p[0]))
    {
      p++;
    }
    }
  if (p == NULL || (p < p_end && p[0] != '\0'))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  return NO_ERROR;
}

/*
 * db_string_to_date() - Parse an ordinary date string (e.g., '10/15/86').
 *    Whitespace is not permitted between slashed components. If the year is
 *    omitted, the current year is assumed.  Dates are currently accepted
 *    only in the slashified US style.
 * returns: 0 on success, ER_DATE_CONVERSION on error
 * str(in): a buffer containing a date to be parsed
 * date(out): a pointer to a DB_DATE to be modified
 */
int
db_string_to_date (const char *str, DB_DATE * date)
{
  return db_string_to_date_ex (str, strlen (str), date);
}

/*
 * db_string_to_time_ex() - Parse an ordinary time string (e.g., '3:30am').
 *    Whitespace is not permitted between numeric components, it is permitted
 *    between the last number and the optional am/pm designator.
 * return : 0 on success, ER_DATE_CONVERSION on error.
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * time(out): a pointer to a DB_TIME to be modified
 */
int
db_string_to_time_ex (const char *str, int str_len, DB_TIME * time)
{
  const char *p;
  const char *p_end = str + str_len;

  p = parse_time (str, str_len, time);
  if (p != NULL)
    {
      while (p < p_end && char_isspace (p[0]))
    {
      p++;
    }
    }
  if (p == NULL || (p < p_end && p[0] != '\0'))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  return NO_ERROR;
}

/*
 * db_string_to_time() - Parse an ordinary time string (e.g., '3:30am').
 *    Whitespace is not permitted between numeric components, it is permitted
 *    between the last number and the optional am/pm designator.
 * return : 0 on success, ER_DATE_CONVERSION on error.
 * str(in): a buffer containing a date to be parsed
 * time(out): a pointer to a DB_TIME to be modified
 */
int
db_string_to_time (const char *str, DB_TIME * time)
{
  return db_string_to_time_ex (str, strlen (str), time);
}

/*
 * db_string_to_timestamp_ex() - Parse a date and time string into a utime.
 *    The time and date are parsed according to the same rules as
 *    db_string_to_time() and db_string_to_date().
 *    they may appear in either order.
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * utime(out): a pointer to a DB_TIMESTAMP to be modified
 */
int
db_string_to_timestamp_ex (const char *str, int str_len, DB_TIMESTAMP * utime)
{
  DB_DATE date;
  DB_TIME time;
  int err = NO_ERROR;
  const char *p, *p_end;
  TZ_ID dummy_tz_id;

  p_end = str + str_len;
  p = parse_for_timestamp (str, str_len, &date, &time, false);
  if (p != NULL)
    {
      while (p < p_end && char_isspace (p[0]))
    {
      p++;
    }
    }
  if (p == NULL || (p < p_end && p[0] != '\0'))
    {
      goto error_exit;
    }

  /* 0000-00-00 00:00:00 treated as time_t 0 */
  if (date == IGREG_SPECIAL)
    {
      *utime = IGREG_SPECIAL;
      return err;
    }

  err = db_timestamp_encode_ses (&date, &time, utime, &dummy_tz_id);
  return err;

error_exit:
  er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
  return ER_DATE_CONVERSION;
}

/*
 * db_string_to_timestamp() - Parse a date and time string into a utime.
 *    The time and date are parsed according to the same rules as
 *    db_string_to_time() and db_string_to_date().
 *    they may appear in either order.
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * utime(out): a pointer to a DB_TIMESTAMP to be modified
 */
int
db_string_to_timestamp (const char *str, DB_TIMESTAMP * utime)
{
  return db_string_to_timestamp_ex (str, strlen (str), utime);
}

/*
 * db_string_to_timestamptz_ex() - Parse a date and time with time zone info
 *                 into a timestamp with TZ

 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * ts_tz(out): a pointer to a DB_TIMESTAMPTZ to be modified
 * has_zone(out): true if string had valid zone information to decode, false
 *        otherwise
 * is_cast(in): true if the function is called in a casting context
 */
int
db_string_to_timestamptz_ex (const char *str, int str_len, DB_TIMESTAMPTZ * ts_tz, bool * has_zone, bool is_cast)
{
  DB_DATE date;
  DB_TIME time;
  int err = NO_ERROR;
  int str_zone_size = 0;
  const char *p, *p_end, *str_zone;
  TZ_REGION session_tz_region;

  str_zone = NULL;
  p_end = str + str_len;
  *has_zone = false;

  tz_get_session_tz_region (&session_tz_region);
  ts_tz->tz_id = 0;

  p = parse_for_timestamp (str, str_len, &date, &time, is_cast);

  if (p == NULL)
    {
      goto error_exit;
    }

  assert (p != NULL);
  while (p < p_end && char_isspace (p[0]))
    {
      p++;
    }

  if (p < p_end)
    {
      *has_zone = true;
      str_zone = p;
      str_zone_size = CAST_BUFLEN (p_end - str_zone);
    }

  err = tz_create_timestamptz (&date, &time, str_zone, str_zone_size, &session_tz_region, ts_tz, &p);
  if (err != NO_ERROR || str_zone == NULL)
    {
      /* error or no timezone in user string (no trailing chars to check) */
      return err;
    }

  if (p != NULL)
    {
      while (p < p_end && char_isspace (p[0]))
    {
      p++;
    }
    }

  if (p == NULL || (p < p_end && p[0] != '\0'))
    {
      goto error_exit;
    }

  return err;

error_exit:
  er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
  return ER_DATE_CONVERSION;
}

/*
 * db_string_to_timestamptz() - Parse a date and time with time zone info into
 *              a timestamp with TZ

 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * ts_tz(out): a pointer to a DB_TIMESTAMPTZ to be modified
 * has_zone(out): true if string had valid zone information to decode, false
 *        otherwise
 */
int
db_string_to_timestamptz (const char *str, DB_TIMESTAMPTZ * ts_tz, bool * has_zone)
{
  return db_string_to_timestamptz_ex (str, strlen (str), ts_tz, has_zone, false);
}

/*
 * db_string_to_timestampltz_ex() - Parse a date and time into a timestamp
 *                  with local timezone
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * ts(out): a pointer to a DB_TIMESTAMP to be modified
 */
int
db_string_to_timestampltz_ex (const char *str, int str_len, DB_TIMESTAMP * ts)
{
  int error = NO_ERROR;
  DB_TIMESTAMPTZ ts_tz;
  bool dummy_has_zone;

  error = db_string_to_timestamptz_ex (str, str_len, &ts_tz, &dummy_has_zone, false);
  if (error != NO_ERROR)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  *ts = ts_tz.timestamp;

  return error;
}

/*
 * db_string_to_timestampltz() - Parse a date and time into a timestamp with
 *               local timezone

 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * ts(out): a pointer to a DB_TIMESTAMP to be modified
 */
int
db_string_to_timestampltz (const char *str, DB_TIMESTAMP * ts)
{
  return db_string_to_timestampltz_ex (str, strlen (str), ts);
}

/*
 * db_date_to_string() - Print a DB_DATE into a char buffer using strftime().
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * date(in): a pointer to a DB_DATE to be printed
 *
 * note: The format string MUST contain a %d in WINDOWS. Keep this file
 *    from passing through the %ld filter.
 *    Do note pass pointers to the tm structure to db_date_decode.
 */
int
db_date_to_string (char *buf, int bufsize, DB_DATE * date)
{
  int mon, day, year;
  const int len_out = 10;
  int cnt = 0;

  if (buf == NULL || bufsize == 0)
    {
      return 0;
    }
  if (bufsize <= len_out)
    {
      return 0;
    }

  db_date_decode (date, &mon, &day, &year);
  buf[cnt++] = mon / 10 + '0';
  buf[cnt++] = mon % 10 + '0';
  buf[cnt++] = '/';
  buf[cnt++] = day / 10 + '0';
  buf[cnt++] = day % 10 + '0';
  buf[cnt++] = '/';
  buf[cnt++] = year / 1000 + '0';
  buf[cnt++] = (year / 100) % 10 + '0';
  buf[cnt++] = (year / 10) % 10 + '0';
  buf[cnt++] = year % 10 + '0';
  buf[cnt] = '\0';

  return cnt;
}

/*
 * db_time_to_string() - Print a DB_TIME into a char buffer using strftime().
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * time(in): a pointer to a DB_TIME to be printed
 *
 * note : DO NOT pass pointers to the tm structure to db_time_decode.
 */
int
db_time_to_string (char *buf, int bufsize, DB_TIME * time)
{
  int hour, min, sec;
  bool pm;
  int cnt = 0;
  const int len_out = 11;

  if (buf == NULL || bufsize == 0)
    {
      return 0;
    }
  if (bufsize <= len_out)
    {
      return 0;
    }

  db_time_decode (time, &hour, &min, &sec);
  pm = (hour >= 12) ? true : false;
  if (hour == 0)
    {
      hour = 12;
    }
  else if (hour > 12)
    {
      hour -= 12;
    }

  buf[cnt++] = hour / 10 + '0';
  buf[cnt++] = hour % 10 + '0';
  buf[cnt++] = ':';
  buf[cnt++] = min / 10 + '0';
  buf[cnt++] = min % 10 + '0';
  buf[cnt++] = ':';
  buf[cnt++] = sec / 10 + '0';
  buf[cnt++] = sec % 10 + '0';
  buf[cnt++] = ' ';
  if (pm)
    {
      buf[cnt++] = 'P';
      buf[cnt++] = 'M';
    }
  else
    {
      buf[cnt++] = 'A';
      buf[cnt++] = 'M';
    }
  buf[cnt] = '\0';

  return cnt;
}

/*
 * db_timestamp_to_string() - Print a DB_TIMESTAMP into a char buffer using
 *    strftime().
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * utime(in): a pointer to a DB_TIMESTAMP to be printed
 */
int
db_timestamp_to_string (char *buf, int bufsize, DB_TIMESTAMP * utime)
{
  DB_DATE date;
  DB_TIME time;
  int m, n;

  (void) db_timestamp_decode_ses (utime, &date, &time);
  m = db_time_to_string (buf, bufsize, &time);
  if (m == 0)
    {
      return 0;
    }
  if (bufsize - m < 2)
    {
      return 0;
    }
  buf[m] = ' ';
  m += 1;
  n = db_date_to_string (buf + m, bufsize - m, &date);
  if (n == 0)
    {
      return 0;
    }
  return m + n;
}

/*
 * db_timestamptz_to_string() - Print a DB_TIMESTAMP and a time zone into a
 *               char buffer
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * utime(in): a pointer to a DB_TIMESTAMP to be printed
 * tz_id(in): reference timezone
 */
int
db_timestamptz_to_string (char *buf, int bufsize, DB_TIMESTAMP * utime, const TZ_ID * tz_id)
{
  int n, res;
  DB_DATE date;
  DB_TIME time;
  int err = NO_ERROR;

  err = db_timestamp_decode_w_tz_id (utime, tz_id, &date, &time);
  if (err != NO_ERROR)
    {
      return 0;
    }

  res = db_time_to_string (buf, bufsize, &time);
  if (res == 0)
    {
      return 0;
    }

  if (bufsize - res < 2)
    {
      return 0;
    }
  n = res;
  buf[n] = ' ';
  n += 1;
  res = db_date_to_string (buf + n, bufsize - n, &date);
  if (res == 0)
    {
      return 0;
    }

  n += res;

  buf[n] = ' ';
  n += 1;

  res = tz_id_to_str (tz_id, buf + n, bufsize - n);
  if (res < 0)
    {
      return 0;
    }

  n += res;
  return n;
}

/*
 * db_timestampltz_to_string() - Print a DB_TIMESTAMP with session time zone
 *               into a char buffer using
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * utime(in): a pointer to a DB_TIMESTAMP to be printed
 */
int
db_timestampltz_to_string (char *buf, int bufsize, DB_TIMESTAMP * utime)
{
  int err = NO_ERROR;
  TZ_ID ses_tz_id;

  err = tz_create_session_tzid_for_timestamp (utime, &ses_tz_id);
  if (err != NO_ERROR)
    {
      return 0;
    }

  return db_timestamptz_to_string (buf, bufsize, utime, &ses_tz_id);
}

/*
 * DB_DATETIME FUNCTIONS
 */

/*
 * encode_mtime() -
 * return : millisecond time value
 * hour(in): hour
 * minute(in): minute
 * second(in): second
 * millisecond(in): millisecond
 */
static unsigned int
encode_mtime (int hour, int minute, int second, int millisecond)
{
  return ((((((hour * 60) + minute) * 60) + second) * 1000) + millisecond);
}

/*
 * decode_mtime() - Converts encoded millisecond time into
 *                  hour/minute/second/millisecond values.
 * return : void
 * time(in): encoded relative time value
 * hourp(out): hour pointer
 * minutep(out) : minute pointer
 * secondp(out) : second pointer
 * millisecondp(out) : millisecond pointer
 */
static void
decode_mtime (int mtimeval, int *hourp, int *minutep, int *secondp, int *millisecondp)
{
  int hours, minutes, seconds, milliseconds;

  milliseconds = mtimeval % 1000;
  seconds = (mtimeval / 1000) % 60;
  minutes = (mtimeval / 60000) % 60;
  hours = (mtimeval / 3600000) % 24;

  if (hourp != NULL)
    {
      *hourp = hours;
    }
  if (minutep != NULL)
    {
      *minutep = minutes;
    }
  if (secondp != NULL)
    {
      *secondp = seconds;
    }
  if (millisecondp != NULL)
    {
      *millisecondp = milliseconds;
    }
}

/*
 * db_datetime_to_string() - Print a DB_DATETIME into a char buffer using
 *    strftime().
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * datetime(in): a pointer to a DB_DATETIME to be printed
 */
int
db_datetime_to_string (char *buf, int bufsize, DB_DATETIME * datetime)
{
  int mon, day, year;
  int hour, minute, second, millisecond;
  const char *ampm;
  int cnt = 0;

  if (buf == NULL || bufsize <= QSTR_DATETIME_LENGTH)
    {
      return 0;
    }

  db_datetime_decode (datetime, &mon, &day, &year, &hour, &minute, &second, &millisecond);
  ampm = (hour >= 12) ? "PM" : "AM";
  hour = (hour % 12 == 0) ? 12 : (hour % 12);

  cnt =
    snprintf (buf, bufsize, "%02d:%02d:%02d.%03d %s %02d/%02d/%04d", hour, minute, second, millisecond, ampm, mon, day,
          year);

  return (cnt < 0 || cnt >= bufsize) ? 0 : cnt;
}

/*
 * db_datetimetz_to_string() - Print a DB_DATETIME with time zone into a char
 *                 buffer using timezone specified
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * dt(in): a pointer to a DB_DATETIME to be printed
 * tz_id(in): zone identifier
 */
int
db_datetimetz_to_string (char *buf, int bufsize, DB_DATETIME * dt, const TZ_ID * tz_id)
{
  int retval, n;
  DB_DATETIME dt_local;

  retval = tz_utc_datetimetz_to_local (dt, tz_id, &dt_local);
  if (retval == ER_QPROC_TIME_UNDERFLOW)
    {
      db_datetime_encode (&dt_local, 0, 0, 0, 0, 0, 0, 0);
      retval = NO_ERROR;
      er_clear ();
    }

  if (retval != NO_ERROR)
    {
      return 0;
    }

  retval = db_datetime_to_string (buf, bufsize, &dt_local);

  if (retval <= 0 || retval > bufsize + 1)
    {
      return retval;
    }
  n = retval;

  buf[n] = ' ';
  n++;

  retval = tz_id_to_str (tz_id, buf + n, bufsize - n);
  if (retval < 0)
    {
      return 0;
    }

  n += retval;
  return n;
}

/*
 * db_datetimeltz_to_string() - Print a DB_DATETIME with time zone into a char
 *              buffer using session local timezone
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * dt(in): a pointer to a DB_DATETIME to be printed
 */
int
db_datetimeltz_to_string (char *buf, int bufsize, DB_DATETIME * dt)
{
  int retval;
  TZ_ID tz_id;

  retval = tz_create_session_tzid_for_datetime (dt, true, &tz_id);
  if (retval != NO_ERROR)
    {
      return 0;
    }

  return db_datetimetz_to_string (buf, bufsize, dt, &tz_id);
}

/*
 * db_datetime_to_string2() - Print a DB_DATETIME into a char buffer using
 *    strftime().
 * return : the number of characters actually printed.
 * buf(out): a buffer to receive the printed representation
 * bufsize(in): the size of that buffer
 * datetime(in): a pointer to a DB_DATETIME to be printed
 *
 * Note: version without PM and AM and formatted YYYY-MM-DD HH:MM:SS.MMM
 */
int
db_datetime_to_string2 (char *buf, int bufsize, DB_DATETIME * datetime)
{
  int mon, day, year;
  int hour, minute, second, millisecond;
  int retval;

  if (buf == NULL || bufsize == 0)
    {
      return 0;
    }

  db_datetime_decode (datetime, &mon, &day, &year, &hour, &minute, &second, &millisecond);

  if (millisecond > 0)
    {
      retval =
    snprintf (buf, bufsize, "%04d-%02d-%02d %02d:%02d:%02d.%03d", year, mon, day, hour, minute, second,
          millisecond);
    }
  else
    {
      retval = snprintf (buf, bufsize, "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, minute, second);
    }
  if (bufsize < retval)
    {
      retval = 0;
    }

  return retval;
}

/*
 * db_string_to_datetime_ex() -
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * datetime(out): a pointer to a DB_DATETIME to be modified
 */
int
db_string_to_datetime_ex (const char *str, int str_len, DB_DATETIME * datetime)
{
  const char *p;
  const char *p_end = str + str_len;

  p = parse_datetime (str, str_len, datetime);
  if (p != NULL)
    {
      while (p < p_end && char_isspace (p[0]))
    p++;
    }
  if (p == NULL || (p < p_end && p[0] != '\0'))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  return NO_ERROR;
}

/*
 * db_string_to_datetime() -
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * datetime(out): a pointer to a DB_DATETIME to be modified
 */
int
db_string_to_datetime (const char *str, DB_DATETIME * datetime)
{
  return db_string_to_datetime_ex (str, strlen (str), datetime);
}

/*
 * db_string_to_datetimetz_ex() -
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * dt_tz(out): a pointer to a DB_DATETIMETZ to be modified
 * has_zone(out): true if string has valid zone information, false otherwise
 */
int
db_string_to_datetimetz_ex (const char *str, int str_len, DB_DATETIMETZ * dt_tz, bool * has_zone)
{
  int er_status = NO_ERROR;
  int str_zone_size = 0;
  const char *p, *p_end;
  const char *str_zone = NULL;
  TZ_REGION session_tz_region;

  p_end = str + str_len;
  dt_tz->tz_id = 0;
  *has_zone = false;

  tz_get_session_tz_region (&session_tz_region);

  p = parse_datetime (str, str_len, &dt_tz->datetime);
  if (p == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  while (p < p_end && char_isspace (p[0]))
    {
      p++;
    }

  if (p < p_end)
    {
      *has_zone = true;
      str_zone = p;
      str_zone_size = CAST_BUFLEN (p_end - str_zone);
    }

  er_status = tz_create_datetimetz (&dt_tz->datetime, str_zone, str_zone_size, &session_tz_region, dt_tz, &p);
  if (er_status != NO_ERROR || str_zone == NULL)
    {
      /* error or no timezone in user string (no trailing chars to check) */
      return er_status;
    }

  if (p != NULL)
    {
      while (p < p_end && char_isspace (p[0]))
    {
      p++;
    }
    }

  if (p == NULL || (p < p_end && p[0] != '\0'))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_DATE_CONVERSION, 0);
      return ER_DATE_CONVERSION;
    }

  return er_status;
}

/*
 * db_string_to_datetimetz() -
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * dt_tz(out): a pointer to a DB_DATETIMETZ to be modified
 * has_zone(out): true if string has valid zone information, false otherwise
 */
int
db_string_to_datetimetz (const char *str, DB_DATETIMETZ * dt_tz, bool * has_zone)
{
  return db_string_to_datetimetz_ex (str, strlen (str), dt_tz, has_zone);
}

/*
 * db_string_to_datetimeltz_ex() -
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * str_len(in): the length of the string to be parsed
 * datetime(out): a pointer to a DB_DATETIME to be modified
 */
int
db_string_to_datetimeltz_ex (const char *str, int str_len, DB_DATETIME * datetime)
{
  int error = NO_ERROR;
  DB_DATETIMETZ dt_tz;
  bool dummy_has_zone;

  error = db_string_to_datetimetz_ex (str, str_len, &dt_tz, &dummy_has_zone);
  if (error == NO_ERROR)
    {
      *datetime = dt_tz.datetime;
    }

  return error;
}

/*
 * db_string_to_datetimeltz() -
 * return : 0 on success, -1 on error.
 * str(in): a buffer containing a date to be parsed
 * datetime(out): a pointer to a DB_DATETIME to be modified
 */
int
db_string_to_datetimeltz (const char *str, DB_DATETIME * datetime)
{
  return db_string_to_datetimeltz_ex (str, strlen (str), datetime);
}

/*
 * db_datetime_decode() - Converts encoded datetime into
 *                        month/day/year/hour/minute/second/millisecond values.
 * return : error code
 * datetime(in): pointer to DB_DATETIME
 * month(out): pointer to month
 * day(out): pointer to day
 * year(out): pointer to year
 * hour(out): pointer to hour
 * minute(out): pointer to minute
 * second(out): pointer to second
 * millisecond(out): pointer to millisecond
 */
int
db_datetime_decode (const DB_DATETIME * datetime, int *month, int *day, int *year, int *hour, int *minute, int *second,
            int *millisecond)
{
  db_date_decode (&datetime->date, month, day, year);
  decode_mtime (datetime->time, hour, minute, second, millisecond);

  return NO_ERROR;
}

/*
 * db_datetime_encode() - Converts month/day/year/hour/minute/second/millisecond
 *                        into an encoded relative
 * return : error code
 * datetime(in): pointer to DB_DATETIME
 * month(out): month
 * day(out): day
 * year(out): year
 * hour(out): hour
 * minute(out): minute
 * second(out): second
 * millisecond(out): millisecond
 */
int
db_datetime_encode (DB_DATETIME * datetime, int month, int day, int year, int hour, int minute, int second,
            int millisecond)
{
  datetime->time = encode_mtime (hour, minute, second, millisecond);
  return db_date_encode (&datetime->date, month, day, year);
}

/*
 * db_subtract_int_from_datetime() -
 * return : error code
 * datetime(in):
 * i2(in):
 * result_datetime(out):
 */
int
db_subtract_int_from_datetime (DB_DATETIME * dt1, DB_BIGINT bi2, DB_DATETIME * result_datetime)
{
  DB_BIGINT bi1, result_bi, tmp_bi;

  if (bi2 < 0)
    {
      if (bi2 == DB_BIGINT_MIN)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_TIME_UNDERFLOW, 0);
      return ER_QPROC_TIME_UNDERFLOW;
    }

      return db_add_int_to_datetime (dt1, -bi2, result_datetime);
    }

  bi1 = ((DB_BIGINT) dt1->date) * MILLISECONDS_OF_ONE_DAY + dt1->time;

  result_bi = bi1 - bi2;
  if (OR_CHECK_SUB_UNDERFLOW (bi1, bi2, result_bi))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_TIME_UNDERFLOW, 0);
      return ER_QPROC_TIME_UNDERFLOW;
    }

  tmp_bi = (DB_BIGINT) (result_bi / MILLISECONDS_OF_ONE_DAY);
  if (OR_CHECK_INT_OVERFLOW (tmp_bi) || tmp_bi > DB_DATE_MAX || tmp_bi < DB_DATE_MIN)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_TIME_UNDERFLOW, 0);
      return ER_QPROC_TIME_UNDERFLOW;
    }
  result_datetime->date = (int) tmp_bi;
  result_datetime->time = (int) (result_bi % MILLISECONDS_OF_ONE_DAY);

  return NO_ERROR;
}

/*
 * db_add_int_to_datetime() -
 * return : error code
 * datetime(in):
 * i2(in):
 * result_datetime(out):
 */
int
db_add_int_to_datetime (DB_DATETIME * datetime, DB_BIGINT bi2, DB_DATETIME * result_datetime)
{
  DB_BIGINT bi1, result_bi, tmp_bi;

  if (bi2 < 0)
    {
      if (bi2 == DB_BIGINT_MIN)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_TIME_UNDERFLOW, 0);
      return ER_QPROC_TIME_UNDERFLOW;
    }

      return db_subtract_int_from_datetime (datetime, -bi2, result_datetime);
    }

  bi1 = ((DB_BIGINT) datetime->date) * MILLISECONDS_OF_ONE_DAY + datetime->time;

  result_bi = bi1 + bi2;
  if (OR_CHECK_ADD_OVERFLOW (bi1, bi2, result_bi))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_TIME_UNDERFLOW, 0);
      return ER_QPROC_TIME_UNDERFLOW;
    }

  tmp_bi = (DB_BIGINT) (result_bi / MILLISECONDS_OF_ONE_DAY);
  if (OR_CHECK_INT_OVERFLOW (tmp_bi) || tmp_bi > DB_DATE_MAX || tmp_bi < DB_DATE_MIN)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_TIME_UNDERFLOW, 0);
      return ER_QPROC_TIME_UNDERFLOW;
    }

  result_datetime->date = (int) tmp_bi;
  result_datetime->time = (int) (result_bi % MILLISECONDS_OF_ONE_DAY);

  return NO_ERROR;
}

/*
 *  db_get_day_of_year() - returns the day of the year (1 to 365(6))
 */
int
db_get_day_of_year (int year, int month, int day)
{
  int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
  int day_of_year = 0;
  int i;

  for (i = 1; i < month; i++)
    {
      day_of_year += days[i - 1];
    }

  day_of_year += day;

  /* for leap years, add one extra day if we're past February. A leap year i a year that is divisible by 4 and if it is
   * divisible 100 it is also divisible by 400 */
  if (month > 2 && IS_LEAP_YEAR (year))
    {
      day_of_year++;
    }
  return day_of_year;
}

/*
 *  db_get_day_of_week() - returns the day of week (0 = Sunday, 6 = Saturday)
 */
int
db_get_day_of_week (int year, int month, int day)
{
  if (year == 0 && month == 0 && day == 0)
    {
      return 0;
    }

  if (month < 3)
    {
      month = month + 12;
      year = year - 1;
    }

  return (day + (2 * month) + (int) (6 * (month + 1) / 10) + year + (int) (year / 4) - (int) (year / 100) +
      (int) (year / 400) + 1) % 7;
}

/*
 *  get_end_of_week_one_of_year() - returns the day number (1-15) at which
 *                                  week 1 in the year ends
 *    year(in)  : year
 *    mode  : specifies the way in which to consider what "week 1" means
 *
 *      mode  First day   Range Week 1 is the first week
 *        of week
 *      0     Sunday      0-53  with a Sunday in this year
 *      1     Monday      0-53  with more than 3 days this year
 *      2     Sunday      1-53  with a Sunday in this year
 *      3     Monday      1-53  with more than 3 days this year
 *      4     Sunday      0-53  with more than 3 days this year
 *      5     Monday      0-53  with a Monday in this year
 *      6     Sunday      1-53  with more than 3 days this year
 *      7     Monday      1-53  with a Monday in this year
 */
static int
get_end_of_week_one_of_year (int year, int mode)
{
  int dow_1Jan = 1 + db_get_day_of_week (year, 1, 1);

  switch (mode)
    {
    case 0:
    case 2:
      if (dow_1Jan == 1)
    {
      return 7;
    }
      else
    {
      return 7 + 8 - dow_1Jan;
    }
    case 1:
    case 3:
      if (dow_1Jan == 1)
    {
      return 7 + 1;
    }
      else if (dow_1Jan > 5)
    {
      return 16 - dow_1Jan;
    }
      else
    {
      return 9 - dow_1Jan;
    }
    case 4:
      if (dow_1Jan > 4)
    {
      return 7 + 8 - dow_1Jan;
    }
      else
    {
      return 8 - dow_1Jan;
    }
    case 5:
      if (dow_1Jan == 1)
    {
      return 7 + 1;
    }
      else if (dow_1Jan == 2)
    {
      return 7;
    }
      else
    {
      return 16 - dow_1Jan;
    }
    case 6:
      if (dow_1Jan > 4)
    {
      return 7 + 8 - dow_1Jan;
    }
      else
    {
      return 8 - dow_1Jan;
    }
    case 7:
      if (dow_1Jan == 2)
    {
      return 7;
    }
      else
    {
      return 16 - dow_1Jan;
    }
    }

  assert (false);

  return 7;
}

/*
 *  db_get_week_of_year() - returns the week number
 *    year(in)  : year
 *    month(in) : month
 *    day(in)   : day of month
 *    mode  : specifies the way in which to compute the week number:
 *
 *      mode  First day   Range Week 1 is the first week
 *        of week
 *      0     Sunday      0-53  with a Sunday in this year
 *      1     Monday      0-53  with more than 3 days this year
 *      2     Sunday      1-53  with a Sunday in this year
 *      3     Monday      1-53  with more than 3 days this year
 *      4     Sunday      0-53  with more than 3 days this year
 *      5     Monday      0-53  with a Monday in this year
 *      6     Sunday      1-53  with more than 3 days this year
 *      7     Monday      1-53  with a Monday in this year
 */
int
db_get_week_of_year (int year, int month, int day, int mode)
{
  const int day_of_year = db_get_day_of_year (year, month, day);
  const int end_of_first_week = get_end_of_week_one_of_year (year, mode);
  int week_number = 0;
  int last_week_of_year = 0;
  int days_last_year = 0;

  assert ((0 <= mode) && (mode <= 7));

  if (day_of_year > end_of_first_week)
    {
      /* if it isn't in the first week, compute week number */
      week_number = 1 + (day_of_year - end_of_first_week) / 7;
      if ((day_of_year - end_of_first_week) % 7)
    {
      week_number++;
    }
      return week_number;
    }

  if (day_of_year > end_of_first_week - 7)
    {
      /* it is in the first week */
      return 1;
    }

  /* day_of_year is in the last week of the previous year */
  if (mode == 0 || mode == 1 || mode == 4 || mode == 5)
    {
      /* return 0 for those modes */
      return 0;
    }

  last_week_of_year = get_end_of_week_one_of_year (year - 1, mode);

  days_last_year = 365;
  /* if it is a leap year */
  if (IS_LEAP_YEAR (year - 1))
    {
      days_last_year++;
    }

  week_number = 1 + (days_last_year - last_week_of_year) / 7;
  if ((days_last_year - last_week_of_year) % 7 != 0)
    {
      week_number++;
    }
  return week_number;
}

/* db_check_time_date_format()
  returns:
    1 if it has only time specifiers,
    2 if it has only date specifiers,
    3 if it has them both
    4 if it has both time and timezone specifiers
    5 if it has time, date and timezone specifiers
  */
int
db_check_time_date_format (const char *format_s)
{
  int i, res = 0, len;
  int format_type[256];
  bool has_timezone = false;

  len = strlen (format_s);
  memset (format_type, 0, sizeof (format_type));

  /* time */
  format_type['f'] = TIME_SPECIFIER;
  format_type['H'] = TIME_SPECIFIER;
  format_type['h'] = TIME_SPECIFIER;
  format_type['I'] = TIME_SPECIFIER;
  format_type['i'] = TIME_SPECIFIER;
  format_type['k'] = TIME_SPECIFIER;
  format_type['l'] = TIME_SPECIFIER;
  format_type['p'] = TIME_SPECIFIER;
  format_type['r'] = TIME_SPECIFIER;
  format_type['S'] = TIME_SPECIFIER;
  format_type['s'] = TIME_SPECIFIER;
  format_type['T'] = TIME_SPECIFIER;

  /* date */
  format_type['a'] = DATE_SPECIFIER;
  format_type['b'] = DATE_SPECIFIER;
  format_type['c'] = DATE_SPECIFIER;
  format_type['D'] = DATE_SPECIFIER;
  format_type['d'] = DATE_SPECIFIER;
  format_type['e'] = DATE_SPECIFIER;
  format_type['j'] = DATE_SPECIFIER;
  format_type['M'] = DATE_SPECIFIER;
  format_type['m'] = DATE_SPECIFIER;
  format_type['U'] = DATE_SPECIFIER;
  format_type['u'] = DATE_SPECIFIER;
  format_type['V'] = DATE_SPECIFIER;
  format_type['v'] = DATE_SPECIFIER;
  format_type['W'] = DATE_SPECIFIER;
  format_type['w'] = DATE_SPECIFIER;
  format_type['X'] = DATE_SPECIFIER;
  format_type['x'] = DATE_SPECIFIER;
  format_type['Y'] = DATE_SPECIFIER;
  format_type['y'] = DATE_SPECIFIER;

  for (i = 1; i < len; i++)
    {
      if (format_s[i - 1] != '%')   /* %x */
    continue;
      if (i > 1 && format_s[i - 2] == '%')  /* but not %%x */
    continue;

      if (format_type[(unsigned char) format_s[i]] != 0)
    {
      res |= format_type[(unsigned char) format_s[i]];
    }
      if (i + 2 < len && format_s[i] == 'T' && format_s[i + 1] == 'Z')
    {
      switch (format_s[i + 2])
        {
        case 'R':
        case 'D':
        case 'H':
        case 'M':
          has_timezone = true;
          break;
        default:
          break;
        }
    }
    }

  if (has_timezone)
    {
      if (res == DATE_SPECIFIER || res == DATETIME_SPECIFIER)
    {
      res = DATETIMETZ_SPECIFIER;
    }
    }

  return res;
}


/*
 * db_add_weeks_and_days_to_date () - add weeks and days to a gived date
 *   return: ER_FAILED error, NO_ERROR ok
 *   day(in,out) : day of date
 *   month(in,out) : month of date
 *   year(in,out) : year of date
 *   weeks(in) : how many weeks will be added to date
 *   day_weeks : how many days will be added to date
 * Note :
 *   day, month and year will be updated just in case of no error
 */
int
db_add_weeks_and_days_to_date (int *day, int *month, int *year, int weeks, int day_week)
{
  int days_months[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  int d, m, y, i;

  if (day == NULL || month == NULL || year == NULL)
    {
      return ER_FAILED;
    }

  if (*year < 0 || *year > 9999)
    {
      return ER_FAILED;
    }

  if (*month < 1 || *month > 12)
    {
      return ER_FAILED;
    }

  if (IS_LEAP_YEAR (*year))
    {
      days_months[2] += 1;
    }

  if (*day < 0 || *day > days_months[*month])
    {
      return ER_FAILED;
    }

  if (weeks < 0)
    {
      return ER_FAILED;
    }

  if (day_week < 0 || day_week > 6)
    {
      return ER_FAILED;
    }

  d = *day;
  m = *month;
  y = *year;
  for (i = 1; i <= weeks; i++)
    {
      d = d + 7;
      if (d > days_months[m])
    {
      d = d - days_months[m];
      m = m + 1;

      if (m > 12)
        {
          m = 1;
          y = y + 1;
          if ((y % 400 == 0) || (y % 100 != 0 && y % 4 == 0))
        {
          days_months[2] = 29;
        }
          else
        {
          days_months[2] = 28;
        }
        }
    }
    }

  d = d + day_week;
  if (d > days_months[m])
    {
      d = d - days_months[m];
      m = m + 1;
      if (m > 12)
    {
      m = 1;
      y = y + 1;
    }
    }

  if (y < 0 || y > 9999)
    {
      return ER_FAILED;
    }

  *day = d;
  *month = m;
  *year = y;

  return NO_ERROR;
}