blob: 926cde2401f30983fa6b875aa2450a06fcb74d1b [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
*****************************************************************************
*
* File: exp_datetime.C
* Description: Datetime Type
*
*
* Created: 8/20/96
* Language: C++
*
*
*
*
*****************************************************************************
*/
#include "Platform.h"
#include "exp_stdh.h"
#include "SQLTypeDefs.h"
#include "exp_datetime.h"
#include "exp_interval.h"
#include "exp_clause_derived.h"
#include "NAAssert.h"
#include "exp_bignum.h"
#undef DllImport
#define DllImport __declspec ( dllimport )
#include "rosetta/rosgen.h"
#define ptimez_h_juliantimestamp
#define ptimez_h_including_section
#include "guardian/ptimez.h"
#ifdef ptimez_h_juliantimestamp
Section missing, generate compiler error
#endif
#define ptimez_h_converttimestamp
#define ptimez_h_including_section
#include "guardian/ptimez.h"
#ifdef ptimez_h_converttimestamp
Section missing, generate compiler error
#endif
#define ptimez_h_interprettimestamp
#define ptimez_h_including_section
#include "guardian/ptimez.h"
#ifdef ptimez_h_interprettimestamp
Section missing, generate compiler error
#endif
#define ptimez_h_computetimestamp
#define ptimez_h_including_section
#include "guardian/ptimez.h"
#ifdef ptimez_h_computetimestamp
Section missing, generate compiler error
#endif
// Forword declaration of static helper function.
//
static short
copyDatetimeFields(rec_datetime_field startField,
rec_datetime_field endField,
short srcFractPrec,
short dstFractPrec,
char *srcData,
char *dstData,
Lng32 dstLen,
NABoolean *roundedDownFlag);
// Helper function to format extra error message text and report error
// srcData is not null terminated, so need a buffer copy to build a C-style string
//
static void
raiseDateConvErrorWithSrcData(int srcLen, ComDiagsArea** diagsArea, char *srcData, CollHeap *heap)
{
char errstr[MAX_OFFENDING_SOURCE_DATA_DISPLAY_LEN];
str_pad(errstr, sizeof(errstr), 0 );
if(srcLen > MAX_OFFENDING_SOURCE_DATA_DISPLAY_LEN -1 )
srcLen = MAX_OFFENDING_SOURCE_DATA_DISPLAY_LEN -1;
str_cpy_all(errstr, srcData, srcLen);
ExRaiseSqlError(heap, diagsArea, EXE_CONVERT_DATETIME_ERROR,NULL,NULL,NULL,NULL,errstr);
}
// Helper function to format extra error message text and report error
//
static void
raiseDateConvErrorWithSrcDataNumeric(ComDiagsArea** diagsArea, long srcData, CollHeap *heap)
{
char errstr[MAX_OFFENDING_SOURCE_DATA_DISPLAY_LEN];
str_pad(errstr, sizeof(errstr), 0 );
str_sprintf(errstr,"%ld",srcData);
ExRaiseSqlError(heap, diagsArea, EXE_CONVERT_DATETIME_ERROR,NULL,NULL,NULL,NULL,errstr);
}
//////////////////////////////////////////////
// Defined in exp_datetime.h
//
// struct DatetimeFormatInfo
// {
// Lng32 format;
// const char * str;
// Lng32 minLen;
// Lng32 maxLen
// };
//////////////////////////////////////////////
const ExpDatetime::DatetimeFormatInfo ExpDatetime::datetimeFormat[] =
{
{ExpDatetime::DATETIME_FORMAT_DEFAULT, "YYYY-MM-DD", 10, 10},
{ExpDatetime::DATETIME_FORMAT_USA, "MM/DD/YYYY", 10, 10},
{ExpDatetime::DATETIME_FORMAT_EUROPEAN, "DD.MM.YYYY", 10, 10},
{ExpDatetime::DATETIME_FORMAT_DEFAULT2, "YYYY-MM", 7, 7},
{ExpDatetime::DATETIME_FORMAT_USA2, "MM/DD/YYYY", 10, 10},
{ExpDatetime::DATETIME_FORMAT_USA3, "YYYY/MM/DD", 10, 10},
{ExpDatetime::DATETIME_FORMAT_USA4, "YYYYMMDD", 8, 8},
{ExpDatetime::DATETIME_FORMAT_USA5, "YY/MM/DD", 8, 8},
{ExpDatetime::DATETIME_FORMAT_USA6, "MM/DD/YY", 8, 8},
{ExpDatetime::DATETIME_FORMAT_USA7, "MM-DD-YYYY", 10, 10},
{ExpDatetime::DATETIME_FORMAT_USA8, "YYYYMM", 6, 6},
{ExpDatetime::DATETIME_FORMAT_EUROPEAN2, "DD-MM-YYYY", 10, 10},
{ExpDatetime::DATETIME_FORMAT_EUROPEAN3, "DD-MON-YYYY", 11, 11},
{ExpDatetime::DATETIME_FORMAT_EUROPEAN4, "DDMONYYYY", 9, 9},
{ExpDatetime::DATETIME_FORMAT_TS4, "HH24:MI:SS", 8, 8},
{ExpDatetime::DATETIME_FORMAT_TS1, "YYYYMMDDHH24MISS", 14, 14},
{ExpDatetime::DATETIME_FORMAT_TS2, "DD.MM.YYYY:HH24.MI.SS", 19, 19},
{ExpDatetime::DATETIME_FORMAT_TS3, "YYYY-MM-DD HH24:MI:SS", 19, 19},
{ExpDatetime::DATETIME_FORMAT_TS5, "YYYYMMDD:HH24:MI:SS", 17, 17},
{ExpDatetime::DATETIME_FORMAT_TS6, "MMDDYYYY HH24:MI:SS", 17, 17},
{ExpDatetime::DATETIME_FORMAT_TS7, "MM/DD/YYYY HH24:MI:SS", 19, 19},
{ExpDatetime::DATETIME_FORMAT_TS8, "DD-MON-YYYY HH:MI:SS", 20, 20},
{ExpDatetime::DATETIME_FORMAT_TS9, "MONTH DD, YYYY, HH:MI", 19, 25},
{ExpDatetime::DATETIME_FORMAT_TS10, "DD.MM.YYYY HH24.MI.SS", 19, 19},
{ExpDatetime::DATETIME_FORMAT_TS11, "YYYY/MM/DD HH24:MI:SS", 19, 19},
{ExpDatetime::DATETIME_FORMAT_NUM1, "99:99:99:99", 11, 11},
{ExpDatetime::DATETIME_FORMAT_NUM2, "-99:99:99:99", 12, 12},
{ExpDatetime::DATETIME_FORMAT_EXTRA_HH, "HH", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_HH12,"HH12", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_HH24,"HH24", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_MI, "MI", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_SS, "SS", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_YYYY,"YYYY", 4, 4},
{ExpDatetime::DATETIME_FORMAT_EXTRA_YYY, "YYY", 3, 3},
{ExpDatetime::DATETIME_FORMAT_EXTRA_YY, "YY", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_Y, "Y", 1, 1},
{ExpDatetime::DATETIME_FORMAT_EXTRA_MON, "MON", 3, 3},
{ExpDatetime::DATETIME_FORMAT_EXTRA_MM, "MM", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_DY, "DY", 3, 3},
{ExpDatetime::DATETIME_FORMAT_EXTRA_DAY, "DAY", 6, 9},
{ExpDatetime::DATETIME_FORMAT_EXTRA_CC, "CC", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_D, "D", 1, 1},
{ExpDatetime::DATETIME_FORMAT_EXTRA_DD, "DD", 2, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_DDD, "DDD", 1, 3},
{ExpDatetime::DATETIME_FORMAT_EXTRA_W, "W", 1, 1},
{ExpDatetime::DATETIME_FORMAT_EXTRA_WW, "WW", 1, 2},
{ExpDatetime::DATETIME_FORMAT_EXTRA_J, "J", 7, 7},
{ExpDatetime::DATETIME_FORMAT_EXTRA_Q, "Q", 1, 1},
// formats that are replaced by one of the other formats at bind time
{ExpDatetime::DATETIME_FORMAT_UNSPECIFIED, "UNSPECIFIED", 11, 11}
};
UInt32 Date2Julian(int y, int m ,int d)
{
int myjulian = 0;
int mycentury = 0;
if ( m <= 2)
{
m = m+13;
y = y+4799;
}
else
{
m = m+1;
y = y+4800;
}
mycentury = y / 100;
myjulian = y * 365 - 32167;
myjulian += y/4 - mycentury + mycentury / 4;
myjulian += 7834 * m / 256 + d;
return myjulian;
}
ExpDatetime::ExpDatetime()
{
setClassID(SimpleTypeID);
}
ExpDatetime::~ExpDatetime()
{
}
Attributes * ExpDatetime::newCopy()
{
ExpDatetime * new_copy = new ExpDatetime();
*new_copy = *this;
return new_copy;
};
Attributes * ExpDatetime::newCopy(CollHeap * heap)
{
ExpDatetime * new_copy = new(heap) ExpDatetime();
*new_copy = *this;
return new_copy;
};
void ExpDatetime::copyAttrs(Attributes *source_) // copy source attrs to this.
{
*this = *((ExpDatetime *)source_);
return;
};
ExpDatetime *
ExpDatetime::castToExpDatetime()
{
return this;
}
Int64 ExpDatetime::getTotalDays(short year, short month, short day)
{
//
// Return the number of days since 01/01/0001.
//
static const short daysBeforeMonth[] = {
/* Jan */ 0,
/* Feb */ 31,
/* Mar */ 31 + 28,
/* Apr */ 31 + 28 + 31,
/* May */ 31 + 28 + 31 + 30,
/* Jun */ 31 + 28 + 31 + 30 + 31,
/* Jul */ 31 + 28 + 31 + 30 + 31 + 30,
/* Aug */ 31 + 28 + 31 + 30 + 31 + 30 + 31,
/* Sep */ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
/* Oct */ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
/* Nov */ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
/* Dec */ 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
};
Int64 totalDays = ((year - 1) * 365) + daysBeforeMonth[month - 1] + (day - 1);
//
// If the month is January or February, don't include the current year when
// adjusting for leap years.
//
if (month <= 2)
year--;
totalDays += (year / 4) - (year / 100) + (year / 400);
return totalDays;
}
// getDatetimeFields() ===============================================
// This static method returns the start and end fields based on the
// given REC_DATETIME_CODE value. This method should be the only
// place in the expression evaluator which deals with the
// REC_DATETIME_CODE enumeration values. All other uses should call
// this method and be based on the start and end fields.
//
// This method has been modified as part of the MP Datetime
// Compatibility project. It has been extended to handle the
// non-standard SQL/MP datetime types.
//
short ExpDatetime::getDatetimeFields(Lng32 datetimeCode,
rec_datetime_field &startField,
rec_datetime_field &endField)
{
switch (datetimeCode) {
case REC_DTCODE_DATE:
case REC_DTCODE_TIMESTAMP:
case REC_DTCODE_YEAR:
case REC_DTCODE_YEAR_MONTH:
case REC_DTCODE_YEAR_DAY:
case REC_DTCODE_YEAR_HOUR:
case REC_DTCODE_YEAR_MINUTE:
case REC_DTCODE_YEAR_SECOND:
startField = REC_DATE_YEAR;
break;
case REC_DTCODE_MONTH:
case REC_DTCODE_MONTH_DAY:
case REC_DTCODE_MONTH_HOUR:
case REC_DTCODE_MONTH_MINUTE:
case REC_DTCODE_MONTH_SECOND:
startField = REC_DATE_MONTH;
break;
case REC_DTCODE_DAY:
case REC_DTCODE_DAY_HOUR:
case REC_DTCODE_DAY_MINUTE:
case REC_DTCODE_DAY_SECOND:
startField = REC_DATE_DAY;
break;
case REC_DTCODE_TIME:
case REC_DTCODE_HOUR:
case REC_DTCODE_HOUR_MINUTE:
case REC_DTCODE_HOUR_SECOND:
startField = REC_DATE_HOUR;
break;
case REC_DTCODE_MINUTE:
case REC_DTCODE_MINUTE_SECOND:
startField = REC_DATE_MINUTE;
break;
case REC_DTCODE_SECOND:
startField = REC_DATE_SECOND;
break;
default:
return -1;
}
switch (datetimeCode) {
case REC_DTCODE_YEAR:
endField = REC_DATE_YEAR;
break;
case REC_DTCODE_YEAR_MONTH:
case REC_DTCODE_MONTH:
endField = REC_DATE_MONTH;
break;
case REC_DTCODE_DATE:
case REC_DTCODE_YEAR_DAY:
case REC_DTCODE_MONTH_DAY:
case REC_DTCODE_DAY:
endField = REC_DATE_DAY;
break;
case REC_DTCODE_YEAR_HOUR:
case REC_DTCODE_MONTH_HOUR:
case REC_DTCODE_DAY_HOUR:
case REC_DTCODE_HOUR:
endField = REC_DATE_HOUR;
break;
case REC_DTCODE_YEAR_MINUTE:
case REC_DTCODE_MONTH_MINUTE:
case REC_DTCODE_DAY_MINUTE:
case REC_DTCODE_HOUR_MINUTE:
case REC_DTCODE_MINUTE:
endField = REC_DATE_MINUTE;
break;
case REC_DTCODE_TIMESTAMP:
case REC_DTCODE_YEAR_SECOND:
case REC_DTCODE_MONTH_SECOND:
case REC_DTCODE_DAY_SECOND:
case REC_DTCODE_TIME:
case REC_DTCODE_HOUR_SECOND:
case REC_DTCODE_MINUTE_SECOND:
case REC_DTCODE_SECOND:
endField = REC_DATE_SECOND;
break;
default:
return -1;
}
return 0;
}
static const Lng32 powersOfTen[] = {1, 10 ,100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000};
void ExpDatetime::convertDatetimeToInterval
( rec_datetime_field datetimeStartField
, rec_datetime_field datetimeEndField
, short fractionPrecision
, rec_datetime_field intervalEndField
, char *datetimeOpData
, Int64 &interval
, char * intervalBignum
, NABoolean &isBignum
) const
{
short rc = 0;
isBignum = FALSE;
interval = 0;
short year;
char month;
char day;
for (Int32 field = datetimeStartField; field <= intervalEndField; field++) {
switch (field) {
case REC_DATE_YEAR:
str_cpy_all((char *) &year, datetimeOpData, sizeof(year));
datetimeOpData += sizeof(year);
interval = year - 1;
break;
case REC_DATE_MONTH:
str_cpy_all((char *) &month, datetimeOpData, sizeof(month));
datetimeOpData += sizeof(month);
interval = (interval * 12) + (month - 1);
break;
case REC_DATE_DAY:
str_cpy_all((char *) &day, datetimeOpData, sizeof(day));
datetimeOpData += sizeof(day);
interval = getTotalDays(year, month, day);
break;
case REC_DATE_HOUR:
interval *= 24;
if (field <= datetimeEndField) {
char hour;
str_cpy_all((char *) &hour, datetimeOpData, sizeof(hour));
datetimeOpData += sizeof(hour);
interval = interval + hour;
}
break;
case REC_DATE_MINUTE:
interval *= 60;
if (field <= datetimeEndField) {
char minute;
str_cpy_all((char *) &minute, datetimeOpData, sizeof(minute));
datetimeOpData += sizeof(minute);
interval = interval + minute;
}
break;
case REC_DATE_SECOND:
interval *= 60;
if (field <= datetimeEndField) {
char second;
str_cpy_all((char *) &second, datetimeOpData, sizeof(second));
datetimeOpData += sizeof(second);
interval = interval + second;
if (fractionPrecision > 0) {
Lng32 fraction;
Int64 fraction64;
str_cpy_all((char *) &fraction, datetimeOpData, sizeof(fraction));
Int64 multiplicator = powersOfTen[fractionPrecision];
if (fractionPrecision <= MAX_DATETIME_MICROS_FRACT_PREC)
{
interval *= multiplicator;
interval = interval + fraction;
}
else
{
// Int64 may run into an overflow if fract precision is > 6
// Use bignum computation to do:
// interval = interval * multiplicator
// interval = interval + fraction
SimpleType op1ST(REC_BIN64_SIGNED, sizeof(Int64), 0, 0,
ExpTupleDesc::SQLMX_FORMAT,
8, 0, 0, 0, Attributes::NO_DEFAULT, 0);
char *op_data[3];
char mulBignum[BigNum::BIGNUM_TEMP_LEN]; // 16 bytes bignum result length
op_data[0] = mulBignum; // result
op_data[1] = (char*) &interval;
op_data[2] = (char*) &multiplicator;
rc = EXP_FIXED_BIGN_OV_MUL(&op1ST, &op1ST, op_data);
BigNum op1BN(BigNum::BIGNUM_TEMP_LEN, BigNum::BIGNUM_TEMP_PRECISION, 0, 0);
char addBignum[BigNum::BIGNUM_TEMP_LEN];
fraction64 = fraction;
op_data[0] = addBignum;
op_data[1] = mulBignum;
op_data[2] = (char*)&fraction64;
rc = EXP_FIXED_BIGN_OV_ADD(&op1BN, &op1ST, op_data);
if (intervalBignum)
{
str_cpy_all(intervalBignum, addBignum, BigNum::BIGNUM_TEMP_LEN);
isBignum = TRUE;
}
}
}
}
break;
}
}
}
short ExpDatetime::getYearMonthDay(Int64 totalDays,
short &year,
char &month,
char &day)
{
const unsigned short daysPerYear = 365;
const unsigned short daysPer4Years = daysPerYear * 4 + 1;
const unsigned short daysPer100Years = daysPer4Years * 25 - 1;
const Int64 daysPer400Years = daysPer100Years * 4 + 1;
Int64 numLeapCenturies = totalDays / daysPer400Years;
totalDays -= numLeapCenturies * daysPer400Years;
Int64 numCenturies = totalDays / daysPer100Years;
//
// If the date is the last day of a leap century, e.g. 12/31/2000, then
// numCenturies will be 4 since the century had an extra day. Correct
// numCenturies to 3.
//
if (numCenturies > 3)
numCenturies = 3;
totalDays -= numCenturies * daysPer100Years;
Int64 numLeapYears = totalDays / daysPer4Years;
totalDays -= numLeapYears * daysPer4Years;
Int64 numYears = totalDays / daysPerYear;
//
// If the date is the last day of a leap year, e.g. 12/31/1996, numYears
// will be 4 since the year had an extra day. Correct numYears to 3.
//
if (numYears > 3)
numYears = 3;
totalDays -= numYears * daysPerYear;
numYears += (numLeapCenturies * 400) +
(numCenturies * 100) +
(numLeapYears * 4) +
1; /* the base year is 0001 */
if (numYears > 9999)
return -1;
year = (short) int64ToInt32(numYears);
static const short daysInMonth[] = {
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
for (month = 1; totalDays >= daysInMonth[month]; month++) {
if ((month == 2) && /* February */
((year % 4) == 0) &&
(((year % 100) != 0) || ((year % 400) == 0))) { /* leap year */
if (totalDays == 28) /* leap day */
break;
totalDays -= 29;
} else
totalDays -= daysInMonth[month];
}
day = (char) int64ToInt32(totalDays);
day++; /* each month begins at day 1 */
return 0;
}
short ExpDatetime::convertIntervalToDatetime(Int64 interval,
char * intervalBignum,
rec_datetime_field startField,
rec_datetime_field endField,
short fractionPrecision,
char *datetimeOpData) const
{
short rc = 0;
short year;
char month;
char day;
char hour;
char minute;
char second;
Lng32 fraction;
Int32 field = endField;
for (; field >= startField; field--) {
switch (field) {
case REC_DATE_SECOND:
if (fractionPrecision > 0) {
//Int64 multiplicator = powersOfTen[fractionPrecision];
Int64 divisor = powersOfTen[fractionPrecision];
Int64 dividend = 0;
if (fractionPrecision <= MAX_DATETIME_MICROS_FRACT_PREC)
{
Int64 dividend = interval;
interval = dividend / divisor;
dividend = dividend - (interval * divisor);
//
// Underflow is allowed for time types, so wrap around if necessary.
//
if (dividend < 0) {
dividend += divisor;
interval -= 1;
}
fraction = int64ToInt32(dividend);
}
else
{
SimpleType opST(REC_BIN64_SIGNED, sizeof(Int64), 0, 0,
ExpTupleDesc::SQLMX_FORMAT,
8, 0, 0, 0, Attributes::NO_DEFAULT, 0);
BigNum opBN(BigNum::BIGNUM_TEMP_LEN, BigNum::BIGNUM_TEMP_PRECISION, 0, 0);
char *op_data[2];
op_data[0] = intervalBignum;
op_data[1] = (char*) &divisor;
Int64 quotient = -1;
short ov;
dividend =
EXP_FIXED_BIGN_OV_MOD(&opBN, &opST, op_data, &ov, &quotient);
interval = quotient;
fraction = int64ToInt32(dividend);
}
}
//
// Underflow is allowed for time types, so wrap around if necessary.
// But be aware that the sign of the result of a modulus operation
// involving a negative operand is implementation-dependent according
// to the C++ reference manual. In this case, we prefer the result to
// be negative.
//
{
Int32 sec = (Int32) (interval % 60);
if ((interval < 0) && ( sec > 0))
sec = - sec;
interval /= 60;
if ( sec < 0) {
sec+= 60;
interval -= 1;
}
second = (char) sec;
break;
}
case REC_DATE_MINUTE:
//
// Underflow is allowed for time types, so wrap around if necessary.
// But be aware that the sign of the result of a modulus operation
// involving a negative operand is implementation-dependent according
// to the C++ reference manual. In this case, we prefer the result to
// be negative.
//
{
Int32 mins = (Int32) (interval % 60);
if ((interval < 0) && (mins > 0))
mins = - mins;
interval /= 60;
if (mins < 0) {
mins += 60;
interval -= 1;
}
minute = (char) mins;
break;
}
case REC_DATE_HOUR:
//
// Underflow is allowed for time types, so wrap around if necessary.
// But be aware that the sign of the result of a modulus operation
// involving a negative operand is implementation-dependent according
// to the C++ reference manual. In this case, we prefer the result to
// be negative.
//
{
Int32 hrs = (Int32) (interval % 24);
if ((interval < 0) && (hrs > 0))
hrs = - hrs;
interval /= 24;
if (hrs < 0) {
hrs += 24;
interval -= 1;
}
hour = (char) hrs;
break;
}
case REC_DATE_DAY:
if (getYearMonthDay(interval, year, month, day) != 0)
return -1;
break;
case REC_DATE_MONTH:
if (endField < REC_DATE_DAY) {
month = char ((interval % 12) + 1);
interval /= 12;
}
break;
case REC_DATE_YEAR:
if (endField < REC_DATE_DAY) {
interval += 1; /* the base year is 0001 */
if (interval > 9999)
return -1;
year = (short) int64ToInt32(interval);
}
break;
}
}
for (field = startField; field <= endField; field++) {
switch (field) {
case REC_DATE_YEAR:
str_cpy_all(datetimeOpData, (char *) &year, sizeof(year));
datetimeOpData += sizeof(year);
break;
case REC_DATE_MONTH:
str_cpy_all(datetimeOpData, (char *) &month, sizeof(month));
datetimeOpData += sizeof(month);
break;
case REC_DATE_DAY:
str_cpy_all(datetimeOpData, (char *) &day, sizeof(day));
datetimeOpData += sizeof(day);
break;
case REC_DATE_HOUR:
str_cpy_all(datetimeOpData, (char *) &hour, sizeof(hour));
datetimeOpData += sizeof(hour);
break;
case REC_DATE_MINUTE:
str_cpy_all(datetimeOpData, (char *) &minute, sizeof(minute));
datetimeOpData += sizeof(minute);
break;
case REC_DATE_SECOND:
str_cpy_all(datetimeOpData, (char *) &second, sizeof(second));
datetimeOpData += sizeof(second);
if (fractionPrecision > 0)
str_cpy_all(datetimeOpData, (char *) &fraction, sizeof(fraction));
break;
}
}
return 0;
}
short ExpDatetime::validateDate(rec_datetime_field startField,
rec_datetime_field endField,
char *datetimeOpData,
ExpDatetime *attr,
short intervalFlag,
NABoolean &LastDayPrevMonth)
{
// Initialize to a valid date. The year is set to 4 so that if all
// we have is a month and a day, 02/29 is valid as it should
// be. Month is set to 1, so that if all we have is a day, 31 is
// valid.
//
short year = 4;
char month = 1;
char day = 1;
short uselastdateflag = FALSE;
short uselastdateonerrflag = FALSE;
short ErrorOnInterval = FALSE;
for (Int32 field = startField; field <= endField; field++) {
switch (field) {
case REC_DATE_YEAR:
str_cpy_all((char *) &year, datetimeOpData, sizeof(year));
datetimeOpData += sizeof(year);
break;
case REC_DATE_MONTH:
month = *datetimeOpData;
datetimeOpData += sizeof(month);
break;
case REC_DATE_DAY:
day = *datetimeOpData;
datetimeOpData += sizeof(day);
break;
default:
// Only validating date fields. Ignore all others.
//
break;
}
}
// Round to the last day of the month.
if ((intervalFlag) && attr->getlastdaymonthflag())
uselastdateflag = TRUE;
// If ending day of resulting date is invalid, day will be rounded
// down to the last day of the month.
if ((intervalFlag) && attr->getlastdayonerrflag())
uselastdateonerrflag = TRUE;
// Check basic range of the fields of the date.
//
if (year > 9999 || year < 1 || month > 12 || month < 1 || day < 1)
return -1;
//
// If the days field is more than the number of days in the month, the date
// is invalid.
//
static const short daysInMonth[] = {
0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const short daysInMonthNonLeap[] = {
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
if (day > daysInMonth[month])
// Return error if we're not adjusting for intervals only validating
// the date. This is default behavior. If add_month or date_add
// functions are used, then intervalFlag would be set.
if ((!uselastdateonerrflag) && (!uselastdateflag))
return -1;
else
if (!intervalFlag)
return -1;
else
if (uselastdateonerrflag)
ErrorOnInterval = TRUE;
//
// If the month field is February and the year is not a leap year, the
// maximum number of days is 28.
//
if ((month == 2) && (day > 28) &&
(((year % 4) != 0) ||
(((year % 100) == 0) && ((year % 400) != 0))))
// Return error if we're not adjusting for interval dates, only validating
// dates. (The default behavior)
if ((!uselastdateonerrflag) && (!uselastdateflag))
return -1;
else
if (!intervalFlag)
return -1;
else
if (uselastdateonerrflag)
ErrorOnInterval = TRUE;
// Check to see whether the month value of the incoming date is the
// last day of the month, then set flag here. We're validating the
// the incoming 1st date, not the resulting adjusted date.
if (!intervalFlag)
{
// it's a non-leap year
if (((year % 4) != 0) ||
(((year % 100) == 0) && ((year % 400) != 0)))
if (day == daysInMonthNonLeap[month])
LastDayPrevMonth = TRUE;
else
LastDayPrevMonth = FALSE;
else
if (day == daysInMonth[month])
LastDayPrevMonth = TRUE;
else
LastDayPrevMonth = FALSE;
}
// Adjust resulting date only if one of the flags are set.
// If not set, then revert to previous behavior and don't adjust dates.
if ((uselastdateonerrflag) || (uselastdateflag))
if (intervalFlag)
{
if (uselastdateonerrflag) // adjust date only if resulting day is invalid
{
// Didn't get an error, so return and don't adjust the date
if (!ErrorOnInterval)
return 0;
}
else
if (uselastdateflag)
{
// If incoming adjusted day is not at the end of the month,
// don't adjust the resulting day to the end of the month
if (!LastDayPrevMonth)
{
// If the final date is in Feb.
// we may need to adjust, return if not Feb.
if ((month == 2) && (day > 28))
{}
else
return 0;
};
};
day = (char) daysInMonth[month];
if ((month == 2) && (day > 28) &&
(((year % 4) != 0) ||
(((year % 100) == 0) && ((year % 400) != 0))))
day = 28;
datetimeOpData -= sizeof(day);
*datetimeOpData = day;
} // if (intervalFlag)
return 0;
}
// returns 0 if valid time, -1 if not
// validates hour, minute, second
short ExpDatetime::validateTime(const char *datetimeOpData)
{
int hour = datetimeOpData[0];
if ((hour < 0) || (hour > 23))
return -1;
int minute = datetimeOpData[1];
if ((minute < 0) || (minute > 59))
return -1;
int second = datetimeOpData[2];
if ((second < 0) || (second > 59))
return -1;
return 0;
}
// compDatetimes() ===================================================
// This method compares two datetime values of the same subtype and
// returns a value indicating if the first value is less than (-1),
// equal to (0), or greater than (+1) the second value.
//
// This method has been modified as part of the MP Datetime
// Compatibility project. It has been made more generic so that it is
// based on the start and end fields of the datetime type.
// ===================================================================
short ExpDatetime::compDatetimes(char *datetimeOpData1,
char *datetimeOpData2)
{
rec_datetime_field startField;
rec_datetime_field endField;
// Get the start and end fields for this Datetime type.
//
getDatetimeFields(getPrecision(),
startField,
endField);
// Compare each field from highest significance (YEAR) to least
// significance (FRACTION). The format is: YYMDHMSFFFF
//
for (Int32 field = startField; field <= endField; field++) {
switch (field) {
case REC_DATE_YEAR:
{
short year1;
short year2;
// Don't know if the datetimeOpData{1,2} pointers point to
// aligned data, so use str_cpy_all.
//
str_cpy_all((char *) &year1, datetimeOpData1, sizeof(year1));
str_cpy_all((char *) &year2, datetimeOpData2, sizeof(year2));
if (year1 < year2)
return -1;
if (year1 > year2)
return 1;
datetimeOpData1 += sizeof(year1);
datetimeOpData2 += sizeof(year2);
break;
}
case REC_DATE_MONTH:
case REC_DATE_DAY:
case REC_DATE_HOUR:
case REC_DATE_MINUTE:
{
// 1 byte field comparisons
//
if (*datetimeOpData1 < *datetimeOpData2)
return -1;
if (*datetimeOpData1 > *datetimeOpData2)
return 1;
datetimeOpData1++;
datetimeOpData2++;
break;
}
case REC_DATE_SECOND:
{
// second comparison is also one byte, but has an optional
// trailing fractional precision
//
if (*datetimeOpData1 < *datetimeOpData2)
return -1;
if (*datetimeOpData1 > *datetimeOpData2)
return 1;
datetimeOpData1++;
datetimeOpData2++;
// check for fractional part
if (getScale() > 0)
{
Lng32 fraction1;
Lng32 fraction2;
// Don't know if the datetimeOpData{1,2} pointers point to
// aligned data, so use str_cpy_all.
//
str_cpy_all((char *) &fraction1, datetimeOpData1, sizeof(fraction1));
str_cpy_all((char *) &fraction2, datetimeOpData2, sizeof(fraction2));
if (fraction1 < fraction2)
return -1;
if (fraction1 > fraction2)
return 1;
}
break;
}
default:
// Should never get here.
//
assert(0);
break;
} // switch ...
} // for ...
// All fields compare equal. Therefore, the values are equal.
//
return 0;
}
// arithDatetimeInterval() ===========================================
// This method is used to add or subtract an interval value to/from a
// datetime value. To perform the arithmetic operation, the following
// steps will be taken:
//
// - The datetime value will have been extended to contain a YEAR
// field if needed (see BiArith::preCodeGen()). The value will need
// to be extended if it contains a DAY field. This is necessary since
// with the introduction of non-standard SQL/MP datetime types, it is
// possible to have a datetime value which has a DAY field but not a
// YEAR or not a MONTH field. In this situation, it is not possible
// to define a meaningful way to do the operation. Does the DAY field
// wrap at 30, 31, 28, or 29. So to make this operation meaningful,
// the value is extended to the current timestamp.
//
// - Ensure that this is a valid datetime value (use validateDate).
// For example, the datetime value "DATETIME 02-29 MONTH TO DAY"
// extended to the current timestamp in a non-leap year would result
// in an invalid timestamp. Presumably, this check should have been
// done before calling this method.
//
// - Convert the given datetime value to an interval scaled to
// match the given interval value (use convertDatetimeToInterval)
//
// - Perform the arithmetic operation on the two interval values (they
// are integer values).
//
// - Convert the resulting value back to a datetime value (use
// convertIntervalToDatetime).
//
// - If necessary, validate the resulting datetime value. It will be
// necessary if the interval being added/subtract to the datetime
// value is in units of months or years and if the original datetime
// value contained a day field.
//
// - Extract the desired fields from the resulting datetime value as
// the final result (use extractDatetime). The desired fields are the
// range of fields of the original datetime value.
//
// This method has been modified as part of the MP Datetime
// Compatibility project. It has been modified to handle non-standard
// datetime types.
// ====================================================================
//
short
ExpDatetime::arithDatetimeInterval(arithOps operation,
ExpDatetime *datetimeOpType,
Attributes *intervalOpType,
char *datetimeOpData,
char *intervalOpData,
char *resultData,
CollHeap *heap,
ComDiagsArea** diagsArea)
{
short rc = 0;
if (operation != DATETIME_ADD &&
operation != DATETIME_SUB) {
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
rec_datetime_field datetimeStartField;
rec_datetime_field datetimeEndField;
NABoolean LastDayPrevMonth = FALSE;
// Get the start and end fields of the given Datetime type.
//
if (getDatetimeFields(datetimeOpType->getPrecision(),
datetimeStartField,
datetimeEndField) != 0) {
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
rec_datetime_field intervalEndField;
// Get the end field of the given Interval type.
//
if (ExpInterval::getIntervalEndField(intervalOpType->getDatatype(),
intervalEndField) != 0) {
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
// Make sure that the given datetime value has enough fields to
// perform this operation. If there is a DAY field, there also has
// to be as YEAR field.
//
if (datetimeStartField <= REC_DATE_DAY &&
datetimeEndField >= REC_DATE_DAY &&
datetimeStartField > REC_DATE_YEAR) {
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
// Copy the given datetime value to a local buffer.
// This will contain the result of the operation, but may
// have more fields that the destination.
//
char dateTimeValue[MAX_DATETIME_SIZE];
// Copy given datetime value to a local buffer.
//
if (copyDatetimeFields(datetimeStartField,
datetimeEndField,
datetimeOpType->getScale(),
datetimeOpType->getScale(),
datetimeOpData,
dateTimeValue,
MAX_DATETIME_SIZE,
NULL) != 0) {
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
// Ensure that the given value is a valid datetime value. For
// example, the datetime value "DATETIME 02-29 MONTH TO DAY"
// extended with the current timestamp in a non-leap year would
// result in an invalid date. (This check presumably is also done by
// the code which performed the extension).
//
if ((datetimeStartField == REC_DATE_YEAR) &&
(datetimeEndField >= REC_DATE_DAY) &&
(validateDate(REC_DATE_YEAR, REC_DATE_DAY,
dateTimeValue, NULL, FALSE,
LastDayPrevMonth) != 0)) {
ExRaiseSqlError(heap, diagsArea, EXE_DATETIME_FIELD_OVERFLOW);
return -1;
}
// Convert the datetime value to an interval with the same precision
// (endField) as the Interval operand. We want to make sure we
// add/subtract MONTHs to MONTHs, etc.
//
Int64 value = -1;
char intervalBignum[BigNum::BIGNUM_TEMP_LEN];
char resultBignum[BigNum::BIGNUM_TEMP_LEN];
NABoolean isBignum = FALSE;
convertDatetimeToInterval(datetimeStartField,
datetimeEndField,
datetimeOpType->getScale(),
intervalEndField,
dateTimeValue,
value,
intervalBignum,
isBignum);
// Perform the arithmetic operation.
//
Int64 interval64 = 0;
switch (intervalOpType->getLength()) {
case SQL_SMALL_SIZE: {
short interval;
str_cpy_all((char *) &interval, intervalOpData, sizeof(interval));
interval64 = interval;
break;
}
case SQL_INT_SIZE: {
Lng32 interval;
str_cpy_all((char *) &interval, intervalOpData, sizeof(interval));
interval64 = interval;
break;
}
case SQL_LARGE_SIZE: {
Int64 interval;
str_cpy_all((char *) &interval, intervalOpData, sizeof(interval));
interval64 = interval;
break;
}
default:
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
if (NOT isBignum) { // result is not a bignum
value += ((operation == DATETIME_ADD) ? interval64 : -interval64);
} else {
// result is a bignum
char *op_data[3];
BigNum op1BN(BigNum::BIGNUM_TEMP_LEN, BigNum::BIGNUM_TEMP_PRECISION, 0, 0);
SimpleType intST(REC_BIN64_SIGNED, sizeof(Int64), 0, 0,
ExpTupleDesc::SQLMX_FORMAT,
8, 0, 0, 0, Attributes::NO_DEFAULT, 0);
op_data[0] = resultBignum;
op_data[1] = intervalBignum;
op_data[2] = (char*)&interval64;
if (operation == DATETIME_ADD) {
rc = EXP_FIXED_BIGN_OV_ADD(&op1BN, &intST, op_data);
} else {
rc = EXP_FIXED_BIGN_OV_SUB(&op1BN, &intST, op_data);
}
}
// Underflow is ok for time only datetime types. Arithmetic on the
// hour field is computed modulo 24. For datetime types containing
// a date portion, underflow is an error.
//
if ((NOT isBignum) && (value < 0) &&
(datetimeStartField < REC_DATE_HOUR)) {
ExRaiseSqlError(heap, diagsArea, EXE_DATETIME_FIELD_OVERFLOW);
return -1;
}
// Convert result back to datetime. Note that this is overlaying the
// local copy of the datetime value.
//
if (convertIntervalToDatetime(value,
(isBignum ? resultBignum : NULL),
datetimeStartField,
intervalEndField,
datetimeOpType->getScale(),
dateTimeValue) != 0) {
ExRaiseSqlError(heap, diagsArea, EXE_DATETIME_FIELD_OVERFLOW);
return -1;
}
// If we are adding/subtracting in units of MONTHS or YEARS, and the
// original datetime value contains a DAY field, then the result
// could be invalid. For example:
//
// DATETIME '1-31' MONTH TO DAY + INTERVAL '1' MONTH -> ERROR
//
// DATETIME '1' MONTH + INTERVAL '1' MONTH -> DATETIME '2' MONTH
// (even if the current date is 2000-1-31)
//
if ((intervalEndField <= REC_DATE_MONTH) &&
(datetimeStartField <= REC_DATE_DAY) &&
(datetimeEndField >= REC_DATE_DAY) &&
(validateDate(datetimeStartField,
REC_DATE_DAY,
dateTimeValue, this, TRUE, LastDayPrevMonth) != 0)) {
ExRaiseSqlError(heap, diagsArea, EXE_DATETIME_FIELD_OVERFLOW);
return -1;
}
// Extract the result from the datetime value. The result fields
// could be different from the fields of the given datetime value.
//
if (extractDatetime(datetimeStartField,
datetimeEndField,
datetimeOpType->getScale(),
dateTimeValue,
resultData) != 0) {
ExRaiseSqlError(heap, diagsArea, EXE_DATETIME_FIELD_OVERFLOW);
return -1;
}
return 0;
}
short ExpDatetime::subDatetimeDatetime(Attributes *datetimeOpType,
Attributes *intervalOpType,
char *datetimeOpData1,
char *datetimeOpData2,
char *resultData,
CollHeap *heap,
ComDiagsArea** diagsArea) const
{
short rc = 0;
rec_datetime_field datetimeStartField;
rec_datetime_field datetimeEndField;
if (getDatetimeFields(datetimeOpType->getPrecision(),
datetimeStartField,
datetimeEndField) != 0) {
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
rec_datetime_field intervalEndField;
if (DFS2REC::isInterval(intervalOpType->getDatatype()))
{
if (ExpInterval::getIntervalEndField(intervalOpType->getDatatype(),
intervalEndField) != 0) {
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
}
else if ((DFS2REC::isBinary(intervalOpType->getDatatype())) &&
(datetimeEndField == REC_DATE_DAY))
{
// special modeSpecial4 case.
// DATE - DATE with result as NUMBER.
intervalEndField = REC_DATE_DAY;
}
else
{
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
Int64 value1;
char intervalBignum1[BigNum::BIGNUM_TEMP_LEN];
NABoolean isBignum1 = FALSE;
NABoolean isBignum2 = FALSE;
convertDatetimeToInterval(datetimeStartField,
datetimeEndField,
datetimeOpType->getScale(),
intervalEndField,
datetimeOpData1,
value1,
intervalBignum1,
isBignum1);
Int64 value2;
char intervalBignum2[BigNum::BIGNUM_TEMP_LEN];
convertDatetimeToInterval(datetimeStartField,
datetimeEndField,
datetimeOpType->getScale(),
intervalEndField,
datetimeOpData2,
value2,
intervalBignum2,
isBignum2);
Int64 result = 0;
if ((NOT isBignum1) && (NOT isBignum2)) // neither is bignum
result = value1 - value2;
else
{
BigNum opBN(BigNum::BIGNUM_TEMP_LEN, BigNum::BIGNUM_TEMP_PRECISION, 0, 0);
SimpleType opST(REC_BIN64_SIGNED, sizeof(Int64), 0, 0,
ExpTupleDesc::SQLMX_FORMAT,
8, 0, 0, 0, Attributes::NO_DEFAULT, 0);
char *op_data[3];
char resultBN[BigNum::BIGNUM_TEMP_LEN];
op_data[0] = resultBN;
op_data[1] = (isBignum1 ? intervalBignum1 : (char*)&value1);
op_data[2] = (isBignum2 ? intervalBignum2 : (char*)&value2);
rc = EXP_FIXED_BIGN_OV_SUB((isBignum1 ? (Attributes*)&opBN : (Attributes*)&opST),
(isBignum2 ? (Attributes*)&opBN : (Attributes*)&opST),
op_data);
if (rc)
{
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
rc = convDoIt(op_data[0], BigNum::BIGNUM_TEMP_LEN, REC_NUM_BIG_SIGNED, BigNum::BIGNUM_TEMP_PRECISION, 0,
(char*)&result, 8, REC_BIN64_SIGNED, 0, 0,
NULL, 0, NULL, NULL);
if (rc)
{
// convert interval value to ascii format
char invalidVal[BigNum::BIGNUM_TEMP_PRECISION+1];
Int32 len = BigNum::BIGNUM_TEMP_PRECISION;
memset(invalidVal, ' ', len);
convDoIt(op_data[0], BigNum::BIGNUM_TEMP_LEN, REC_NUM_BIG_SIGNED, BigNum::BIGNUM_TEMP_PRECISION, 0,
invalidVal, BigNum::BIGNUM_TEMP_PRECISION, REC_BYTE_F_ASCII, 0, 0,
NULL, 0, NULL, NULL);
len--;
while (invalidVal[len] == ' ')
len--;
len++;
invalidVal[len] = 0;
ExRaiseSqlError(heap, diagsArea, EXE_INVALID_INTERVAL_RESULT,
NULL, NULL, NULL, NULL,
invalidVal);
return -1;
}
}
//
// Scale the result to the interval qualifier's fractional precision.
//
if (intervalEndField == REC_DATE_SECOND) {
short fpDiff = intervalOpType->getScale() - datetimeOpType->getScale();
if (fpDiff > 0) {
do {
result *= 10;
} while (--fpDiff > 0);
} else {
while (fpDiff < 0) {
result /= 10;
fpDiff++;
}
}
}
switch (intervalOpType->getLength()) {
case SQL_SMALL_SIZE: {
short interval = (short) int64ToInt32(result);
str_cpy_all(resultData, (char*) &interval, sizeof(interval));
break;
}
case SQL_INT_SIZE: {
Lng32 interval = int64ToInt32(result);
str_cpy_all(resultData, (char*) &interval, sizeof(interval));
break;
}
case SQL_LARGE_SIZE:
str_cpy_all(resultData, (char*) &result, sizeof(result));
break;
default:
ExRaiseSqlError(heap, diagsArea, EXE_INTERNAL_ERROR);
return -1;
}
return 0;
};
// scaleFraction() ===================================================
// This static helper function to the ExpDatetime class is used to
// scale the fraction value from one precision to another. If the
// scaling results in loss of data, the roundedDownFlag, if provided,
// is set to TRUE.
//
// This function was added as part of the MP Datetime Compatibility
// project.
// ===================================================================
//
static Lng32
scaleFraction(Int32 srcFractPrec,
Lng32 srcFraction,
Int32 dstFractPrec,
NABoolean *roundedDownFlag = NULL)
{
// The roundedDownFlag indicates if any data is lost when scaling
// the fraction field of the datetime value.
//
if (roundedDownFlag)
*roundedDownFlag = FALSE; // assume success
Lng32 fraction = 0;
// If there is a fraction value in the destination and there is a
// fraction value in the source, scale the fraction to the
// destination precision.
//
if (dstFractPrec >= 0 && srcFractPrec > 0) {
fraction = srcFraction;
if (srcFractPrec < dstFractPrec) {
Lng32 multiplier = powersOfTen[dstFractPrec - srcFractPrec];
fraction *= multiplier;
} else if (srcFractPrec > dstFractPrec) {
Lng32 divisor = powersOfTen[srcFractPrec - dstFractPrec];
fraction /= divisor;
if (roundedDownFlag && ((fraction * divisor) < srcFraction)) {
*roundedDownFlag = TRUE;
}
}
// If the two precisions are equal, no scaling is required.
//
}
return fraction;
}
// currentTimeStamp() ================================================
// This static method populates the given buffer (char *) with the
// value of the current timestamp in the format of an ExpDatetime
// value (i.e., 2-1-1-1-1-1-4 for YEAR through FRACTION). This method
// can be used anywhere the current timestamp is needed in this
// form. For instance ex_function_current::eval() in
// exp/exp_function.cpp.
//
// The result is returned in the buffer pointed to by the parameter
// 'resultData'. This buffer must be allocated by the caller and it
// must be large enough to hold a complete timestamp (11 bytes).
//
// This method was added as part of the MP Datetime Compatibility
// project. The code for the method came from
// ex_function_current::eval() in exp_function.cpp
// ===================================================================
short
ExpDatetime::currentTimeStamp(char *dstData,
rec_datetime_field startField,
rec_datetime_field endField,
short fractPrec)
{
// The Timestamp fields decared to have the same size as the
// corresponding field of the ExpDatetime format (YYMDHMSFFFF).
//
short year;
char month;
char day;
char hour;
char minute;
char second;
Lng32 fraction;
// Get the value of the current timestamp.
//
Int64 juliantimestamp = CONVERTTIMESTAMP(JULIANTIMESTAMP(0,0,0,-1),0,-1,0);
// Convert the timestamp into an array of shorts with each element
// containing the value of one field of the timestamp.
//
short timestamp[8];
INTERPRETTIMESTAMP(juliantimestamp, timestamp);
// Extract the fields into local variables which correspond in size
// to the fields of the ExpDatetime format (YYMDHMSFFFF).
//
year = timestamp[0];
month = (char) timestamp[1];
day = (char) timestamp[2];
hour = (char) timestamp[3];
minute = (char) timestamp[4];
second = (char) timestamp[5];
fraction = timestamp[6] * 1000 + timestamp[7];
// Copy all the fields to the result value.
//
for (Int32 field = startField; field <= endField; field++) {
switch (field) {
case REC_DATE_YEAR:
str_cpy_all(dstData, (char *) &year, sizeof(year));
dstData += sizeof(year);
break;
case REC_DATE_MONTH:
*dstData++ = month;
break;
case REC_DATE_DAY:
*dstData++ = day;
break;
case REC_DATE_HOUR:
*dstData++ = hour;
break;
case REC_DATE_MINUTE:
*dstData++ = minute;
break;
case REC_DATE_SECOND:
*dstData++ = second;
// If there is a fractional precision in the destination,
// copy and scale it from the source.
//
if (fractPrec > 0) {
fraction = scaleFraction(MAX_DATETIME_FRACT_PREC, fraction,fractPrec);
str_cpy_all(dstData, (char *) &fraction, sizeof(fraction));
}
break;
default:
return -1;
}
}
return 0;
}
// sizeofDatetimeFields() ==============================================
// This static helper function for the ExpDatetime class is used to
// determine the size in number of bytes, of a range of datetime fields.
//
// This function was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
static Lng32
sizeofDatetimeFields(rec_datetime_field startField,
rec_datetime_field endField,
short fractPrec)
{
Lng32 sizeInBytes = 0;
if (startField <= endField) {
// All fields have at least one byte.
//
sizeInBytes = endField - startField + 1;
// The YEAR field has two bytes, so add an extra.
//
if(startField == REC_DATE_YEAR) {
sizeInBytes++;
}
// The FRACTION field has 4 bytes, so if it is present add 4.
//
if(endField == REC_DATE_SECOND && fractPrec > 0) {
sizeInBytes += sizeof(Lng32);
}
}
return sizeInBytes;
}
// copyDatetimeFields() ==============================================
// This static helper function for the ExpDatetime class is used to
// copy a set of fields from one datetime value to another. If
// present, the fraction value is scaled to match the destination
// before coping.
//
// This function was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
static short
copyDatetimeFields(rec_datetime_field startField,
rec_datetime_field endField,
short srcFractPrec,
short dstFractPrec,
char *srcData,
char *dstData,
Lng32 dstLen,
NABoolean *roundedDownFlag)
{
// get the size of all the fields, excluding the fraction field if
// present.
//
Lng32 size = sizeofDatetimeFields(startField, endField, 0);
// Copy all the fields, excluding the fraction field.
//
str_cpy_all(dstData, srcData, size);
dstData += size;
srcData += size;
// If there is a fractional precision in the destination,
// copy and scale the fraction from the source.
//
if (endField == REC_DATE_SECOND && dstFractPrec >= 0) {
Lng32 fraction = 0;
// If there is a fraction precision in the source datetime
// value, scale it to the precision of the destination
// datetime If no fractional precision is given, use the
// default value of 0.
//
if (srcFractPrec > 0) {
str_cpy_all((char *)&fraction, srcData, sizeof(fraction));
fraction = scaleFraction(srcFractPrec,
fraction,
dstFractPrec,
roundedDownFlag);
}
// if destination has space for fraction, copy it.
if ((dstLen > 0) && (dstLen >= (size + sizeof(fraction))))
str_cpy_all(dstData, (char *) &fraction, sizeof(fraction));
}
return 0;
}
// minimumTimeStamp() ================================================
// This static helper function for the ExpDatetime class is used to
// construct a minimum datetime value for the given start and end
// fields.
//
// The Minimum timestamp is:
//
// 0001-01-01 00:00:00.000000
//
// This function was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
static short
minimumTimeStamp(char *dstData,
rec_datetime_field dstStartField,
rec_datetime_field dstEndField,
short dstFractPrec)
{
for (Int32 field = dstStartField; field <= dstEndField; field++) {
switch (field) {
case REC_DATE_YEAR:
{
short year = 1;
str_cpy_all(dstData, (char *)&year, sizeof(year));
dstData += sizeof(year);
break;
}
case REC_DATE_MONTH:
case REC_DATE_DAY:
*dstData++ = (char) 1;
break;
case REC_DATE_HOUR:
case REC_DATE_MINUTE:
*dstData++ = (char) 0;
break;
case REC_DATE_SECOND:
*dstData++ = (char) 0;
if (dstFractPrec) {
Lng32 fraction = 0;
str_cpy_all(dstData, (char *)&fraction, sizeof(Lng32));
dstData += sizeof(Lng32);
}
break;
default:
return -1;
}
}
return 0;
}
// convDatetimeDatetime() ============================================
// This method is used to convert the given datetime value to another
// datetime value. Missing leading fields are handled by
// Cast::preCodeGen() this method should never see them. Missing
// trailing fields are padded with their minimum value. This method
// will be used by convDatetimeDatetime (in exp_conv.cpp)
//
// The result is returned in the buffer pointed to by the parameter
// 'dstData'. This buffer must be allocated by the caller and it
// must be large enough to hold the result.
//
// This method was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
short
ExpDatetime::convDatetimeDatetime(char *srcData,
rec_datetime_field dstStartField,
rec_datetime_field dstEndField,
short dstFractPrec,
char *dstData,
Lng32 dstLen,
short validateFlag,
NABoolean *roundedDownFlag)
{
rec_datetime_field srcStartField;
rec_datetime_field srcEndField;
rec_datetime_field endField;
NABoolean LastDayPrevMonth = FALSE;
// Get the start and end fields for the source datetime type.
//
if (getDatetimeFields(getPrecision(),
srcStartField,
srcEndField) != 0) {
return -1;
}
// Are there any missing leading fields or do the src and dst fields
// not overlap? If so, this is an error. (missing leading fields
// are handled by Cast::preCodeGen()).
//
if (dstStartField < srcStartField ||
srcEndField < dstStartField ||
dstEndField < srcStartField) {
return -1;
}
// Skip over source fields that are not in the destination.
//
srcData += sizeofDatetimeFields(srcStartField,
(rec_datetime_field)(dstStartField - 1),
getScale());
srcStartField = dstStartField;
endField = srcEndField;
if (endField > dstEndField) {
endField = dstEndField;
}
// Copy required source fields to destination.
//
if (copyDatetimeFields(dstStartField,
endField,
getScale(),
dstFractPrec,
srcData,
dstData,
dstLen,
roundedDownFlag) != 0) {
return -1;
}
if (validateFlag && validateDate(dstStartField, endField,
dstData, NULL, FALSE,
LastDayPrevMonth))
return -1;
// Skip over copied source fields.
//
dstData += sizeofDatetimeFields(dstStartField, endField, dstFractPrec);
dstStartField = (rec_datetime_field)(endField + 1);
// If we still have more fields in the destination, pad them with
// the minimum timestamp.
//
if (dstStartField <= dstEndField) {
if (minimumTimeStamp(dstData,
dstStartField,
dstEndField,
dstFractPrec) != 0) {
return -1;
}
}
return 0;
}
// extractDatetime ===================================================
// This method is used to extract a given range of fields from a
// timestamp value. This method is used by
// ExpDatetime::addDatetimeInterval and ExpDatetime::subDatetimeInterval.
//
// This method was added as part of the MP Datetime Compatibility
// project.
// ===================================================================
//
short
ExpDatetime::extractDatetime(rec_datetime_field srcStartField,
rec_datetime_field srcEndField,
short srcFractPrec,
char *srcData,
char *dstData)
{
rec_datetime_field dstStartField;
rec_datetime_field dstEndField;
// Get the start and end fields for the destination datetime type.
//
if (getDatetimeFields(getPrecision(),
dstStartField,
dstEndField) != 0) {
return -1;
}
if (srcStartField > dstStartField || srcEndField < dstEndField) {
return -1;
}
srcData += sizeofDatetimeFields(srcStartField,
(rec_datetime_field)(dstStartField - 1),
srcFractPrec);
if (copyDatetimeFields(dstStartField,
dstEndField,
srcFractPrec,
getScale(),
srcData,
dstData,
getLength(),
NULL) != 0) {
return -1;
}
return 0;
}
// determineFormat() =================================================
// This static helper function for the ExpDatetime class is used to
// determine the datetime format (DEFAULT, USA, or EUROPEAN) of an
// ASCII string.
//
// The ASCII string can be in one of three formats:
//
// Default : yyyy-mm-dd hh:mm:ss.msssss
// USA : mm/dd/yyyy hh:mm:ss.msssss [am|pm]
// European: dd.mm.yyyy hh.mm.ss.msssss
//
// There are some variations on the formats above:
// - the delimiter between the date and the time portion
// can be either a ' ' (space) or a ':'
// - the [am|pm] in the USA format is case insensitive.
// - any range of consectutive (YEAR to SECOND) fields may
// be present.
//
// This function was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
static
ExpDatetime::DatetimeFormats
determineFormat(char *src,
rec_datetime_field startField,
rec_datetime_field endField)
{
// Find the first non-digit character. This is the first delimiter
//
const char* s = src;
Int32 firstFieldSize = 0;
while (isDigit8859_1(*s)) {
s++;
firstFieldSize++;
}
if ((startField == endField) ||
(startField == REC_DATE_DAY && endField == REC_DATE_HOUR)) {
// In this case there is nothing to distinguish the different
// formats. Any of them could be used. Use USA to allow for a
// possible AM/PM
//
return ExpDatetime::DATETIME_FORMAT_USA;
} else if (((startField == REC_DATE_YEAR) && (endField > REC_DATE_YEAR)) ||
((startField < REC_DATE_DAY) && (endField >= REC_DATE_DAY))) {
// In this case there is a delimiter in the DATE portion of the
// DATETIME value. The delimiter will determine the format.
//
switch(*s) {
case '-':
return ExpDatetime::DATETIME_FORMAT_DEFAULT;
case '/':
{
if (firstFieldSize == 2)
return ExpDatetime::DATETIME_FORMAT_USA;
else
return ExpDatetime::DATETIME_FORMAT_ERROR;
}
case '.':
return ExpDatetime::DATETIME_FORMAT_EUROPEAN;
default:
return ExpDatetime::DATETIME_FORMAT_ERROR;
}
} else if (((startField <= REC_DATE_HOUR) && (endField > REC_DATE_HOUR)) ||
((startField < REC_DATE_SECOND) && (endField == REC_DATE_SECOND))) {
// In this case there is no delimiter in the DATE portion, but
// there is one in the TIME portion of the DATETIME value. This
// delimiter will determine the format.
//
// If the is a delimiter before the TIME portion, go to the next
// delimiter. This can happen if the DATE portion only has a DAY
// field.
//
if (startField == REC_DATE_DAY) {
s++;
while (isDigit8859_1(*s)) {
s++;
}
}
// : could be either USA or DEFAULT. Use USA to allow for a
// possible AM/PM.
//
switch(*s) {
case ':' :
return ExpDatetime::DATETIME_FORMAT_USA;
case '.' :
return ExpDatetime::DATETIME_FORMAT_EUROPEAN;
default:
return ExpDatetime::DATETIME_FORMAT_ERROR;
}
}
// Any other delimiter is an error.
//
return ExpDatetime::DATETIME_FORMAT_ERROR;
}
// containsField() ===================================================
// This static helper function for the ExpDatetime class is used to
// determine if a given field is contained within the range of the
// given start and end fields.
//
// This function was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
static
NABoolean
containsField(rec_datetime_field field,
rec_datetime_field startField,
rec_datetime_field endField)
{
// The field is contained within the range if it is bounded by the
// start and end fields OR if the field is FRACTION and the range
// contains a SECOND field. In the latter case, the FRACTION field
// is optional, but this function still returns TRUE.
//
if ((field >= startField && field <= endField) ||
((endField == REC_DATE_SECOND) && (field == REC_DATE_FRACTION_MP))) {
return TRUE;
} else {
return FALSE;
}
}
// scanField() =======================================================
// This static helper function for the ExpDatetime class is used to
// scan the various fields of a ASCII datetime value.
//
// parameters:
//
// src - IN/OUT
// Pointer to field of the ASCII datetime value. As an input,
// it should point to the delimiter before the field to be
// scanned. If no delimiter is expected, it should point to the
// field to be scanned. As an output, upon return, this
// parameter will point to the character just beyond this field,
// or the first non-digit character encounter while scanning the
// field.
//
// srcEnd - IN
// Pointer to the character just beyond the end of the datetime
// value. This is required since the datetime value will not
// always be NULL terminated.
//
// field - IN
// The type of field to be scanned.
//
// exptDelim - IN
// The expected leading delimiter. The NULL character if a
// delimiter is not expected.
//
// fractPrec - IN
// The fractional precision of the destination. Used to perform
// scaling of the sources fraction.
//
// value - OUT
// The numeric value of the scanned field
//
// heap and diagsArea
// Used to report some errors while scanning.
//
// return - OUT
// returns FALSE if an error occurred while scanning, TRUE
// otherwise.
//
// This function was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
static
NABoolean
scanField(char *&src,
char *srcEnd,
rec_datetime_field field,
char exptDelim,
Lng32 &fractPrec,
Lng32 &value,
CollHeap *heap,
ComDiagsArea** diagsArea,
ULng32 flags)
{
NABoolean noDatetimeValidation = (flags & CONV_NO_DATETIME_VALIDATION) != 0;
NABoolean noHadoopDateFix = (flags & CONV_NO_HADOOP_DATE_FIX) != 0;
// The maximum lengths of the various fields. Since the value of
// REC_DATE_YEAR is 1, the first entry is just a place holder.
//
static const Lng32 maxLens[] = { 0, 4, 2, 2, 2, 2, 2, 9 };
static const Lng32 minValue[] = { 0, 0001, 01, 01, 00, 00, 00, 000000000 };
static const Lng32 maxValue[] = { 0, 9999, 12, 31, 23, 59, 59, 999999999 };
// The length of the scanned field.
//
short len = 0;
// The numeric value of the scanned field.
//
value = 0;
// The leading delimiter in the source string.
//
char delim;
// If we are expecting a leading delimiter, make sure the actual
// delimiter matches the expected.
//
if (exptDelim) {
// Get the actual delimiter
//
if (src < srcEnd && *src) {
delim = *src++;
} else {
delim = '\0';
}
// Check actual delimiter against the expected delimiter.
// An expected delimiter of ';' matches a ':' or a ' ' (space).
// An expected delimiter of '|' matches a ':' or a ' ' (space) or 'T' (ISO format).
// A NULL actual delimiter matches any expected delimiter.
//
if (delim) {
if (exptDelim == '-' ||
exptDelim == '/' ||
exptDelim == '.' ||
exptDelim == ':' ||
exptDelim == ' ') {
if (delim != exptDelim) {
return FALSE;
}
} else if (exptDelim == ';') {
if (delim != ':' && delim != ' ') {
return FALSE;
}
} else if (exptDelim == '|') {
if (delim != ':' && delim != ' ' && delim != 'T') {
return FALSE;
}
} else {
return FALSE;
}
}
}
// Convert the string of digits into a numeric value. Stop scanning
// when the max. number of digits for this field have been scanned
// or if a non-digit is encountered or if the end of the source was
// reached
//
for (len = 0;
len < maxLens[field] && src < srcEnd && isDigit8859_1(*src);
len++) {
value = (value * 10) + (*src++ - '0');
}
// Scale the fraction of the source to match the fractional
// precision of the destination.
//
if (field == REC_DATE_FRACTION_MP) {
value = scaleFraction(len, value, fractPrec);
fractPrec = len;
}
// For all but the FRACTION field, datetime fields are required to
// have the maximum length.
//
if (len < maxLens[field] && field != REC_DATE_FRACTION_MP) {
if ((NOT noHadoopDateFix) &&
(field >= REC_DATE_HOUR)) {
// extend with zeroes
value = 0;
}
else {
// An unknown character was encountered in the string.
//
char hexstr[MAX_OFFENDING_SOURCE_DATA_DISPLAY_LEN];
ExRaiseSqlError(heap, diagsArea, EXE_CONVERT_STRING_ERROR,NULL,NULL,NULL,NULL,stringToHex(hexstr, sizeof(hexstr), src, srcEnd-src));
return FALSE;
}
} else if (src < srcEnd && isDigit8859_1(*src)) {
return FALSE;
}
// If the value is too small or too large, the field is invalid.
//
if ((NOT noDatetimeValidation) &&
(value < minValue[field] || value > maxValue[field])) {
return FALSE;
}
return TRUE;
}
short
ExpDatetime::convAsciiToDatetime(char *srcData,
Lng32 srcLen,
char *dstData,
Lng32 dstLen,
rec_datetime_field dstStartField,
rec_datetime_field dstEndField,
Lng32 format,
Lng32 &scale,
CollHeap *heap,
ComDiagsArea** diagsArea,
ULng32 flags)
{
NABoolean noDatetimeValidation = (flags & CONV_NO_DATETIME_VALIDATION) != 0;
Lng32 originalSrcLen = srcLen;
char * originalSrcData = srcData;
// skip leading and trailing blanks and adjust srcData and srcLen
// accordingly
//
NABoolean LastDayPrevMonth = FALSE;
Lng32 i;
for (i = 0; i < srcLen && *srcData == ' '; i++) {
srcData++;
}
if (i == srcLen) {
// string contains only blanks.
//
ExRaiseSqlError(heap, diagsArea, EXE_CONVERT_DATETIME_ERROR);
if(*diagsArea != NULL)
**diagsArea << DgString0("(string contains only blanks)");
return -1;
};
srcLen -= i;
// we know that we found at least one non-blank character.
// so we just decrement srcLen till we scanned all
// trailing blanks
//
while (srcData[srcLen - 1] == ' ') {
srcLen--;
}
// Indicates if an " AM" or " PM" strings appears at the end of the
// source.
// 0 - means no AM/PM indicator
// 1 - means AM
// 2 - means PM
//
short usaAmPm = 0;
// Check for the " AM" or " PM" at the end of the source. If it is
// there, record and adjust the srcLen to in effect remove it from
// the source.
//
if ((srcData[srcLen - 3] == ' ') &&
((srcData[srcLen - 2] == 'A') || (srcData[srcLen - 2] == 'a')) &&
((srcData[srcLen - 1] == 'M') || (srcData[srcLen - 1] == 'm'))) {
usaAmPm = 1;
srcLen -= 3;
} else if ((srcData[srcLen - 3] == ' ') &&
((srcData[srcLen - 2] == 'P') || (srcData[srcLen - 2] == 'p')) &&
((srcData[srcLen - 1] == 'M') || (srcData[srcLen - 1] == 'm'))) {
usaAmPm = 2;
srcLen -= 3;
}
// Indicates if a 'Z' appears at the end of source.
// 'Z' is a local timezone indicator for ISO format datetime values.
// 0 - means no 'Z' indicator
// 1 - means Z
//
short defZ = 0;
// Check for the " AM" or " PM" at the end of the source. If it is
// there, record and adjust the srcLen to in effect remove it from
// the source.
//
if ((usaAmPm == 0) && (srcData[srcLen - 1] == 'Z')) {
defZ = 1;
srcLen -= 1;
}
char *src = srcData;
char *srcEnd = srcData + srcLen;
// Determine the format of the source string.
//
if (format == DATETIME_FORMAT_NONE)
format = determineFormat(src, dstStartField, dstEndField);
// If the format could not be determined, issue an error.
//
if (format == DATETIME_FORMAT_ERROR) {
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
// check to see if they are timezone adjustment designator
// as per ISO8601 datetime format.
// TZD is of the form: +HH:MM pr -HH:MM
//
NABoolean TZD = FALSE;
NABoolean isAdd = FALSE;
Lng32 hh = 0;
Lng32 mm = 0;
Lng32 tzdSize = strlen("+HH:MM");
char * tzd = NULL;
if ((srcLen > tzdSize) &&
(tzd = (srcEnd - tzdSize)) &&
((tzd[0] == '+') ||
(tzd[0] == '-')) &&
(tzd[3] == ':')) {
hh = str_atoi(&tzd[1], strlen("HH"));
mm = str_atoi(&tzd[4], strlen("MM"));
if (tzd[0] == '+')
isAdd = FALSE;
else
isAdd = TRUE;
TZD = TRUE;
srcLen -= tzdSize;
srcEnd -= tzdSize;
} // tzd specified
// if timezone is specified and end field is not DAY, return error.
if ((defZ || TZD) && (dstEndField == REC_DATE_DAY))
{
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
// The order of the fields for the various formats.
//
static const rec_datetime_field realFields[][DATETIME_MAX_NUM_FIELDS] = {
{ REC_DATE_YEAR,
REC_DATE_MONTH,
REC_DATE_DAY,
REC_DATE_HOUR,
REC_DATE_MINUTE,
REC_DATE_SECOND,
REC_DATE_FRACTION_MP}, // DEFAULT
{ REC_DATE_MONTH,
REC_DATE_DAY,
REC_DATE_YEAR,
REC_DATE_HOUR,
REC_DATE_MINUTE,
REC_DATE_SECOND,
REC_DATE_FRACTION_MP}, // USA
{ REC_DATE_DAY,
REC_DATE_MONTH,
REC_DATE_YEAR,
REC_DATE_HOUR,
REC_DATE_MINUTE,
REC_DATE_SECOND,
REC_DATE_FRACTION_MP} // EUROPEAN
};
// The order of the leading delimiters for the various formats.
// 'x' - indicates the no leading delimiter is expected for the
// first field.
//
static const char delimiters[][DATETIME_MAX_NUM_FIELDS+1] = {
"x--|::.", // DEFAULT
"x//;::.", // USA
"x..;..." // EUROPEAN
};
// Used to hold the scanned values of the datetime value.
//
Lng32 datetimeValues[DATETIME_MAX_NUM_FIELDS+1];
// Until the first field is scanned, no leading delimiter is
// expected.
//
NABoolean needDelimiter = FALSE;
// The expected delimiter. The Null character indicates to
// scanField that no leading delimiter is expected.
//
char delim = '\0';
// Iterate over all the fields in the order dictated by the format.
// Only fields the should be present are actually scanned.
//
Int32 field;
Lng32 trueScale = scale;
for (field = 0; field < DATETIME_MAX_NUM_FIELDS; field++) {
// Determine the field expected for this format.
//
rec_datetime_field realField = realFields[format][field];
// Scan this field only if it is contained within the destinations
// start and end field.
//
if (containsField(realField, dstStartField, dstEndField)) {
// Get the expected leading delimiter if one is required.
//
if(needDelimiter)
delim = delimiters[format][field];
// After the first field is scanned, a leading delimiter is
// always required.
//
needDelimiter = TRUE;
// Scan the field.
// src - advanced to the next field.
// datetimeVValues[realField] is set to the scanned value.
//
if (!scanField(src,
srcEnd,
realField,
delim,
trueScale,
datetimeValues[realField],
heap,
diagsArea,
flags)) {
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
}
}
// If there are any remaining characters in the input string.
if (src != srcEnd) {
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
// Adjust the value of the hour field if an "AM" or "PM" was
// present.
//
if (format == DATETIME_FORMAT_USA &&
containsField(REC_DATE_HOUR, dstStartField, dstEndField) &&
usaAmPm) {
if (datetimeValues[REC_DATE_HOUR] > 12) {
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
if (usaAmPm == 1) {
// " AM" present.
//
if (datetimeValues[REC_DATE_HOUR] == 12) {
datetimeValues[REC_DATE_HOUR] = 0;
}
} else {
// " PM" present.
//
if (datetimeValues[REC_DATE_HOUR] < 12) {
datetimeValues[REC_DATE_HOUR] += 12;
}
}
} else if (usaAmPm) {
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
short year = 1900;
char month = 1;
char day = 1;
char hour = 0;
char minute = 0;
char second = 0;
Lng32 fraction = 0;
// Copy the parsed values to the destination.
//
char *dst = dstData;
for (field = dstStartField; field <= dstEndField ; field++) {
switch (field) {
case REC_DATE_YEAR:
{
year = (short)datetimeValues[field];
str_cpy_all(dst, (char *)&year, sizeof(year));
dst += sizeof(year);
}
break;
case REC_DATE_MONTH:
month = (char)datetimeValues[field];
*dst++ = month;
break;
case REC_DATE_DAY:
day = (char)datetimeValues[field];
*dst++ = day;
break;
case REC_DATE_HOUR:
hour = (char)datetimeValues[field];
*dst++ = hour;
break;
case REC_DATE_MINUTE:
minute = (char)datetimeValues[field];
*dst++ = minute;
break;
case REC_DATE_SECOND:
second = (char)datetimeValues[field];
*dst++ = second;
if (scale) {
fraction = datetimeValues[field + 1];
str_cpy_all(dst, (char *)&fraction, sizeof(fraction));
dst += sizeof(fraction);
}
break;
default:
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
}
scale = trueScale;
// Validate the date fields of the result.
//
if (NOT noDatetimeValidation)
if (validateDate(dstStartField, dstEndField,
dstData, NULL, FALSE,
LastDayPrevMonth)) {
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
};
if (TZD) {
// timezone specified. Compute the new datetime value.
// first, convert current datetime value to juliantimestamp
Lng32 jtsFraction = fraction / 1000;
short timestamp[] = {
year, month, day, hour, minute, second,
(short)(jtsFraction / 1000), (short)(jtsFraction % 1000)
};
short error;
Int64 juliantimestamp = COMPUTETIMESTAMP(timestamp, &error);
if (error) {
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
Int64 usec = (hh*60L + mm) * 60L * 1000000L;
if (isAdd)
juliantimestamp += usec;
else
juliantimestamp -= usec;
INTERPRETTIMESTAMP(juliantimestamp, timestamp);
char *dst = dstData;
for (field = dstStartField; field <= dstEndField ; field++) {
switch (field) {
case REC_DATE_YEAR:
{
year = timestamp[0];
str_cpy_all(dst, (char *)&year, sizeof(year));
dst += sizeof(year);
}
break;
case REC_DATE_MONTH:
month = (char) timestamp[1];
*dst++ = month;
break;
case REC_DATE_DAY:
day = (char) timestamp[2];
*dst++ = day;
break;
case REC_DATE_HOUR:
hour = (char) timestamp[3];
*dst++ = hour;
break;
case REC_DATE_MINUTE:
minute = (char) timestamp[4];
*dst++ = minute;
break;
case REC_DATE_SECOND:
second = (char) timestamp[5];
*dst++ = second;
if (scale) {
//fraction = timestamp[6] * 1000 + timestamp[7];
str_cpy_all(dst, (char *)&fraction, sizeof(fraction));
dst += sizeof(fraction);
}
break;
default:
raiseDateConvErrorWithSrcData(originalSrcLen,diagsArea, originalSrcData, heap);
return -1;
}
}
} // TZD
// Success
//
return 0;
}
short
ExpDatetime::convAsciiToDatetime(char *srcData,
Lng32 srcLen,
char *dstData,
Lng32 dstLen,
Lng32 format,
CollHeap *heap,
ComDiagsArea** diagsArea,
ULng32 flags)
{
rec_datetime_field dstStartField;
rec_datetime_field dstEndField;
if (getDatetimeFields(getPrecision(),
dstStartField,
dstEndField) != 0) {
return -1;
}
Lng32 scale = getScale();
return convAsciiToDatetime(srcData, srcLen, dstData, dstLen,
dstStartField, dstEndField, format, scale,
heap, diagsArea, flags);
}
static NABoolean convertStrToMonth(char* &srcData, char *result,
const char * nextByte,
CollHeap * heap, ComDiagsArea** diagsArea)
{
int copyLen = strlen(srcData);
char * originalSrcData = srcData;
const char * months[] =
{
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC"
};
for (Int32 i = 0; i < 12; i++)
{
char upVal[3];
str_cpy_convert(upVal, srcData, 3, 1);
if (memcmp(upVal, months[i], 3) == 0)
{
*result = (char)(i + 1);
srcData += 3;
if (nextByte)
{
if (*srcData != *nextByte)
{
// string contains non-digit
raiseDateConvErrorWithSrcData(copyLen,diagsArea, originalSrcData, heap);
return FALSE; // error
}
srcData++;
}
return TRUE;
}
} // for
// error
raiseDateConvErrorWithSrcData(copyLen,diagsArea, originalSrcData, heap);
return FALSE;
}
static NABoolean
convertStrToMonthLongFormat(char* &value, char *result)
{
const char * months[] =
{
"JANUARY",
"FEBRUARY",
"MARCH",
"APRIL",
"MAY",
"JUNE",
"JULY",
"AUGUST",
"SEPTEMBER",
"OCTOBER",
"NOVEMBER",
"DECEMBER"
};
for (Int32 i = 0; i < 12; i++)
{
char upVal[10];
str_cpy_convert(upVal, value, strlen(months[i]), 1);
if (memcmp(upVal, months[i], strlen(months[i])) == 0)
{
*result = (char)(i + 1);
value += strlen(months[i]);
return TRUE;
}
}
// error
return FALSE;
}
static short convSrcDataToDst(Lng32 numSrcBytes, char* &srcData,
Lng32 numTgtBytes, char *dstData,
const char * nextByte,
CollHeap * heap, ComDiagsArea** diagsArea)
{
Lng32 src = 0;
Lng32 val = 0;
for (val = 0, src = 0; src < numSrcBytes && isDigit8859_1(*srcData);
src++, srcData++)
val = val * 10 + (*srcData - '0');
if (src < numSrcBytes)
{
// string contains non-digit
raiseDateConvErrorWithSrcData(numSrcBytes,diagsArea, srcData, heap);
return -1;
}
if (numTgtBytes == sizeof(Lng32))
*(Lng32*)dstData = val;
else if (numTgtBytes == sizeof(short))
*(short*)dstData = val;
else if (numTgtBytes == sizeof(char))
*(char*)dstData = val;
else
return -1;
if (nextByte && (strlen(nextByte) > 0))
{
if (*srcData != *nextByte)
{
// string contains non-digit
raiseDateConvErrorWithSrcData(numSrcBytes,diagsArea, srcData, heap);
return -1;
}
srcData++;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
// ExpDatetime::convAsciiToDate() ================================
// This method is used to convert the given ASCII string
// to a datetime date value.
//
// The result is returned in the buffer pointed to by the parameter
// 'dstData'. This buffer must be allocated by the caller and it
// must be large enough to hold the result.
//
// This method is called assuming the correct source format. The source
// string must contain date and, possibly, leading and trailing blanks
// only. The size of destination buffer should be just enough to hold
// internal representation of the date value, i.e. 4 bytes.
//
// target dstData has the format:
// Timestamp:
// dstData[0..1] 2-bytes for year.
// dstData[2] .. dstData[6] 1-byte for month through second.
// dstData[7..10] 4-bytes for fraction.
// Date:
// dstData[0..1] 2-bytes for year.
// dstData[2] .. dstData[3] 1-byte for month through day.
// Time:
// dstData[0] .. dstData[2] 1-byte for hour through second.
// =====================================================================
//
short
ExpDatetime::convAsciiToDate(char *srcData,
Lng32 inSrcLen,
char *dstData,
Lng32 dstLen,
Int32 format,
CollHeap *heap,
ComDiagsArea** diagsArea,
ULng32 flags)
{
NABoolean noDatetimeValidation = (flags & CONV_NO_DATETIME_VALIDATION) != 0;
char * timeData = NULL; // assume no time data
char * origSrcData = srcData;
short year;
Lng32 srcFormat, i;
NABoolean LastDayPrevMonth = FALSE;
Lng32 srcLen = inSrcLen;
if (*srcData == ' ') {
// skip leading blanks and adjust srcData and srcLen accordingly
//
for (i = 0; i < srcLen && *srcData == ' '; i++) {
srcData++;
}
if (i == srcLen) {
// string contains only blanks.
raiseDateConvErrorWithSrcData(inSrcLen,diagsArea, srcData, heap);
return -1;
};
srcLen -= i;
};
// need to decide what the source format is
//
if (format == DATETIME_FORMAT_NONE)
srcFormat = determineFormat(srcData, REC_DATE_YEAR, REC_DATE_DAY);
else
srcFormat = format;
Lng32 minLength = getDatetimeFormatLen(srcFormat, TRUE,
REC_DATE_YEAR, REC_DATE_DAY);
if ((minLength <= 0) || (srcLen < minLength)) {
// string doesn't seem to contain all date fields.
//
raiseDateConvErrorWithSrcData(inSrcLen,diagsArea, srcData, heap);
return -1;
};
switch (srcFormat) {
case DATETIME_FORMAT_DEFAULT: // YYYY-MM-DD
{
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, "-", heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], "-", heap, diagsArea))
return -1;
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], NULL, heap, diagsArea))
return -1;
};
break;
case DATETIME_FORMAT_DEFAULT2: // YYYY-MM
{
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, "-", heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], NULL, heap, diagsArea))
return -1;
// the day
// day is not specified, fill in as '1' (first day of month).
dstData[3] = 1;
};
break;
case DATETIME_FORMAT_TS3: // YYYY-MM-DD HH24:MI:SS
{
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, "-", heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], "-", heap, diagsArea))
return -1;
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], " ", heap, diagsArea))
return -1;
// the hour
if (convSrcDataToDst(2, srcData, 1, &dstData[4], ":", heap, diagsArea))
return -1;
// the minute
if (convSrcDataToDst(2, srcData, 1, &dstData[5], ":", heap, diagsArea))
return -1;
// the second
if (convSrcDataToDst(2, srcData, 1, &dstData[6], NULL, heap, diagsArea))
return -1;
dstData[7] = 0;
dstData[8] = 0;
dstData[9] = 0;
dstData[10] = 0;
timeData = &dstData[4];
};
break;
case DATETIME_FORMAT_USA: // MM/DD/YYYY AM|PM
case DATETIME_FORMAT_USA2: // MM/DD/YYYY
case DATETIME_FORMAT_USA6: // MM/DD/YY
case DATETIME_FORMAT_USA7: // MM-DD-YYYY
{
char sep = (srcFormat == DATETIME_FORMAT_USA7 ? '-' : '/');
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], &sep, heap,diagsArea))
return -1;
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], &sep, heap, diagsArea))
return -1;
// the year
Int32 numOfYdigits = (srcFormat == DATETIME_FORMAT_USA6 ? 2 : 4);
if (convSrcDataToDst(numOfYdigits, srcData, 2, dstData, NULL, heap, diagsArea))
return -1;
};
break;
case DATETIME_FORMAT_TS6: // MMDDYYYY HH24:MI:SS
case DATETIME_FORMAT_TS7: // MM/DD/YYYY HH24:MI:SS
{
char sep = '/';
char * septr = (srcFormat == DATETIME_FORMAT_TS7 ? &sep : NULL);
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], septr, heap, diagsArea))
return -1;
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], septr, heap, diagsArea))
return -1;
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, " ", heap, diagsArea))
return -1;
// the hour
if (convSrcDataToDst(2, srcData, 1, &dstData[4], ":", heap, diagsArea))
return -1;
// the minute
if (convSrcDataToDst(2, srcData, 1, &dstData[5], ":", heap, diagsArea))
return -1;
// the second
if (convSrcDataToDst(2, srcData, 1, &dstData[6], NULL, heap, diagsArea))
return -1;
dstData[7] = 0;
dstData[8] = 0;
dstData[9] = 0;
dstData[10] = 0;
timeData = &dstData[4];
};
break;
case DATETIME_FORMAT_USA3: // YYYY/MM/DD
case DATETIME_FORMAT_USA4: // YYYYMMDD
case DATETIME_FORMAT_USA5: // YY/MM/DD
{
// the year
Lng32 numYearDigits = (srcFormat == DATETIME_FORMAT_USA5 ? 2 : 4);
char sep = '/';
char * septr = (srcFormat == DATETIME_FORMAT_USA4 ? NULL : &sep);
// the year
if (convSrcDataToDst(numYearDigits, srcData, 2, dstData, septr, heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], septr, heap, diagsArea))
return -1;
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], NULL, heap, diagsArea))
return -1;
};
break;
case DATETIME_FORMAT_USA8: // YYYYMM
{
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, NULL, heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], NULL, heap, diagsArea))
return -1;
// the day
// day is not specified, fill in as '1' (first day of month).
dstData[3] = 1;
};
break;
case DATETIME_FORMAT_TS1: // YYYYMMDDHH24MISS
case DATETIME_FORMAT_TS5: // YYYYMMDD:HH24:MI:SS
{
char sep = ':';
char * septr = (srcFormat == DATETIME_FORMAT_TS1 ? NULL : &sep);
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, NULL, heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], NULL, heap, diagsArea))
return -1;
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], septr, heap, diagsArea))
return -1;
// the hour
if (convSrcDataToDst(2, srcData, 1, &dstData[4], septr, heap, diagsArea))
return -1;
// the minute
if (convSrcDataToDst(2, srcData, 1, &dstData[5], septr, heap, diagsArea))
return -1;
// the second
if (convSrcDataToDst(2, srcData, 1, &dstData[6], NULL, heap, diagsArea))
return -1;
dstData[7] = 0;
dstData[8] = 0;
dstData[9] = 0;
dstData[10] = 0;
timeData = &dstData[4];
};
break;
case DATETIME_FORMAT_TS9: // MONTH DD, YYYY, HH:MI AM|PM
{
// the month
char * prevSrcData = srcData;
if (! convertStrToMonthLongFormat(srcData, &dstData[2])) {
raiseDateConvErrorWithSrcData(inSrcLen,diagsArea, srcData, heap);
return -1;
}
minLength += (srcData - prevSrcData);
srcData += 1; // skip blank after "Month"
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], ",", heap, diagsArea))
return -1;
srcData++; // skip over blank
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, ",", heap, diagsArea))
return -1;
srcData++; // skip over blank
// the hour
if (convSrcDataToDst(2, srcData, 1, &dstData[4], ":", heap, diagsArea))
return -1;
// the minute
if (convSrcDataToDst(2, srcData, 1, &dstData[5], NULL, heap, diagsArea))
return -1;
dstData[6] = 0;
dstData[7] = 0;
dstData[8] = 0;
dstData[9] = 0;
dstData[10] = 0;
timeData = &dstData[4];
}
break;
case DATETIME_FORMAT_EUROPEAN: // DD.MM.YYYY
case DATETIME_FORMAT_EUROPEAN2: // DD-MM-YYYY
{
char sep = (srcFormat == DATETIME_FORMAT_EUROPEAN ? '.' : '-');
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], &sep, heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], &sep, heap, diagsArea))
return -1;
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, NULL, heap, diagsArea))
return -1;
};
break;
case DATETIME_FORMAT_EUROPEAN3: // DD-MON-YYYY
case DATETIME_FORMAT_EUROPEAN4: // DDMONYYYY
{
char sep = '-';
char * septr = (srcFormat == DATETIME_FORMAT_EUROPEAN3 ? &sep : NULL);
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], septr, heap, diagsArea))
return -1;
// the month
if (! convertStrToMonth(srcData, &dstData[2], septr, heap, diagsArea))
return -1;
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, NULL, heap, diagsArea))
return -1;
};
break;
case DATETIME_FORMAT_TS2: // DD.MM.YYYY:HH24.MI.SS
case DATETIME_FORMAT_TS10: // DD.MM.YYYY HH24.MI.SS
{
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], ".", heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], ".", heap, diagsArea))
return -1;
// the year
if (srcFormat == DATETIME_FORMAT_TS2)
{
if (convSrcDataToDst(4, srcData, 2, dstData, ":", heap, diagsArea))
return -1;
}
else
{
if (convSrcDataToDst(4, srcData, 2, dstData, " ", heap, diagsArea))
return -1;
}
// the hour
if (convSrcDataToDst(2, srcData, 1, &dstData[4], ".", heap, diagsArea))
return -1;
// the minute
if (convSrcDataToDst(2, srcData, 1, &dstData[5], ".", heap, diagsArea))
return -1;
// the second
if (convSrcDataToDst(2, srcData, 1, &dstData[6], NULL, heap, diagsArea))
return -1;
dstData[7] = 0;
dstData[8] = 0;
dstData[9] = 0;
dstData[10] = 0;
timeData = &dstData[4];
};
break;
case DATETIME_FORMAT_TS11: // YYYY/MM/DD HH24:MI:SS
{
char sep = '/';
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, &sep, heap, diagsArea))
return -1;
// the month
if (convSrcDataToDst(2, srcData, 1, &dstData[2], &sep, heap, diagsArea))
return -1;
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], " ", heap, diagsArea))
return -1;
// the hour
if (convSrcDataToDst(2, srcData, 1, &dstData[4], ":", heap, diagsArea))
return -1;
// the minute
if (convSrcDataToDst(2, srcData, 1, &dstData[5], ":", heap, diagsArea))
return -1;
// the second
if (convSrcDataToDst(2, srcData, 1, &dstData[6], NULL, heap, diagsArea))
return -1;
dstData[7] = 0;
dstData[8] = 0;
dstData[9] = 0;
dstData[10] = 0;
timeData = &dstData[4];
};
break;
case DATETIME_FORMAT_TS8: // DD-MON-YYYY HH:MI:SS
{
// the day
if (convSrcDataToDst(2, srcData, 1, &dstData[3], "-", heap, diagsArea))
return -1;
// the month
if (! convertStrToMonth(srcData, &dstData[2], "-", heap, diagsArea))
return -1;
// the year
if (convSrcDataToDst(4, srcData, 2, dstData, " ", heap, diagsArea))
return -1;
// the hour
if (convSrcDataToDst(2, srcData, 1, &dstData[4], ":", heap, diagsArea))
return -1;
// the minute
if (convSrcDataToDst(2, srcData, 1, &dstData[5], ":", heap, diagsArea))
return -1;
// the second
if (convSrcDataToDst(2, srcData, 1, &dstData[6], NULL, heap, diagsArea))
return -1;
dstData[7] = 0;
dstData[8] = 0;
dstData[9] = 0;
dstData[10] = 0;
timeData = &dstData[4];
};
break;
case DATETIME_FORMAT_TS4: // HH24:MI:SS
{
// the hour
if (convSrcDataToDst(2, srcData, 1, &dstData[0], ":", heap, diagsArea))
return -1;
// the minute
if (convSrcDataToDst(2, srcData, 1, &dstData[1], ":", heap, diagsArea))
return -1;
// the second
if (convSrcDataToDst(2, srcData, 1, &dstData[2], NULL, heap, diagsArea))
return -1;
timeData = &dstData[0];
};
break;
default:
{
// Format could not be determined, issue an error.
raiseDateConvErrorWithSrcData(inSrcLen,diagsArea, srcData, heap);
return -1;
}
};
// done for all fields. if the source string is not exhausted
// make sure it contains only blanks
if (srcLen > minLength) {
srcLen -= minLength;
for (i = 0; i < srcLen; i++) {
if (srcData[i] != ' ') {
// string contains only blanks.
raiseDateConvErrorWithSrcData(inSrcLen,diagsArea,srcData,heap);
return -1;
}
}
} // if srcLen > 10
// Validate the date fields of the result.
//
if (srcFormat != DATETIME_FORMAT_TS4)
{
if (NOT noDatetimeValidation)
if (validateDate(REC_DATE_YEAR, REC_DATE_DAY,
dstData, NULL, FALSE,
LastDayPrevMonth)) {
raiseDateConvErrorWithSrcData(inSrcLen,diagsArea,origSrcData,heap);
return -1;
};
}
// Validate the time fields of the result
//
if (timeData)
{
if (NOT noDatetimeValidation)
if (validateTime(timeData))
{
raiseDateConvErrorWithSrcData(inSrcLen,diagsArea,origSrcData,heap);
return -1;
}
}
// Success
//
return 0;
}
// convertToAscii() ==============================================
// This static helper function of the ExpDatetime class is used by
// convDatetimeToASCII() to convert a numeric value to a string of the
// given width.
//
// 'value' is an input containing the numeric value to be formatted.
//
// 'result' is an input/output parameter pointing to the destination.
// As an output it points to the next available byte in the result
// buffer.
//
// 'width' is an input specifying the required width of the value.
//
// This function assumes that the value will fit within the specified
// width.
//
// This function was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
static void
convertToAscii(Lng32 value, char *&result, UInt32 width)
{
UInt32 i = width;
// Format value as a string.
//
while ((value != 0) && (i > 0)) {
result[--i] = '0' + (char) (value % 10);
value /= 10;
}
// Fill in remaining leading characters with '0'
//
while (i > 0)
result[--i] = '0';
// Update result pointer to point to end of string.
//
result += width;
}
static void
convertMonthToStr(Lng32 value, char *&result, UInt32 width)
{
const char * months[] =
{
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC"
};
strcpy(result, months[value-1]);
// Update result pointer to point to end of string.
//
result += width;
}
static void
convertDayOfWeekToStr(Lng32 value, char *&result, NABoolean bAbbreviation, UInt32 width)
{
const char* dayofweek[] =
{
"SUNDAY ",
"MONDAY ",
"TUESDAY ",
"WEDNESDAY",
"THURSDAY ",
"FRIDAY ",
"SATURDAY "
};
const char* dayofweek_abb[] =
{
"SUN",
"MON",
"TUE",
"WED",
"THU",
"FRI",
"SAT"
};
if (bAbbreviation)
strcpy(result, dayofweek_abb[value-1]);
else
strcpy(result, dayofweek[value-1]);
// Update result pointer to point to end of string.
result += width;
}
static void
convertMonthToStrLongFormat(Lng32 value, char *&result, UInt32 width)
{
const char * months[] =
{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
strcpy(result, months[value-1]);
// Update result pointer to point to end of string.
//
result += strlen(months[value-1]);
}
Lng32 ExpDatetime::getDatetimeFormatLen(Lng32 format, NABoolean to_date,
rec_datetime_field startField,
rec_datetime_field endField)
{
switch (format)
{
case DATETIME_FORMAT_DEFAULT:
case DATETIME_FORMAT_USA:
case DATETIME_FORMAT_EUROPEAN:
{
if (to_date)
{
return ExpDatetime::getDatetimeFormatLen(format);
}
else
{
Lng32 minReqDstLen = 0;
Int32 field;
for (field = startField; field <= endField; field++) {
switch (field) {
case REC_DATE_YEAR:
minReqDstLen += 5;
break;
case REC_DATE_MONTH:
minReqDstLen += 3;
break;
case REC_DATE_DAY:
case REC_DATE_MINUTE:
minReqDstLen += 3;
break;
case REC_DATE_HOUR:
minReqDstLen += 3;
if (format == DATETIME_FORMAT_USA)
minReqDstLen += 3;
break;
case REC_DATE_SECOND:
minReqDstLen += 3;
break;
default:
return -1;
}
}
// No trailing delimiter required.
//
minReqDstLen--;
return minReqDstLen;
}
}
break;
default:
return ExpDatetime::getDatetimeFormatLen(format);
}
return 0;
}
// convDatetimeToASCII() ============================================
// This method is used to convert the given datetime value to an ASCII
// string in one of three formats (DEFAULT, EUROPEAN, and USA).
//
// This method was added as part of the MP Datetime Compatibility
// project.
// =====================================================================
//
Lng32
ExpDatetime::convDatetimeToASCII(char *srcData,
char *dstData,
Lng32 dstLen,
Int32 format,
char *formatStr,
CollHeap *heap,
ComDiagsArea** diagsArea)
{
// Get the start and end fields of the datetime value.
//
rec_datetime_field startField;
rec_datetime_field endField;
if (getDatetimeFields(getPrecision(),
startField,
endField) != 0) {
return -1;
}
// Variable for the Date portion of the datetime value. We need to
// store these values since the order in which they appear in the
// string result, depends on the format.
//
short year=0;
char month=0;
char day=0;
// Remember the original hour in case we need to output an 'AM' or
// 'PM'
//
char militaryHour = 0;
char *dstDataPtr = dstData;
// Calculate the minimum length required to store the string
// result. The fraction portion of the second field can be left off
// or only partially present, so do not include it in this
// calculation.
//
Lng32 minReqDstLen = 0;
minReqDstLen = getDatetimeFormatLen(format, FALSE, startField, endField);
// Make sure we have enough room for at least the minimum.
//
if((minReqDstLen <= 0) || (minReqDstLen > dstLen)) {
ExRaiseSqlError(heap, diagsArea, EXE_STRING_OVERFLOW);
return -1;
}
// Capture the date portion of the datetime value in the
// corresponding variable.
//
Int32 field;
for (field = startField;
field <= endField && field <= REC_DATE_DAY;
field++) {
switch (field) {
case REC_DATE_YEAR:
str_cpy_all((char *) &year, srcData, sizeof(year));
srcData += sizeof(year);
break;
case REC_DATE_MONTH:
month = *srcData++;
break;
case REC_DATE_DAY:
day = *srcData++;
break;
default:
return -1;
}
}
// Format the Date portion in the proper format.
//
switch (format) {
case DATETIME_FORMAT_DEFAULT:
case DATETIME_FORMAT_DEFAULT2:
case DATETIME_FORMAT_USA3:
case DATETIME_FORMAT_USA4:
case DATETIME_FORMAT_USA5:
case DATETIME_FORMAT_USA8:
case DATETIME_FORMAT_TS1:
case DATETIME_FORMAT_TS3:
case DATETIME_FORMAT_TS5:
case DATETIME_FORMAT_TS11:
if (year) {
if (format == DATETIME_FORMAT_USA5)
convertToAscii(year, dstDataPtr, 2);
else
convertToAscii(year, dstDataPtr, 4);
if (endField > REC_DATE_YEAR) {
if ((format == DATETIME_FORMAT_DEFAULT) ||
(format == DATETIME_FORMAT_DEFAULT2) ||
(format == DATETIME_FORMAT_TS3))
*dstDataPtr++ = '-';
else if ((format == DATETIME_FORMAT_USA3) ||
(format == DATETIME_FORMAT_USA5) ||
(format == DATETIME_FORMAT_TS11))
*dstDataPtr++ = '/';
}
}
if (month) {
convertToAscii(month, dstDataPtr, 2);
if (endField > REC_DATE_MONTH) {
if ((format == DATETIME_FORMAT_DEFAULT) ||
(format == DATETIME_FORMAT_TS3))
*dstDataPtr++ = '-';
else if ((format == DATETIME_FORMAT_USA3) ||
(format == DATETIME_FORMAT_USA5) ||
(format == DATETIME_FORMAT_TS11))
*dstDataPtr++ = '/';
}
}
if (day) {
convertToAscii(day, dstDataPtr, 2);
}
break;
case DATETIME_FORMAT_USA:
case DATETIME_FORMAT_USA2:
case DATETIME_FORMAT_USA6:
case DATETIME_FORMAT_USA7:
case DATETIME_FORMAT_TS6:
case DATETIME_FORMAT_TS7:
{
char delim = (format == DATETIME_FORMAT_USA7 ? '-'
: (format == DATETIME_FORMAT_TS6 ? 0 : '/'));
if (month) {
convertToAscii(month, dstDataPtr, 2);
if ((startField < REC_DATE_MONTH || endField > REC_DATE_MONTH) &&
(delim != 0)) {
*dstDataPtr++ = delim;
}
}
if (day) {
convertToAscii(day, dstDataPtr, 2);
if (startField < REC_DATE_MONTH &&
(delim != 0)) {
*dstDataPtr++ = delim;
}
}
if (year) {
Int32 numOfYdigits = (format == DATETIME_FORMAT_USA6 ? 2 : 4);
convertToAscii(year, dstDataPtr, numOfYdigits);
}
}
break;
case DATETIME_FORMAT_EUROPEAN:
case DATETIME_FORMAT_EUROPEAN2:
case DATETIME_FORMAT_EUROPEAN3:
case DATETIME_FORMAT_EUROPEAN4:
case DATETIME_FORMAT_TS2:
case DATETIME_FORMAT_TS8:
case DATETIME_FORMAT_TS10:
if (day) {
convertToAscii(day, dstDataPtr, 2);
if (startField < REC_DATE_DAY) {
if ((format == DATETIME_FORMAT_EUROPEAN) ||
(format == DATETIME_FORMAT_TS2) ||
(format == DATETIME_FORMAT_TS10))
*dstDataPtr++ = '.';
else if (format != DATETIME_FORMAT_EUROPEAN4)
*dstDataPtr++ = '-';
}
}
if (month) {
if ((format == DATETIME_FORMAT_EUROPEAN3) ||
(format == DATETIME_FORMAT_EUROPEAN4) ||
(format == DATETIME_FORMAT_TS8))
convertMonthToStr(month, dstDataPtr, 3);
else
convertToAscii(month, dstDataPtr, 2);
if (startField < REC_DATE_MONTH) {
if ((format == DATETIME_FORMAT_EUROPEAN) ||
(format == DATETIME_FORMAT_TS2) ||
(format == DATETIME_FORMAT_TS10))
*dstDataPtr++ = '.';
else if (format != DATETIME_FORMAT_EUROPEAN4)
*dstDataPtr++ = '-';
}
}
if (year) {
convertToAscii(year, dstDataPtr, 4);
}
break;
case DATETIME_FORMAT_TS9:
{
Lng32 length = ExpDatetime::getDatetimeFormatMaxLen(DATETIME_FORMAT_TS9);
memset(dstDataPtr, ' ', length);
convertMonthToStrLongFormat(month, dstDataPtr, 3);
*dstDataPtr++ = ' ';
convertToAscii(day, dstDataPtr, 2);
*dstDataPtr++ = ',';
*dstDataPtr++ = ' ';
convertToAscii(year, dstDataPtr, 4);
*dstDataPtr++ = ',';
}
break;
case DATETIME_FORMAT_TS4:
{
// do nothing for date part.
}
break;
case DATETIME_FORMAT_EXTRA_HH:
case DATETIME_FORMAT_EXTRA_HH24:
case DATETIME_FORMAT_EXTRA_HH12:
{
char hour = *srcData++;
if ( DATETIME_FORMAT_EXTRA_HH12 == format )
{
if (hour > 12)
hour = hour - 12;
}
convertToAscii(hour, dstDataPtr, 2);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_MI:
{
char minute = *(srcData+1);
convertToAscii(minute, dstDataPtr, 2);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_SS:
{
char second = *(srcData+2);
convertToAscii(second, dstDataPtr, 2);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_YYYY:
case DATETIME_FORMAT_EXTRA_YYY:
case DATETIME_FORMAT_EXTRA_YY:
case DATETIME_FORMAT_EXTRA_Y:
{
UInt32 nw = 4; //DATETIME_FORMAT_EXTRA_YYYY
if ( DATETIME_FORMAT_EXTRA_YYY == format )
{
nw = 3;
year = year % 1000;
}
else if ( DATETIME_FORMAT_EXTRA_YY == format )
{
nw = 2;
year = year % 100;
}
else if ( DATETIME_FORMAT_EXTRA_Y == format )
{
nw = 1;
year = year % 10;
}
convertToAscii(year, dstDataPtr, nw);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_CC:
{
year = (year+99)/100;
convertToAscii(year, dstDataPtr,2);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_MON:
case DATETIME_FORMAT_EXTRA_MM:
{
if (DATETIME_FORMAT_EXTRA_MM == format)
convertToAscii(month, dstDataPtr,2);
else if (DATETIME_FORMAT_EXTRA_MON == format)
{
if (0 == month)
return -1;
convertMonthToStr(month, dstDataPtr, 3);
}
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_DY:
case DATETIME_FORMAT_EXTRA_DAY:
case DATETIME_FORMAT_EXTRA_D:
{
Int64 interval = getTotalDays(year, month, day);
short dayofweek = (short)(((interval + 1) % 7) + 1);
if (DATETIME_FORMAT_EXTRA_D == format)
{
convertToAscii(dayofweek,dstDataPtr,1);
}
else if (DATETIME_FORMAT_EXTRA_DAY == format
|| DATETIME_FORMAT_EXTRA_DY == format)
{
if (0 == day)
return -1;
//SUNDAY or SUN
NABoolean bAbbr = (DATETIME_FORMAT_EXTRA_DY == format ? TRUE:FALSE);
UInt32 width = 9;
if (bAbbr)
width = 3;
convertDayOfWeekToStr(dayofweek, dstDataPtr, bAbbr, width);
}
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_DD:
{
convertToAscii(day, dstDataPtr, 2);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_DDD:
{
int dayofyear = 0;
if( day )
dayofyear = Date2Julian(year,month,day)-Date2Julian(year,1,1)+1;
convertToAscii(dayofyear,dstDataPtr,3);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_W:
{
int weekofmonth = 0;
if (day)
weekofmonth = (day-1)/7+1;
convertToAscii(weekofmonth,dstDataPtr,1);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_WW:
{
//same with built-in function week
int weekofmonth = 0;
if ( day )
{
Int64 interval = getTotalDays(year, 1, 1);
int dayofweek = (int)(((interval + 1) % 7) + 1);
int dayofyear = Date2Julian(year,month,day)-Date2Julian(year,1,1)+1;
weekofmonth = (dayofyear-1+dayofweek-1)/7+1;
}
convertToAscii(weekofmonth,dstDataPtr,2);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_J:
{
int julianday = Date2Julian(year,month,day);
convertToAscii(julianday,dstDataPtr,7);
return (dstDataPtr - dstData);
}
break;
case DATETIME_FORMAT_EXTRA_Q:
{
if (month)
{
month = (month-1)/3+1;
}
convertToAscii(month,dstDataPtr,1);
return (dstDataPtr - dstData);
}
break;
default:
return -1;
}
// Add a delimiter between the date and time portion if required.
//
if (field > startField && field <= endField)
{
switch (format) {
case DATETIME_FORMAT_TS1:
{
}
break;
case DATETIME_FORMAT_TS2:
case DATETIME_FORMAT_TS5:
{
*dstDataPtr++ = ':';
}
break;
default:
{
*dstDataPtr++ = ' ';
}
break;
}
}
// Format the Time portion in the proper format.
//
for (; field <= endField; field++) {
switch (field) {
case REC_DATE_HOUR: {
char hour = militaryHour = *srcData++;
// USA format uses AM|PM format.
//
if ((format == DATETIME_FORMAT_USA) ||
(format == DATETIME_FORMAT_TS7)) {
if (hour < 1)
hour += 12;
else if (hour > 12)
hour -= 12;
}
convertToAscii(hour, dstDataPtr, 2);
if (endField > REC_DATE_HOUR) {
if ((format == DATETIME_FORMAT_EUROPEAN) ||
(format == DATETIME_FORMAT_TS10))
*dstDataPtr++ = '.';
else if (format != DATETIME_FORMAT_TS1)
*dstDataPtr++ = ':';
}
break;
}
case REC_DATE_MINUTE: {
char minute = *srcData++;
convertToAscii(minute, dstDataPtr, 2);
if (endField > REC_DATE_MINUTE) {
if (format == DATETIME_FORMAT_TS9)
return (dstDataPtr - dstData);
else if ((format == DATETIME_FORMAT_EUROPEAN) ||
(format == DATETIME_FORMAT_TS10))
*dstDataPtr++ = '.';
else if (format != DATETIME_FORMAT_TS1)
*dstDataPtr++ = ':';
}
break;
}
case REC_DATE_SECOND: {
char second = *srcData++;
convertToAscii(second, dstDataPtr, 2);
// If there is a fraction portion of the second field and there
// is room in the destination string, format as much of the
// fraction as possible.
//
Int32 fractionPrecision = getScale();
Lng32 fraction;
if (fractionPrecision > 0) {
// dstPrecision is the available space in the destination
// string (minus 1 for delimiter)
//
Lng32 dstPrecision = dstLen - minReqDstLen - 1;
// Get the fraction value.
//
str_cpy_all((char *) &fraction, srcData, sizeof(fraction));
// If it won't all fit, scale it down so it will.
//
if(dstPrecision < fractionPrecision) {
fraction = scaleFraction(fractionPrecision, fraction, dstPrecision);
fractionPrecision = dstPrecision;
}
}
// If we still have a fraction precision left, format it into
// the result string.
//
if(fractionPrecision > 0) {
if (format == DATETIME_FORMAT_USA2)
{
*dstDataPtr++ = ':';
convertToAscii(fraction, dstDataPtr, 2);
}
else
{
*dstDataPtr++ = '.';
convertToAscii(fraction, dstDataPtr, fractionPrecision);
}
}
break;
}
default:
return -1;
}
}
// If the format is USA and there is an HOUR field, add AM or PM.
//
if (((format == DATETIME_FORMAT_USA) ||
(format == DATETIME_FORMAT_TS7)) &&
startField <= REC_DATE_HOUR &&
endField >= REC_DATE_HOUR) {
if (militaryHour < 12) {
str_cpy_all(dstDataPtr, " AM", 3);
} else {
str_cpy_all(dstDataPtr, " PM", 3);
}
dstDataPtr += 3;
}
// if format includes time field but source is a DATE datatype, extend
// the returned string with zeroes
if (isTimestampFormat(format))
{
if (format == DATETIME_FORMAT_TS1)
{
}
else if ((format == DATETIME_FORMAT_TS2) ||
(format == DATETIME_FORMAT_TS5))
{
*dstDataPtr = ':';
dstDataPtr++;
}
else
{
*dstDataPtr = ' ';
dstDataPtr++;
}
if (format == DATETIME_FORMAT_TS1)
{
str_cpy_all(dstDataPtr, "000000", 6);
dstDataPtr += 6;
}
else if (format == DATETIME_FORMAT_EUROPEAN)
{
str_cpy_all(dstDataPtr, "00.00.00", 8);
dstDataPtr += 8;
}
else
{
str_cpy_all(dstDataPtr, "00:00:00", 8);
dstDataPtr += 8;
}
}
// Return the actual number of bytes formatted.
//
return dstDataPtr - dstData;
}
// convNumericTimeToASCII() ============================================
// This method is used to convert the given numeric time value to an ASCII
// string in the provided format. It is based on special1 behavior.
//
// Input numeric value is an Int64.
//
// DATETIME_FORMAT_TIME1: 99:99:99:99
// an 8-digit number is broken up into 2 digit
// parts separated by : (colon)
// Ex, 12345 will be returned as 00:01:23:45
// If negative number, sign is ignored.
//
// DATETIME_FORMAT_TIME2: -99:99:99:99
// an 8-digit number is broken up into 2 digit
// parts separated by : (colon)
// Ex, -12345 will be returned as -00:01:23:45
//
// If value is less than -long max or greater than + long max, the
// result will be "**********".
//
// DATETIME_FORMAT_TIME_STR: format is in formatStr. Not yet supported.
//
// =====================================================================
//
Lng32
ExpDatetime::convNumericTimeToASCII(char *srcData,
char *dstData,
Lng32 dstLen,
Int32 format,
char *formatStr,
CollHeap *heap,
ComDiagsArea** diagsArea)
{
if ((format != DATETIME_FORMAT_NUM1) &&
(format != DATETIME_FORMAT_NUM2))
return -1;
if ((format == DATETIME_FORMAT_NUM1) &&
(dstLen < 11))
return -1;
if ((format == DATETIME_FORMAT_NUM2) &&
(dstLen < 12))
return -1;
Int64 temp = *(Int64*)srcData;
NABoolean negative = FALSE;
if (temp < 0)
{
// cannot convert negative number with NUM1 format
if (format == DATETIME_FORMAT_NUM1)
{
raiseDateConvErrorWithSrcDataNumeric(diagsArea,temp,heap);
return -1;
}
temp = -temp;
if (format == DATETIME_FORMAT_NUM2)
{
negative = TRUE;
}
}
NABoolean overflow = FALSE;
if ((temp > INT_MAX) ||
(temp < -INT_MAX))
{
overflow = TRUE;
}
if (overflow)
{
str_pad(dstData, dstLen, '*');
}
else
{
Lng32 part1, part2, part3, part4;
part4 = (Lng32)(temp - (temp/100)*100);
temp = temp/100;
part3 = (Lng32)(temp - (temp/100)*100);
temp = temp/100;
part2 = (Lng32)(temp - (temp/100)*100);
temp = temp/100;
part1 = (Lng32)(temp - (temp/100)*100);
temp = temp/100;
// if more digits left in input, error out.
if (temp > 0)
{
raiseDateConvErrorWithSrcDataNumeric(diagsArea,temp,heap);
return -1;
}
if (format == DATETIME_FORMAT_NUM2)
{
if (negative)
str_sprintf(dstData, "-%02d:%02d:%02d:%02d", part1, part2, part3, part4);
else
str_sprintf(dstData, " %02d:%02d:%02d:%02d", part1, part2, part3, part4);
}
else
str_sprintf(dstData, "%02d:%02d:%02d:%02d", part1, part2, part3, part4);
}
// number of bytes formatted.
return 11;
}
static const UInt32 maxFieldLen[] = { 4, 2, 2, 2, 2, 2, 6 };
short ExpDatetime::getDisplaySize(Lng32 datetimeCode,
short fractionPrecision)
{
rec_datetime_field startField, endField;
getDatetimeFields(datetimeCode, startField, endField);
Int32 field = startField - REC_DATE_YEAR;
size_t displayLength = maxFieldLen[field];
while (field < (endField - REC_DATE_YEAR))
displayLength += 1 /* for separator */ + maxFieldLen[++field];
if (fractionPrecision > 0)
{
if (startField == REC_DATE_FRACTION_MP)
{
displayLength = fractionPrecision;
}
else
{
displayLength += 1 /* for separator */ + fractionPrecision;
}
}
return displayLength;
}
short ExpDatetime::convAsciiDatetimeToASCII(char *srcData,
Lng32 srcPrecision,
Lng32 srcScale,
Lng32 srcLen,
char *dstData,
Lng32 dstLen,
Int32 format,
CollHeap *heap,
ComDiagsArea** diagsArea)
{
short rc = 0;
SimpleType tempST(REC_DATETIME, 12,
srcScale, srcPrecision,
ExpTupleDesc::SQLMX_FORMAT,
0, 0, 0, 0, Attributes::NO_DEFAULT, 0);
char tempDTBuf[12]; // max length for an internal datetime value.
ExpDatetime &tempDT = (ExpDatetime&)tempST;
rc =
tempDT.convAsciiToDatetime
(srcData, srcLen, tempDTBuf, 12, DATETIME_FORMAT_NONE, heap, diagsArea, 0);
if (rc)
return rc;
rc =
tempDT.convDatetimeToASCII(tempDTBuf, dstData, dstLen, format, NULL,
heap, diagsArea);
if (rc < 0)
return rc;
return 0;
}
// getFieldName() ====================================================
// This static helper function of the ExpDatetime class returns the
// string (char *) value of the given datetime field. For example, if
// given the value REC_DATE_MONTH, this method returns the string
// "MONTH". This function is used by getDefaultStringValue (see above).
//
// This function was added as part of the MP Datetime Compatibility
// project.
// ===================================================================
//
static
const char*
getFieldName(rec_datetime_field field)
{
switch (field) {
case REC_DATE_YEAR:
return "YEAR";
case REC_DATE_MONTH:
return "MONTH";
case REC_DATE_DAY:
return "DAY";
case REC_DATE_HOUR:
return "HOUR";
case REC_DATE_MINUTE:
return "MINUTE";
case REC_DATE_SECOND:
return "SECOND";
default:
return NULL;
}
}
// getDefaultStringValue() ===========================================
// This method constructs a string (char *) value representing the
// default value for a datetime type. This method is used to construct
// values for columns that have been added to a table through an ALTER
// TABLE statement. When a column is added to a table, the existing
// records are not immediately updated. Rather, when these records
// are selected, a default value for the column is constructed. As
// described in the SQL/MP reference manual (ALTER TABLE, p..A-37) the
// default value used for all datetime values is based on the
// TimeStamp: '0001-01-01:12:00:00.00000'. The default value is the
// appropriate range of fields taken from the default TimeStamp. For
// the non-standard datetime types, the default value must also
// include the datetime qualifier. For example the default value for
// a non-standard datetime of MONTH TO DAY would be: "DATETIME '01-01'
// MONTH TO DAY".
//
// This method is only called from the generator
// (ExpGenerator::addDefaultValue() in generator/GenExpGenerator.cpp)
//
// This method was added as part of the MP Datetime Compatibility
// project.
// ===================================================================
//
char *
ExpDatetime::getDefaultStringValue(CollHeap *heap)
{
rec_datetime_field startField;
rec_datetime_field endField;
char *ptr;
Int32 len;
// Get the start and end fields for this datetime type.
//
if(getDatetimeFields(getPrecision(),
startField,
endField) != 0) {
return NULL;
}
char buffer[MAX_DATETIME_STRING_LEN];
ptr = buffer;
// Construct the type name
//
const char *dateKW = "DATE '";
const char *timeKW = "TIME '";
const char *timeStampKW = "TIMESTAMP '";
const char *datetimeKW = "DATETIME '";
switch (getPrecision()) {
case REC_DTCODE_DATE:
len = str_len(dateKW);
str_cpy_all(ptr,dateKW, len);
break;
case REC_DTCODE_TIME:
len = str_len(timeKW);
str_cpy_all(ptr,timeKW, len);
break;
case REC_DTCODE_TIMESTAMP:
len = str_len(timeStampKW);
str_cpy_all(ptr,timeStampKW, len);
break;
default:
len = str_len(datetimeKW);
str_cpy_all(ptr,datetimeKW, len);
break;
}
static const char * const defValues[] = {"", "0001", "01", "01", "12", "00", "00"};
static const char delims[6] = {'x','-','-',':',':',':'};
// Construct the default value string, based on the start and end
// fields.
//
for (Int32 field = startField; field <= endField; field++) {
Int32 flen = str_len(defValues[field]);
str_cpy_all(ptr+len, defValues[field], flen);
len += flen;
if(field != endField) {
ptr[len++] = delims[field];
}
}
ptr[len++] = '\'';
// Construct the datetime qualifier if needed.
//
if(getPrecision() > REC_DTCODE_TIMESTAMP) {
ptr[len++] = ' ';
Int32 flen = str_len(getFieldName(startField));
str_cpy_all(ptr+len, getFieldName(startField), flen);
len += flen;
if(startField != endField) {
flen = str_len(" TO ");
str_cpy_all(ptr+len, " TO ", flen);
len += flen;
flen = str_len(getFieldName(endField));
str_cpy_all(ptr+len, getFieldName(endField), flen);
len += flen;
}
}
ptr[len++] = ';';
ptr[len++] = '\0';
// Allocate space for the default string (including NULL char) and
// copy from local buffer
//
ptr = new (heap) char[str_len(buffer) + 1];
str_cpy_all(ptr, buffer, str_len(buffer) + 1);
return ptr;
}