mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 17:23:03 +01:00
* Add FORMAT clause to convert datetime types to string and vice versa * Add tests for FORMAT clause * Fixes after review * Change TZD to TZR * Change inline variables back to static * Add README documentation * Add ability to use " in raw string and ... Use session timezone if timezone is not specified. Add ability to use + sign in timezone offset. Add truncating string exception. * Move util methods from BOOST_AUTO_TEST_SUITE * Switch back to inline variables * Consider charset in the format string * Add ability to write patterns without separators * Use printf to add extra zeros Also add extra zeros to the year patterns. * Replace template exception with a plain function * Clean code after review * Fix bug with TZH:TZM when TZH is 0 * Add TZR to STRING to DATE --------- Co-authored-by: Artyom Ivanov <artyom.ivanov@red-soft.ru>
This commit is contained in:
parent
1fe1abd366
commit
897ac0c650
95
doc/README.cast.format.md
Normal file
95
doc/README.cast.format.md
Normal file
@ -0,0 +1,95 @@
|
||||
## 1. DATETIME TO STRING
|
||||
|
||||
The following flags are currently implemented for datetime to string conversion:
|
||||
| Format Pattern | Description |
|
||||
| -------------- | ----------- |
|
||||
| YEAR | Year (1 - 9999) |
|
||||
| YYYY | Last 4 digits of Year (0001 - 9999) |
|
||||
| YYY | Last 3 digits of Year (000 - 999) |
|
||||
| YY | Last 2 digits of Year (00 - 99) |
|
||||
| Y | Last 1 digits of Year (0 - 9) |
|
||||
| Q | Quarter of the Year (1 - 4) |
|
||||
| MM | Month (01 - 12) |
|
||||
| MON | Short Month name (Apr) |
|
||||
| MONTH | Full Month name (APRIL) |
|
||||
| RM | Roman representation of the Month (I - XII) |
|
||||
| WW | Week of the Year (01 - 53) |
|
||||
| W | Week of the Month (1 - 5) |
|
||||
| D | Day of the Week (1 - 7) |
|
||||
| DAY | Full name of the Day (MONDAY) |
|
||||
| DD | Day of the Month (01 - 31) |
|
||||
| DDD | Day of the Year (001 - 366) |
|
||||
| DY | Short name of the Day (Mon) |
|
||||
| J | Julian Day (number of days since January 1, 4712 BC) |
|
||||
| HH / HH12 | Hour of the Day (01 - 12) with period (AM, PM) |
|
||||
| HH24 | Hour of the Day (00 - 23) |
|
||||
| MI | Minutes (00 - 59) |
|
||||
| SS | Seconds (00 - 59) |
|
||||
| SSSSS | Seconds after midnight (0 - 86399) |
|
||||
| FF1 - FF9 | Fractional seconds with the specified accuracy |
|
||||
| TZH | Time zone in Hours (-14 - 14) |
|
||||
| TZM | Time zone in Minutes (00 - 59) |
|
||||
| TZR | Time zone Name |
|
||||
|
||||
The dividers are:
|
||||
| Dividers |
|
||||
| ------------- |
|
||||
| . |
|
||||
| / |
|
||||
| , |
|
||||
| ; |
|
||||
| : |
|
||||
| 'space' |
|
||||
| - |
|
||||
|
||||
Patterns can be used without any dividers:
|
||||
```
|
||||
SELECT CAST(CURRENT_TIMESTAMP AS VARCHAR(50) FORMAT 'YEARMMDD HH24MISS') FROM RDB$DATABASE;
|
||||
=========================
|
||||
20230719 161757
|
||||
```
|
||||
However, be careful with patterns like `DDDDD`, it will be interpreted as `DDD` + `DD`.
|
||||
|
||||
It is possible to insert raw text into a format string with `""`: `... FORMAT '"Today is" DAY'` - Today is MONDAY. To add `"` in output raw string use `\"` (to print `\` use `\\`).
|
||||
Also the format is case-insensitive, so `YYYY-MM` == `yyyy-mm`.
|
||||
Example:
|
||||
```
|
||||
SELECT CAST(CURRENT_TIMESTAMP AS VARCHAR(45) FORMAT 'DD.MM.YEAR HH24:MI:SS "is" J "Julian day"') FROM RDB$DATABASE;
|
||||
=========================
|
||||
14.6.2023 15:41:29 is 2460110 Julian day
|
||||
```
|
||||
|
||||
## 2. STRING TO DATETIME
|
||||
|
||||
The following flags are currently implemented for string to datetime conversion:
|
||||
| Format Pattern | Description |
|
||||
| ------------- | ------------- |
|
||||
| YEAR | Year |
|
||||
| YYYY | Last 4 digits of Year |
|
||||
| YYY | Last 3 digits of Year |
|
||||
| YY | Last 2 digits of Year |
|
||||
| Y | Last 1 digits of Year |
|
||||
| MM | Month (1 - 12) |
|
||||
| MON | Short Month name (Apr) |
|
||||
| MONTH | Full Month name (APRIL) |
|
||||
| RM | Roman representation of the Month (I - XII) |
|
||||
| DD | Day of the Month (1 - 31) |
|
||||
| J | Julian Day (number of days since January 1, 4712 BC) |
|
||||
| HH / HH12 | Hour of the Day (1 - 12) with period (AM, PM) |
|
||||
| HH24 | Hour of the Day (0 - 23) |
|
||||
| MI | Minutes (0 - 59) |
|
||||
| SS | Seconds (0 - 59) |
|
||||
| SSSSS | Seconds after midnight (0 - 86399) |
|
||||
| FF1 - FF4 | Fractional seconds with the specified accuracy |
|
||||
| TZH | Time zone in Hours (-14 - 14) |
|
||||
| TZM | Time zone in Minutes (0 - 59) |
|
||||
| TZR | Time zone Name |
|
||||
|
||||
Dividers are the same as for datetime to string conversion and can also be omitted.
|
||||
|
||||
Example:
|
||||
```
|
||||
SELECT CAST('2000.12.08 12:35:30.5000' AS TIMESTAMP FORMAT 'YEAR.MM.DD HH24:MI:SS.FF4') FROM RDB$DATABASE;
|
||||
=====================
|
||||
2000-12-08 12:35:30.5000
|
||||
```
|
@ -222,6 +222,7 @@ PARSER_TOKEN(TOK_FLOOR, "FLOOR", true)
|
||||
PARSER_TOKEN(TOK_FOLLOWING, "FOLLOWING", true)
|
||||
PARSER_TOKEN(TOK_FOR, "FOR", false)
|
||||
PARSER_TOKEN(TOK_FOREIGN, "FOREIGN", false)
|
||||
PARSER_TOKEN(TOK_FORMAT, "FORMAT", true)
|
||||
PARSER_TOKEN(TOK_FREE_IT, "FREE_IT", true)
|
||||
PARSER_TOKEN(TOK_FROM, "FROM", false)
|
||||
PARSER_TOKEN(TOK_FULL, "FULL", false)
|
||||
|
@ -80,7 +80,6 @@ namespace
|
||||
|
||||
static const TimeZoneDesc* getDesc(USHORT timeZone);
|
||||
static inline bool isOffset(USHORT timeZone);
|
||||
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm);
|
||||
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone);
|
||||
static inline USHORT displacementToOffsetZone(SSHORT displacement);
|
||||
static int parseNumber(const char*& p, const char* end);
|
||||
@ -634,6 +633,20 @@ void TimeZoneUtil::extractOffset(const ISC_TIME_TZ& timeTz, SSHORT* offset)
|
||||
extractOffset(tsTz, offset);
|
||||
}
|
||||
|
||||
// Makes a time zone id from offsets.
|
||||
USHORT TimeZoneUtil::makeFromOffset(int sign, unsigned tzh, unsigned tzm)
|
||||
{
|
||||
if (!TimeZoneUtil::isValidOffset(sign, tzh, tzm))
|
||||
{
|
||||
string str;
|
||||
str.printf("%s%02u:%02u", (sign == -1 ? "-" : "+"), tzh, tzm);
|
||||
status_exception::raise(Arg::Gds(isc_invalid_timezone_offset) << str);
|
||||
}
|
||||
|
||||
return (USHORT)displacementToOffsetZone((tzh * 60 + tzm) * sign);
|
||||
}
|
||||
|
||||
|
||||
// Converts a time from local to UTC.
|
||||
void TimeZoneUtil::localTimeToUtc(ISC_TIME& time, ISC_USHORT timeZone)
|
||||
{
|
||||
@ -1156,19 +1169,6 @@ static inline bool isOffset(USHORT timeZone)
|
||||
return timeZone <= ONE_DAY * 2;
|
||||
}
|
||||
|
||||
// Makes a time zone id from offsets.
|
||||
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm)
|
||||
{
|
||||
if (!TimeZoneUtil::isValidOffset(sign, tzh, tzm))
|
||||
{
|
||||
string str;
|
||||
str.printf("%s%02u:%02u", (sign == -1 ? "-" : "+"), tzh, tzm);
|
||||
status_exception::raise(Arg::Gds(isc_invalid_timezone_offset) << str);
|
||||
}
|
||||
|
||||
return (USHORT)displacementToOffsetZone((tzh * 60 + tzm) * sign);
|
||||
}
|
||||
|
||||
// Gets the displacement from a offset-based time zone id.
|
||||
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone)
|
||||
{
|
||||
|
@ -97,6 +97,8 @@ public:
|
||||
static void extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, SSHORT* offset);
|
||||
static void extractOffset(const ISC_TIME_TZ& timeTz, SSHORT* offset);
|
||||
|
||||
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm);
|
||||
|
||||
static void localTimeToUtc(ISC_TIME& time, ISC_USHORT timeZone);
|
||||
static void localTimeToUtc(ISC_TIME_TZ& timeTz);
|
||||
|
||||
|
@ -337,6 +337,80 @@ void NoThrowTimeStamp::round_time(ISC_TIME &ntime, const int precision)
|
||||
ntime -= (ntime % period);
|
||||
}
|
||||
|
||||
int NoThrowTimeStamp::convertGregorianDateToWeekDate(const struct tm& times)
|
||||
{
|
||||
// Algorithm for Converting Gregorian Dates to ISO 8601 Week Date by Rick McCarty, 1999
|
||||
// http://personal.ecu.edu/mccartyr/ISOwdALG.txt
|
||||
|
||||
const int y = times.tm_year + 1900;
|
||||
const int dayOfYearNumber = times.tm_yday + 1;
|
||||
|
||||
// Find the jan1Weekday for y (Monday=1, Sunday=7)
|
||||
const int yy = (y - 1) % 100;
|
||||
const int c = (y - 1) - yy;
|
||||
const int g = yy + yy / 4;
|
||||
const int jan1Weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
|
||||
|
||||
// Find the weekday for y m d
|
||||
const int h = dayOfYearNumber + (jan1Weekday - 1);
|
||||
const int weekday = 1 + ((h - 1) % 7);
|
||||
|
||||
// Find if y m d falls in yearNumber y-1, weekNumber 52 or 53
|
||||
int yearNumber, weekNumber;
|
||||
|
||||
if ((dayOfYearNumber <= (8 - jan1Weekday)) && (jan1Weekday > 4))
|
||||
{
|
||||
yearNumber = y - 1;
|
||||
weekNumber = ((jan1Weekday == 5) || ((jan1Weekday == 6) &&
|
||||
isLeapYear(yearNumber))) ? 53 : 52;
|
||||
}
|
||||
else
|
||||
{
|
||||
yearNumber = y;
|
||||
|
||||
// Find if y m d falls in yearNumber y+1, weekNumber 1
|
||||
int i = isLeapYear(y) ? 366 : 365;
|
||||
|
||||
if ((i - dayOfYearNumber) < (4 - weekday))
|
||||
{
|
||||
yearNumber = y + 1;
|
||||
weekNumber = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Find if y m d falls in yearNumber y, weekNumber 1 through 53
|
||||
if (yearNumber == y)
|
||||
{
|
||||
int j = dayOfYearNumber + (7 - weekday) + (jan1Weekday - 1);
|
||||
weekNumber = j / 7;
|
||||
if (jan1Weekday > 4)
|
||||
weekNumber--;
|
||||
}
|
||||
|
||||
return weekNumber;
|
||||
}
|
||||
|
||||
int NoThrowTimeStamp::convertGregorianDateToJulianDate(int year, int month, int day)
|
||||
{
|
||||
int jdn = (1461 * (year + 4800 + (month - 14)/12))/4 + (367 * (month - 2 - 12 * ((month - 14)/12)))
|
||||
/ 12 - (3 * ((year + 4900 + (month - 14)/12)/100))/4 + day - 32075;
|
||||
return jdn;
|
||||
}
|
||||
|
||||
void NoThrowTimeStamp::convertJulianDateToGregorianDate(int jdn, int& outYear, int& outMonth, int& outDay)
|
||||
{
|
||||
int a = jdn + 32044;
|
||||
int b = (4 * a +3 ) / 146097;
|
||||
int c = a - (146097 * b) / 4;
|
||||
int d = (4 * c + 3) / 1461;
|
||||
int e = c - (1461 * d) / 4;
|
||||
int m = (5 * e + 2) / 153;
|
||||
|
||||
outDay = e - (153 * m + 2) / 5 + 1;
|
||||
outMonth = m + 3 - 12 * (m / 10);
|
||||
outYear = 100 * b + d - 4800 + (m / 10);
|
||||
}
|
||||
|
||||
// Encode timestamp from UNIX datetime structure
|
||||
void NoThrowTimeStamp::encode(const struct tm* times, int fractions)
|
||||
{
|
||||
|
@ -168,6 +168,10 @@ public:
|
||||
static void add10msec(ISC_TIMESTAMP* v, SINT64 msec, SINT64 multiplier);
|
||||
static void round_time(ISC_TIME& ntime, const int precision);
|
||||
|
||||
static int convertGregorianDateToWeekDate(const struct tm& times);
|
||||
static int convertGregorianDateToJulianDate(int year, int month, int day);
|
||||
static void convertJulianDateToGregorianDate(int jdn, int& outYear, int& outMonth, int& outDay);
|
||||
|
||||
static inline bool isLeapYear(const int year) noexcept
|
||||
{
|
||||
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||
|
@ -971,7 +971,7 @@ const int HIGH_WORD = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const TEXT FB_SHORT_MONTHS[][4] =
|
||||
inline const TEXT FB_SHORT_MONTHS[][4] =
|
||||
{
|
||||
"Jan", "Feb", "Mar",
|
||||
"Apr", "May", "Jun",
|
||||
@ -980,7 +980,7 @@ static const TEXT FB_SHORT_MONTHS[][4] =
|
||||
"\0"
|
||||
};
|
||||
|
||||
static const TEXT* const FB_LONG_MONTHS_UPPER[] =
|
||||
inline const TEXT* const FB_LONG_MONTHS_UPPER[] =
|
||||
{
|
||||
"JANUARY",
|
||||
"FEBRUARY",
|
||||
@ -997,6 +997,32 @@ static const TEXT* const FB_LONG_MONTHS_UPPER[] =
|
||||
0
|
||||
};
|
||||
|
||||
// Starts with SUNDAY cuz tm.tm_wday starts with it
|
||||
inline const TEXT FB_SHORT_DAYS[][4] =
|
||||
{
|
||||
"Sun",
|
||||
"Mon",
|
||||
"Tue",
|
||||
"Wed",
|
||||
"Thu",
|
||||
"Fri",
|
||||
"Sat",
|
||||
"\0"
|
||||
};
|
||||
|
||||
// Starts with SUNDAY cuz tm.tm_wday starts with it
|
||||
inline const TEXT* const FB_LONG_DAYS_UPPER[] =
|
||||
{
|
||||
"SUNDAY",
|
||||
"MONDAY",
|
||||
"TUESDAY",
|
||||
"WEDNESDAY",
|
||||
"THURSDAY",
|
||||
"FRIDAY",
|
||||
"SATURDAY",
|
||||
"\0"
|
||||
};
|
||||
|
||||
const FB_SIZE_T FB_MAX_SIZEOF = ~FB_SIZE_T(0); // Assume FB_SIZE_T is unsigned
|
||||
|
||||
inline FB_SIZE_T fb_strlen(const char* str)
|
||||
|
1107
src/common/cvt.cpp
1107
src/common/cvt.cpp
File diff suppressed because it is too large
Load Diff
@ -108,5 +108,7 @@ SQUAD CVT_get_quad(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction);
|
||||
void CVT_string_to_datetime(const dsc*, ISC_TIMESTAMP_TZ*, bool*, const Firebird::EXPECT_DATETIME,
|
||||
bool, Firebird::Callbacks*);
|
||||
const UCHAR* CVT_get_bytes(const dsc*, unsigned&);
|
||||
Firebird::string CVT_datetime_to_format_string(const dsc* desc, const Firebird::string& format, Firebird::Callbacks* cb);
|
||||
ISC_TIMESTAMP_TZ CVT_string_to_format_datetime(const dsc* desc, const Firebird::string& format, Firebird::Callbacks* cb);
|
||||
|
||||
#endif //COMMON_CVT_H
|
||||
|
321
src/common/tests/CvtTest.cpp
Normal file
321
src/common/tests/CvtTest.cpp
Normal file
@ -0,0 +1,321 @@
|
||||
#include "boost/test/unit_test.hpp"
|
||||
#include "../common/tests/CvtTestUtils.h"
|
||||
#include "../jrd/cvt_proto.h"
|
||||
|
||||
using namespace Firebird;
|
||||
using namespace Jrd;
|
||||
using namespace CvtTestUtils;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(CVTSuite)
|
||||
BOOST_AUTO_TEST_SUITE(CVTDatetimeFormat)
|
||||
|
||||
static void errFunc(const Firebird::Arg::StatusVector& v)
|
||||
{
|
||||
v.raise();
|
||||
}
|
||||
|
||||
CVTCallback cb(errFunc);
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(CVTDatetimeToFormatString)
|
||||
|
||||
template<typename T>
|
||||
static void testCVTDatetimeToFormatString(T date, const string& format, const string& expected, Callbacks& cb)
|
||||
{
|
||||
dsc desc;
|
||||
desc.dsc_dtype = getDSCTypeFromDateType<T>();
|
||||
desc.dsc_length = sizeof(T);
|
||||
desc.dsc_address = (UCHAR*) &date;
|
||||
desc.dsc_scale = 0;
|
||||
|
||||
string result = CVT_datetime_to_format_string(&desc, format, &cb);
|
||||
BOOST_TEST(result == expected, "\nRESULT: " << result.c_str() << "\nEXPECTED: " << expected.c_str());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(FunctionalTest)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_DATE)
|
||||
{
|
||||
testCVTDatetimeToFormatString(createDate(1, 1, 1), "YEAR.YYYY.YYY.YY.Y", "1.0001.001.01.1", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1234, 1, 1), "YEAR.YYYY.YYY.YY.Y", "1234.1234.234.34.4", cb);
|
||||
testCVTDatetimeToFormatString(createDate(9999, 1, 1), "YEAR.YYYY.YYY.YY.Y", "9999.9999.999.99.9", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createDate(1, 1, 1), "YEAR.YYYY.YYY.YY.Y", "1.0001.001.01.1", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1234, 1, 1), "YEAR.YYYY.YYY.YY.Y", "1234.1234.234.34.4", cb);
|
||||
testCVTDatetimeToFormatString(createDate(9999, 1, 1), "YEAR.YYYY.YYY.YY.Y", "9999.9999.999.99.9", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createDate(1, 1, 1), "Q", "1", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 4, 1), "Q", "2", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 7, 1), "Q", "3", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 10, 1), "Q", "4", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createDate(1, 1, 1), "MM:RM-MON/MONTH", "01:I-Jan/JANUARY", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 6, 1), "MM-RM.MON;MONTH", "06-VI.Jun;JUNE", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 12, 1), "MM,RM.MON:MONTH", "12,XII.Dec:DECEMBER", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createDate(1, 1, 1), "WW/W", "01/1", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 6, 15), "WW-W", "24-3", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 12, 30), "WW.W", "52.5", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createDate(2023, 6, 4), "D;DAY-DY", "1;SUNDAY-Sun", cb);
|
||||
testCVTDatetimeToFormatString(createDate(2023, 6, 7), "D.DAY,DY", "4.WEDNESDAY,Wed", cb);
|
||||
testCVTDatetimeToFormatString(createDate(2023, 6, 10), "D DAY DY", "7 SATURDAY Sat", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createDate(1, 1, 1), "DDD", "001", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 1, 12), "DDD", "012", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 6, 15), "DDD", "166", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1, 12, 31), "DDD", "365", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createDate(1, 1, 1), "J", "1721426", cb);
|
||||
testCVTDatetimeToFormatString(createDate(2000, 12, 8), "J", "2451887", cb);
|
||||
testCVTDatetimeToFormatString(createDate(9999, 12, 31), "J", "5373484", cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_TIME)
|
||||
{
|
||||
testCVTDatetimeToFormatString(createTime(0, 0, 0), "HH-HH12.HH24,MI/SS SSSSS", "12 AM-12 AM.00,00/00 0", cb);
|
||||
testCVTDatetimeToFormatString(createTime(12, 35, 15), "HH.HH12:HH24;MI-SS/SSSSS", "12 PM.12 PM:12;35-15/45315", cb);
|
||||
testCVTDatetimeToFormatString(createTime(23, 59, 59), " HH - HH12 . HH24 , MI / SS SSSSS ", " 11 PM - 11 PM . 23 , 59 / 59 86399 ", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createTime(0, 0, 0, 1), "FF1.FF2/FF3;FF4:FF5-FF6,FF7-FF8 FF9", "1.10/100;1000:10000-100000,1000000-10000000 100000000", cb);
|
||||
testCVTDatetimeToFormatString(createTime(0, 0, 0, 1000), "FF1.FF2/FF3;FF4:FF5-FF6,FF7-FF8 FF9", "1.10/100;1000:10000-100000,1000000-10000000 100000000", cb);
|
||||
testCVTDatetimeToFormatString(createTime(0, 0, 0, 9999), "FF1.FF2/FF3;FF4:FF5-FF6,FF7-FF8 FF9", "9.99/999;9999:99990-999900,9999000-99990000 999900000", cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_TIMESTAMP)
|
||||
{
|
||||
ISC_TIMESTAMP timestamp = createTimeStamp(1982, 4, 21, 1, 34, 15, 2500);
|
||||
|
||||
testCVTDatetimeToFormatString(timestamp, "YEAR.YYYY.YYY.YY.Y/J", "1982.1982.982.82.2/2445081", cb);
|
||||
testCVTDatetimeToFormatString(timestamp, "Q-MM-RM-MON-MONTH", "2-04-IV-Apr-APRIL", cb);
|
||||
testCVTDatetimeToFormatString(timestamp, "WW,W-D;DAY:DD DDD.DY", "16,3-4;WEDNESDAY:21 111.Wed", cb);
|
||||
testCVTDatetimeToFormatString(timestamp, "HH-HH12-HH24-MI-SS-SSSSS.FF2", "01 AM-01 AM-01-34-15-5655.25", cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_TIME_TZ)
|
||||
{
|
||||
testCVTDatetimeToFormatString(createTimeTZ(15, 35, 59, 0, 900), "HH-HH12-HH24-MI-SS-SSSSS.FF1/TZH/TZM", "03 PM-03 PM-15-35-59-56159.9/+00/00", cb);
|
||||
testCVTDatetimeToFormatString(createTimeTZ(15, 35, 59, 160), "HH24:MI-TZH:TZM", "18:15-+02:40", cb);
|
||||
testCVTDatetimeToFormatString(createTimeTZ(15, 35, 59, -160), "HH24:MI TZH:TZM", "12:55 -02:40", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createTimeTZ(0, 0, 0, 160), "TZM:TZH", "+40:02", cb);
|
||||
testCVTDatetimeToFormatString(createTimeTZ(0, 0, 0, 160), "TZH MI TZM", "+02 40 +40", cb);
|
||||
testCVTDatetimeToFormatString(createTimeTZ(0, 0, 0, -160), "TZH MI TZM", "-02 20 -40", cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_TIMESTAMP_TZ)
|
||||
{
|
||||
ISC_TIMESTAMP_TZ timestampTZ = createTimeStampTZ(1982, 4, 21, 1, 34, 15, 0, 500);
|
||||
|
||||
testCVTDatetimeToFormatString(timestampTZ, "YEAR.YYYY.YYY.YY.Y/J", "1982.1982.982.82.2/2445081", cb);
|
||||
testCVTDatetimeToFormatString(timestampTZ, "Q-MM-RM-MON-MONTH", "2-04-IV-Apr-APRIL", cb);
|
||||
testCVTDatetimeToFormatString(timestampTZ, "WW,W-D;DAY:DD DDD.DY", "16,3-4;WEDNESDAY:21 111.Wed", cb);
|
||||
testCVTDatetimeToFormatString(timestampTZ, "HH-HH12-HH24-MI-SS-SSSSS.FF2/TZH/TZM", "01 AM-01 AM-01-34-15-5655.50/+00/00", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 1, 34, 15, 70), "HH24:MI-TZH:TZM", "02:44-+01:10", cb);
|
||||
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 1, 34, 15, -70), "HH24:MI TZH:TZM", "00:24 -01:10", cb);
|
||||
|
||||
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 0, 0, 0, 160), "TZM:TZH", "+40:02", cb);
|
||||
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 0, 0, 0, 160), "TZH MI TZM", "+02 40 +40", cb);
|
||||
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 0, 0, 0, -160), "TZH MI TZM", "-02 20 -40", cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_SOLID_PATTERNS)
|
||||
{
|
||||
ISC_TIMESTAMP_TZ timestampTZ = createTimeStampTZ(1982, 4, 21, 1, 34, 15, 0, 500);
|
||||
|
||||
testCVTDatetimeToFormatString(timestampTZ, "YEARYYYYYYYYYYJ", "198219821982822445081", cb);
|
||||
testCVTDatetimeToFormatString(timestampTZ, "QMMRMMONMONTH", "204IVAprAPRIL", cb);
|
||||
testCVTDatetimeToFormatString(timestampTZ, "WWWD/DAYDDDDDDY", "1634/WEDNESDAY1111112", cb);
|
||||
testCVTDatetimeToFormatString(timestampTZ, "HHHH12HH24MISSSSSSSFF2TZHTZM", "01 AM01 AM013456551550+0000", cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_RAW_TEXT)
|
||||
{
|
||||
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "YYYY-\"RaW TeXt\"-MON", "1981-RaW TeXt-Jul", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "YYYY-\"Raw Text with \\\"Quotes\\\"\"-MON", "1981-Raw Text with \"Quotes\"-Jul", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "YYYY-\"\\\\\\\"\\\\BS\\\\\\\"\\\\\"-YYYY", "1981-\\\"\\BS\\\"\\-1981", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"Test1\"-Y\"Test2\"", "Test1-1Test2", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"\"-Y\"Test2\"", "-1Test2", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"Test1\"-Y\"\"", "Test1-1", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"\"-Y\"\"", "-1", cb);
|
||||
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"\"\"\"", "", cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // FunctionalTest
|
||||
BOOST_AUTO_TEST_SUITE_END() // CVTDatetimeToFormatString
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(CVTStringToFormatDateTime)
|
||||
|
||||
static void testCVTStringToFormatDateTime(const string& date, const string& format,
|
||||
const ISC_TIMESTAMP_TZ& expected, Callbacks& cb)
|
||||
{
|
||||
string varyingString = "xx";
|
||||
varyingString += date;
|
||||
*(USHORT*) varyingString.data() = varyingString.size() - sizeof(USHORT);
|
||||
|
||||
dsc desc;
|
||||
desc.dsc_dtype = dtype_varying;
|
||||
desc.dsc_length = varyingString.size() + sizeof(USHORT);
|
||||
desc.dsc_address = (UCHAR*) varyingString.data();
|
||||
desc.dsc_scale = 0;
|
||||
|
||||
const ISC_TIMESTAMP_TZ result = CVT_string_to_format_datetime(&desc, format, &cb);
|
||||
|
||||
struct tm resultTimes;
|
||||
memset(&resultTimes, 0, sizeof(resultTimes));
|
||||
int resultFractions;
|
||||
NoThrowTimeStamp::decode_timestamp(result.utc_timestamp, &resultTimes, &resultFractions);
|
||||
SSHORT resultOffset;
|
||||
TimeZoneUtil::extractOffset(result, &resultOffset);
|
||||
|
||||
struct tm expectedTimes;
|
||||
memset(&expectedTimes, 0, sizeof(expectedTimes));
|
||||
int expectedFractions;
|
||||
NoThrowTimeStamp::decode_timestamp(expected.utc_timestamp, &expectedTimes, &expectedFractions);
|
||||
SSHORT expectedOffset;
|
||||
TimeZoneUtil::extractOffset(expected, &expectedOffset);
|
||||
|
||||
bool isEqual = !((bool) memcmp(&resultTimes, &expectedTimes, sizeof(struct tm)))
|
||||
&& resultFractions == expectedFractions && resultOffset == expectedOffset;
|
||||
|
||||
BOOST_TEST(isEqual, "\nRESULT: " << DECOMPOSE_TM_STRUCT(resultTimes, resultFractions, resultOffset)
|
||||
<< "\nEXPECTED: " << DECOMPOSE_TM_STRUCT(expectedTimes, expectedFractions, expectedOffset));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(FunctionalTest)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_DATE)
|
||||
{
|
||||
testCVTStringToFormatDateTime("1", "YEAR", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("1234", "YEAR", createTimeStampTZ(1234, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("9999", "YEAR", createTimeStampTZ(9999, 1, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "YYYY", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("1234", "YYYY", createTimeStampTZ(1234, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("9999", "YYYY", createTimeStampTZ(9999, 1, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "YYY", createTimeStampTZ(2001, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("450", "YYY", createTimeStampTZ(2450, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("451", "YYY", createTimeStampTZ(1451, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("999", "YYY", createTimeStampTZ(1999, 1, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "YY", createTimeStampTZ(2001, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("45", "YY", createTimeStampTZ(2045, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("46", "YY", createTimeStampTZ(1946, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("99", "YY", createTimeStampTZ(1999, 1, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "Y", createTimeStampTZ(2001, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("9", "Y", createTimeStampTZ(2009, 1, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "MM", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("6", "MM", createTimeStampTZ(1, 6, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("12", "MM", createTimeStampTZ(1, 12, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("Jan", "MON", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("Jun", "MON", createTimeStampTZ(1, 6, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("Dec", "MON", createTimeStampTZ(1, 12, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("January", "MONTH", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("June", "MONTH", createTimeStampTZ(1, 6, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("December", "MONTH", createTimeStampTZ(1, 12, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("I", "RM", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("IV", "RM", createTimeStampTZ(1, 4, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("XII", "RM", createTimeStampTZ(1, 12, 1, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "DD", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("15", "DD", createTimeStampTZ(1, 1, 15, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("31", "DD", createTimeStampTZ(1, 1, 31, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("2451887", "J", createTimeStampTZ(2000, 12, 8, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("1721426", "J", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("5373484", "J", createTimeStampTZ(9999, 12, 31, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1:1,1", "YEAR.MM.DD", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("1981-8/13", "YEAR.MM.DD", createTimeStampTZ(1981, 8, 13, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("9999 12;31", "YEAR.MM.DD", createTimeStampTZ(9999, 12, 31, 0, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("25.Jan.25", "YY;MON;DD", createTimeStampTZ(2025, 1, 25, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("./.1981./-8--/13--", " YEAR. -.MM.,,-.DD//", createTimeStampTZ(1981, 8, 13, 0, 0, 0, 0), cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_TIME)
|
||||
{
|
||||
testCVTStringToFormatDateTime("12 AM", "HH", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("1 AM", "HH", createTimeStampTZ(1, 1, 1, 1, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("11 AM", "HH", createTimeStampTZ(1, 1, 1, 11, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("12 PM", "HH", createTimeStampTZ(1, 1, 1, 12, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("1 PM", "HH", createTimeStampTZ(1, 1, 1, 13, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("11 PM", "HH", createTimeStampTZ(1, 1, 1, 23, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("12 AM", "HH12", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("1 AM", "HH12", createTimeStampTZ(1, 1, 1, 1, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("11 AM", "HH12", createTimeStampTZ(1, 1, 1, 11, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("12 PM", "HH12", createTimeStampTZ(1, 1, 1, 12, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("1 PM", "HH12", createTimeStampTZ(1, 1, 1, 13, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("11 PM", "HH12", createTimeStampTZ(1, 1, 1, 23, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("0", "HH24", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("12", "HH24", createTimeStampTZ(1, 1, 1, 12, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("23", "HH24", createTimeStampTZ(1, 1, 1, 23, 0, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("0", "MI", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("30", "MI", createTimeStampTZ(1, 1, 1, 0, 30, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("59", "MI", createTimeStampTZ(1, 1, 1, 0, 59, 0, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("0", "SS", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("30", "SS", createTimeStampTZ(1, 1, 1, 0, 0, 30, 0), cb);
|
||||
testCVTStringToFormatDateTime("59", "SS", createTimeStampTZ(1, 1, 1, 0, 0, 59, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("0", "SSSSS", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
|
||||
testCVTStringToFormatDateTime("45315", "SSSSS", createTimeStampTZ(1, 1, 1, 12, 35, 15, 0), cb);
|
||||
testCVTStringToFormatDateTime("86399", "SSSSS", createTimeStampTZ(1, 1, 1, 23, 59, 59, 0), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "FF1", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1000), cb);
|
||||
testCVTStringToFormatDateTime("5", "FF1", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 5000), cb);
|
||||
testCVTStringToFormatDateTime("9", "FF1", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 9000), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "FF2", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 100), cb);
|
||||
testCVTStringToFormatDateTime("10", "FF2", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1000), cb);
|
||||
testCVTStringToFormatDateTime("50", "FF2", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 5000), cb);
|
||||
testCVTStringToFormatDateTime("99", "FF2", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 9900), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 10), cb);
|
||||
testCVTStringToFormatDateTime("10", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 100), cb);
|
||||
testCVTStringToFormatDateTime("100", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1000), cb);
|
||||
testCVTStringToFormatDateTime("500", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 5000), cb);
|
||||
testCVTStringToFormatDateTime("999", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 9990), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1), cb);
|
||||
testCVTStringToFormatDateTime("10", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 10), cb);
|
||||
testCVTStringToFormatDateTime("100", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 100), cb);
|
||||
testCVTStringToFormatDateTime("1000", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1000), cb);
|
||||
testCVTStringToFormatDateTime("5000", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 5000), cb);
|
||||
testCVTStringToFormatDateTime("9999", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 9999), cb);
|
||||
|
||||
testCVTStringToFormatDateTime("1 PM - 25 - 45 - 200", "HH.MI.SS.FF4", createTimeStampTZ(1, 1, 1, 13, 25, 45, 0, 200), cb);
|
||||
testCVTStringToFormatDateTime("15:0:15:2", "HH24.MI.SS.FF1", createTimeStampTZ(1, 1, 1, 15, 0, 15, 0, 2000), cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_TZ)
|
||||
{
|
||||
testCVTStringToFormatDateTime("12:00 2:30", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, 150, 0), cb);
|
||||
testCVTStringToFormatDateTime("12:00 +2:30", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, 150, 0), cb);
|
||||
testCVTStringToFormatDateTime("12:00 -2:30", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, -150, 0), cb);
|
||||
testCVTStringToFormatDateTime("12:00 +0:30", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, 30, 0), cb);
|
||||
testCVTStringToFormatDateTime("12:00 +0:00", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, 0, 0), cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_SOLID_PATTERNS)
|
||||
{
|
||||
testCVTStringToFormatDateTime("1 PM - 25 - 45 - 200", "HHMISSFF4", createTimeStampTZ(1, 1, 1, 13, 25, 45, 0, 200), cb);
|
||||
testCVTStringToFormatDateTime("1981-8/13", "YEARMMDD", createTimeStampTZ(1981, 8, 13, 0, 0, 0, 0), cb);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // FunctionalTest
|
||||
BOOST_AUTO_TEST_SUITE_END() // CVTStringToFormatDateTime
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // CVTDatetimeFormat
|
||||
BOOST_AUTO_TEST_SUITE_END() // CVTSuite
|
126
src/common/tests/CvtTestUtils.h
Normal file
126
src/common/tests/CvtTestUtils.h
Normal file
@ -0,0 +1,126 @@
|
||||
#ifndef CVT_TEST_UTILS_H
|
||||
#define CVT_TEST_UTILS_H
|
||||
|
||||
#include "firebird.h"
|
||||
#include "../common/dsc.h"
|
||||
#include "../common/TimeZoneUtil.h"
|
||||
#include "../common/TimeZones.h"
|
||||
|
||||
using Firebird::NoThrowTimeStamp;
|
||||
using Firebird::TimeZoneUtil;
|
||||
|
||||
namespace CvtTestUtils {
|
||||
|
||||
#define DECOMPOSE_TM_STRUCT(times, fractions, timezone) "Year:" << times.tm_year + 1900 << \
|
||||
" Month:" << times.tm_mon << \
|
||||
" Day:" << times.tm_mday << \
|
||||
" Hour:" << times.tm_hour << \
|
||||
" Min:" << times.tm_min << \
|
||||
" Sec:" << times.tm_sec << \
|
||||
" Fract: " << fractions << \
|
||||
" IsDST:" << times.tm_isdst << \
|
||||
" WDay:" << times.tm_wday << \
|
||||
" YDay:" << times.tm_yday << \
|
||||
" TZ Offset:" << timezone \
|
||||
|
||||
template<typename T>
|
||||
static constexpr int sign(T value)
|
||||
{
|
||||
return (T(0) < value) - (value < T(0));
|
||||
}
|
||||
|
||||
static struct tm initTMStruct(int year, int month, int day)
|
||||
{
|
||||
struct tm times;
|
||||
memset(×, 0, sizeof(struct tm));
|
||||
|
||||
times.tm_year = year - 1900;
|
||||
times.tm_mon = month - 1;
|
||||
times.tm_mday = day;
|
||||
mktime(×);
|
||||
|
||||
return times;
|
||||
}
|
||||
|
||||
static ISC_DATE createDate(int year, int month, int day)
|
||||
{
|
||||
struct tm times = initTMStruct(year, month, day);
|
||||
return NoThrowTimeStamp::encode_date(×);
|
||||
}
|
||||
|
||||
static ISC_TIME createTime(int hours, int minutes, int seconds, int fractions = 0)
|
||||
{
|
||||
return NoThrowTimeStamp::encode_time(hours, minutes, seconds, fractions);
|
||||
}
|
||||
|
||||
static ISC_TIMESTAMP createTimeStamp(int year, int month, int day, int hours, int minutes, int seconds, int fractions = 0)
|
||||
{
|
||||
struct tm times = initTMStruct(year, month, day);
|
||||
times.tm_hour = hours;
|
||||
times.tm_min = minutes;
|
||||
times.tm_sec = seconds;
|
||||
|
||||
return NoThrowTimeStamp::encode_timestamp(×, fractions);
|
||||
}
|
||||
|
||||
static ISC_TIME_TZ createTimeTZ(int hours, int minutes, int seconds, int offsetInMinutes, int fractions = 0)
|
||||
{
|
||||
ISC_TIME_TZ timeTZ;
|
||||
timeTZ.time_zone = TimeZoneUtil::makeFromOffset(sign(offsetInMinutes), abs(offsetInMinutes / 60),
|
||||
abs(offsetInMinutes % 60));
|
||||
timeTZ.utc_time = createTime(hours, minutes, seconds, fractions);
|
||||
|
||||
return timeTZ;
|
||||
}
|
||||
|
||||
static ISC_TIMESTAMP_TZ createTimeStampTZ(int year, int month, int day, int hours, int minutes, int seconds,
|
||||
int offsetInMinutes, int fractions = 0)
|
||||
{
|
||||
ISC_TIMESTAMP_TZ timestampTZ;
|
||||
timestampTZ.time_zone = TimeZoneUtil::makeFromOffset(sign(offsetInMinutes), abs(offsetInMinutes / 60),
|
||||
abs(offsetInMinutes % 60));
|
||||
timestampTZ.utc_timestamp = createTimeStamp(year, month, day, hours, minutes, seconds, fractions);
|
||||
|
||||
return timestampTZ;
|
||||
}
|
||||
|
||||
class CVTCallback : public Firebird::Callbacks
|
||||
{
|
||||
public:
|
||||
explicit CVTCallback(ErrorFunction aErr) : Callbacks(aErr)
|
||||
{}
|
||||
|
||||
public:
|
||||
bool transliterate(const dsc* from, dsc* to, CHARSET_ID&) override { return true; }
|
||||
CHARSET_ID getChid(const dsc* d) override { return 0; }
|
||||
Jrd::CharSet* getToCharset(CHARSET_ID charset2) override { return nullptr; }
|
||||
void validateData(Jrd::CharSet* toCharset, SLONG length, const UCHAR* q) override { }
|
||||
ULONG validateLength(Jrd::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
|
||||
const USHORT size) override { return 0; }
|
||||
SLONG getLocalDate() override { return 0; }
|
||||
ISC_TIMESTAMP getCurrentGmtTimeStamp() override { ISC_TIMESTAMP ts; return ts; }
|
||||
USHORT getSessionTimeZone() override { return 1439; }
|
||||
void isVersion4(bool& v4) override { }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static UCHAR getDSCTypeFromDateType() { return 0; }
|
||||
|
||||
template<>
|
||||
UCHAR getDSCTypeFromDateType<ISC_DATE>() { return dtype_sql_date; }
|
||||
|
||||
template<>
|
||||
UCHAR getDSCTypeFromDateType<ISC_TIME>() { return dtype_sql_time; }
|
||||
|
||||
template<>
|
||||
UCHAR getDSCTypeFromDateType<ISC_TIMESTAMP>() { return dtype_timestamp; }
|
||||
|
||||
template<>
|
||||
UCHAR getDSCTypeFromDateType<ISC_TIME_TZ>() { return dtype_sql_time_tz; }
|
||||
|
||||
template<>
|
||||
UCHAR getDSCTypeFromDateType<ISC_TIMESTAMP_TZ>() { return dtype_timestamp_tz; }
|
||||
|
||||
} // namespace CvtTestUtils
|
||||
|
||||
#endif // CVT_TEST_UTILS_H
|
@ -3494,24 +3494,30 @@ dsc* BoolAsValueNode::execute(thread_db* tdbb, Request* request) const
|
||||
//--------------------
|
||||
|
||||
|
||||
static RegisterNode<CastNode> regCastNode({blr_cast});
|
||||
static RegisterNode<CastNode> regCastNode({blr_cast, blr_cast_format});
|
||||
|
||||
CastNode::CastNode(MemoryPool& pool, ValueExprNode* aSource, dsql_fld* aDsqlField)
|
||||
CastNode::CastNode(MemoryPool& pool, ValueExprNode* aSource, dsql_fld* aDsqlField, const string& aFormat)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_CAST>(pool),
|
||||
dsqlAlias("CAST"),
|
||||
dsqlField(aDsqlField),
|
||||
source(aSource),
|
||||
itemInfo(NULL),
|
||||
format(pool, aFormat),
|
||||
artificial(false)
|
||||
{
|
||||
castDesc.clear();
|
||||
}
|
||||
|
||||
// Parse a datatype cast.
|
||||
DmlNode* CastNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/)
|
||||
DmlNode* CastNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
|
||||
{
|
||||
fb_assert(blrOp == blr_cast || blrOp == blr_cast_format);
|
||||
|
||||
CastNode* node = FB_NEW_POOL(pool) CastNode(pool);
|
||||
|
||||
if (blrOp == blr_cast_format)
|
||||
csb->csb_blr_reader.getString(node->format);
|
||||
|
||||
ItemInfo itemInfo;
|
||||
PAR_desc(tdbb, csb, &node->castDesc, &itemInfo);
|
||||
|
||||
@ -3539,6 +3545,7 @@ string CastNode::internalPrint(NodePrinter& printer) const
|
||||
NODE_PRINT(printer, castDesc);
|
||||
NODE_PRINT(printer, source);
|
||||
NODE_PRINT(printer, itemInfo);
|
||||
NODE_PRINT(printer, format);
|
||||
|
||||
return "CastNode";
|
||||
}
|
||||
@ -3549,6 +3556,7 @@ ValueExprNode* CastNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
node->dsqlAlias = dsqlAlias;
|
||||
node->source = doDsqlPass(dsqlScratch, source);
|
||||
node->dsqlField = dsqlField;
|
||||
node->format = format;
|
||||
|
||||
DDL_resolve_intl_type(dsqlScratch, node->dsqlField, NULL);
|
||||
node->setParameterType(dsqlScratch, NULL, false);
|
||||
@ -3593,8 +3601,16 @@ bool CastNode::setParameterType(DsqlCompilerScratch* /*dsqlScratch*/,
|
||||
// Generate BLR for a data-type cast operation.
|
||||
void CastNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_cast);
|
||||
if (format.hasData())
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_cast_format);
|
||||
dsqlScratch->appendString(0, format);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_cast);
|
||||
|
||||
dsqlScratch->putDtype(dsqlField, true);
|
||||
|
||||
GEN_expr(dsqlScratch, source);
|
||||
}
|
||||
|
||||
@ -3640,6 +3656,7 @@ ValueExprNode* CastNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
||||
node->source = copier.copy(tdbb, source);
|
||||
node->castDesc = castDesc;
|
||||
node->itemInfo = itemInfo;
|
||||
node->format = format;
|
||||
|
||||
return node;
|
||||
}
|
||||
@ -3652,7 +3669,7 @@ bool CastNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other
|
||||
const CastNode* o = nodeAs<CastNode>(other);
|
||||
fb_assert(o);
|
||||
|
||||
return dsqlField == o->dsqlField;
|
||||
return dsqlField == o->dsqlField && format == o->format;
|
||||
}
|
||||
|
||||
bool CastNode::sameAs(const ExprNode* other, bool ignoreStreams) const
|
||||
@ -3666,7 +3683,7 @@ bool CastNode::sameAs(const ExprNode* other, bool ignoreStreams) const
|
||||
const CastNode* const otherNode = nodeAs<CastNode>(other);
|
||||
fb_assert(otherNode);
|
||||
|
||||
return DSC_EQUIV(&castDesc, &otherNode->castDesc, true);
|
||||
return DSC_EQUIV(&castDesc, &otherNode->castDesc, true) && format == otherNode->format;
|
||||
}
|
||||
|
||||
ValueExprNode* CastNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
||||
@ -3706,12 +3723,12 @@ dsc* CastNode::execute(thread_db* tdbb, Request* request) const
|
||||
|
||||
const auto impure = request->getImpure<impure_value>(impureOffset);
|
||||
|
||||
return perform(tdbb, impure, value, &castDesc, itemInfo);
|
||||
return perform(tdbb, impure, value, &castDesc, itemInfo, format);
|
||||
}
|
||||
|
||||
// Cast from one datatype to another.
|
||||
dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
|
||||
const dsc* castDesc, const ItemInfo* itemInfo)
|
||||
const dsc* castDesc, const ItemInfo* itemInfo, const Firebird::string& format)
|
||||
{
|
||||
// If validation is not required and the source value is either NULL
|
||||
// or already in the desired data type, simply return it "as is"
|
||||
@ -3767,7 +3784,58 @@ dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
|
||||
if (!value)
|
||||
return nullptr;
|
||||
|
||||
MOV_move(tdbb, value, &impure->vlu_desc);
|
||||
|
||||
if (format.hasData())
|
||||
{
|
||||
if (DTYPE_IS_TEXT(impure->vlu_desc.dsc_dtype))
|
||||
{
|
||||
string result = CVT_datetime_to_format_string(value, format, &EngineCallbacks::instance);
|
||||
USHORT dscLength = DSC_string_length(&impure->vlu_desc);
|
||||
string::size_type resultLength = result.length();
|
||||
if (resultLength > dscLength)
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) <<
|
||||
Arg::Gds(isc_trunc_limits) << Arg::Num(dscLength) << Arg::Num(resultLength));
|
||||
}
|
||||
|
||||
USHORT dscOffset = 0;
|
||||
if (impure->vlu_desc.dsc_dtype == dtype_cstring)
|
||||
dscOffset = 1;
|
||||
else if (impure->vlu_desc.dsc_dtype == dtype_varying)
|
||||
{
|
||||
dscOffset = sizeof(USHORT);
|
||||
((vary*) impure->vlu_desc.dsc_address)->vary_length = resultLength;
|
||||
}
|
||||
|
||||
memcpy(impure->vlu_desc.dsc_address + dscOffset, result.c_str(), resultLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
ISC_TIMESTAMP_TZ timestampTZ = CVT_string_to_format_datetime(value, format, &EngineCallbacks::instance);
|
||||
switch (impure->vlu_desc.dsc_dtype)
|
||||
{
|
||||
case dtype_sql_time:
|
||||
*(ISC_TIME*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp.timestamp_time;
|
||||
break;
|
||||
case dtype_sql_date:
|
||||
*(ISC_DATE*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp.timestamp_date;
|
||||
break;
|
||||
case dtype_timestamp:
|
||||
*(ISC_TIMESTAMP*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp;
|
||||
break;
|
||||
case dtype_sql_time_tz:
|
||||
case dtype_ex_time_tz:
|
||||
*(ISC_TIME_TZ*) impure->vlu_desc.dsc_address = TimeZoneUtil::timeStampTzToTimeTz(timestampTZ);
|
||||
break;
|
||||
case dtype_timestamp_tz:
|
||||
case dtype_ex_timestamp_tz:
|
||||
*(ISC_TIMESTAMP_TZ*) impure->vlu_desc.dsc_address = timestampTZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
MOV_move(tdbb, value, &impure->vlu_desc);
|
||||
|
||||
if (impure->vlu_desc.dsc_dtype == dtype_text)
|
||||
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
||||
@ -5781,55 +5849,7 @@ dsc* ExtractNode::execute(thread_db* tdbb, Request* request) const
|
||||
|
||||
case blr_extract_week:
|
||||
{
|
||||
// Algorithm for Converting Gregorian Dates to ISO 8601 Week Date by Rick McCarty, 1999
|
||||
// http://personal.ecu.edu/mccartyr/ISOwdALG.txt
|
||||
|
||||
const int y = times.tm_year + 1900;
|
||||
const int dayOfYearNumber = times.tm_yday + 1;
|
||||
|
||||
// Find the jan1Weekday for y (Monday=1, Sunday=7)
|
||||
const int yy = (y - 1) % 100;
|
||||
const int c = (y - 1) - yy;
|
||||
const int g = yy + yy / 4;
|
||||
const int jan1Weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
|
||||
|
||||
// Find the weekday for y m d
|
||||
const int h = dayOfYearNumber + (jan1Weekday - 1);
|
||||
const int weekday = 1 + ((h - 1) % 7);
|
||||
|
||||
// Find if y m d falls in yearNumber y-1, weekNumber 52 or 53
|
||||
int yearNumber, weekNumber;
|
||||
|
||||
if ((dayOfYearNumber <= (8 - jan1Weekday)) && (jan1Weekday > 4))
|
||||
{
|
||||
yearNumber = y - 1;
|
||||
weekNumber = ((jan1Weekday == 5) || ((jan1Weekday == 6) &&
|
||||
TimeStamp::isLeapYear(yearNumber))) ? 53 : 52;
|
||||
}
|
||||
else
|
||||
{
|
||||
yearNumber = y;
|
||||
|
||||
// Find if y m d falls in yearNumber y+1, weekNumber 1
|
||||
int i = TimeStamp::isLeapYear(y) ? 366 : 365;
|
||||
|
||||
if ((i - dayOfYearNumber) < (4 - weekday))
|
||||
{
|
||||
yearNumber = y + 1;
|
||||
weekNumber = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Find if y m d falls in yearNumber y, weekNumber 1 through 53
|
||||
if (yearNumber == y)
|
||||
{
|
||||
int j = dayOfYearNumber + (7 - weekday) + (jan1Weekday - 1);
|
||||
weekNumber = j / 7;
|
||||
if (jan1Weekday > 4)
|
||||
weekNumber--;
|
||||
}
|
||||
|
||||
part = weekNumber;
|
||||
part = NoThrowTimeStamp::convertGregorianDateToWeekDate(times);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,8 @@ public:
|
||||
class CastNode final : public TypedNode<ValueExprNode, ExprNode::TYPE_CAST>
|
||||
{
|
||||
public:
|
||||
explicit CastNode(MemoryPool& pool, ValueExprNode* aSource = NULL, dsql_fld* aDsqlField = NULL);
|
||||
explicit CastNode(MemoryPool& pool, ValueExprNode* aSource = NULL, dsql_fld* aDsqlField = NULL,
|
||||
const Firebird::string& aFormat = NULL);
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
@ -274,13 +275,14 @@ public:
|
||||
virtual dsc* execute(thread_db* tdbb, Request* request) const;
|
||||
|
||||
static dsc* perform(thread_db* tdbb, impure_value* impure, dsc* value,
|
||||
const dsc* castDesc, const ItemInfo* itemInfo);
|
||||
const dsc* castDesc, const ItemInfo* itemInfo, const Firebird::string& format = nullptr);
|
||||
|
||||
public:
|
||||
MetaName dsqlAlias;
|
||||
dsql_fld* dsqlField;
|
||||
NestConst<ValueExprNode> source;
|
||||
NestConst<ItemInfo> itemInfo;
|
||||
Firebird::string format;
|
||||
dsc castDesc;
|
||||
bool artificial;
|
||||
};
|
||||
|
128
src/dsql/parse.y
128
src/dsql/parse.y
@ -700,6 +700,7 @@ using namespace Firebird;
|
||||
|
||||
%token <metaNamePtr> ANY_VALUE
|
||||
%token <metaNamePtr> CALL
|
||||
%token <metaNamePtr> FORMAT
|
||||
%token <metaNamePtr> NAMED_ARG_ASSIGN
|
||||
|
||||
// precedence declarations for expression evaluation
|
||||
@ -4855,6 +4856,7 @@ non_charset_simple_type
|
||||
| numeric_type
|
||||
| float_type
|
||||
| decfloat_type
|
||||
| date_time_type
|
||||
| BIGINT
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
@ -4898,60 +4900,6 @@ non_charset_simple_type
|
||||
$$->length = sizeof(SSHORT);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| DATE
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
stmt_ambiguous = true;
|
||||
|
||||
if (client_dialect <= SQL_DIALECT_V5)
|
||||
{
|
||||
// Post warning saying that DATE is equivalent to TIMESTAMP
|
||||
ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(301) <<
|
||||
Arg::Warning(isc_dtype_renamed));
|
||||
$$->dtype = dtype_timestamp;
|
||||
$$->length = sizeof(GDS_TIMESTAMP);
|
||||
}
|
||||
else if (client_dialect == SQL_DIALECT_V6_TRANSITION)
|
||||
yyabandon(YYPOSNARG(1), -104, isc_transitional_date);
|
||||
else
|
||||
{
|
||||
$$->dtype = dtype_sql_date;
|
||||
$$->length = sizeof(ULONG);
|
||||
}
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| TIME without_time_zone_opt
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
|
||||
checkTimeDialect();
|
||||
$$->dtype = dtype_sql_time;
|
||||
$$->length = sizeof(SLONG);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| TIME WITH TIME ZONE
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
|
||||
checkTimeDialect();
|
||||
$$->dtype = dtype_sql_time_tz;
|
||||
$$->length = sizeof(ISC_TIME_TZ);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| TIMESTAMP without_time_zone_opt
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
$$->dtype = dtype_timestamp;
|
||||
$$->length = sizeof(GDS_TIMESTAMP);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| TIMESTAMP WITH TIME ZONE
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
$$->dtype = dtype_timestamp_tz;
|
||||
$$->length = sizeof(ISC_TIMESTAMP_TZ);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| BOOLEAN
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
@ -8720,6 +8668,77 @@ named_argument
|
||||
cast_specification
|
||||
: CAST '(' value AS data_type_descriptor ')'
|
||||
{ $$ = newNode<CastNode>($3, $5); }
|
||||
| CAST '(' value AS cast_format_type cast_format_clause utf_string ')'
|
||||
{ $$ = newNode<CastNode>($3, $5, *$7); }
|
||||
;
|
||||
|
||||
%type <metaNamePtr> cast_format_clause
|
||||
cast_format_clause
|
||||
: FORMAT
|
||||
;
|
||||
|
||||
%type <legacyField> date_time_type
|
||||
date_time_type
|
||||
: DATE
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
stmt_ambiguous = true;
|
||||
|
||||
if (client_dialect <= SQL_DIALECT_V5)
|
||||
{
|
||||
// Post warning saying that DATE is equivalent to TIMESTAMP
|
||||
ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(301) <<
|
||||
Arg::Warning(isc_dtype_renamed));
|
||||
$$->dtype = dtype_timestamp;
|
||||
$$->length = sizeof(GDS_TIMESTAMP);
|
||||
}
|
||||
else if (client_dialect == SQL_DIALECT_V6_TRANSITION)
|
||||
yyabandon(YYPOSNARG(1), -104, isc_transitional_date);
|
||||
else
|
||||
{
|
||||
$$->dtype = dtype_sql_date;
|
||||
$$->length = sizeof(ULONG);
|
||||
}
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| TIME without_time_zone_opt
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
|
||||
checkTimeDialect();
|
||||
$$->dtype = dtype_sql_time;
|
||||
$$->length = sizeof(SLONG);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| TIME WITH TIME ZONE
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
|
||||
checkTimeDialect();
|
||||
$$->dtype = dtype_sql_time_tz;
|
||||
$$->length = sizeof(ISC_TIME_TZ);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| TIMESTAMP without_time_zone_opt
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
$$->dtype = dtype_timestamp;
|
||||
$$->length = sizeof(GDS_TIMESTAMP);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
| TIMESTAMP WITH TIME ZONE
|
||||
{
|
||||
$$ = newNode<dsql_fld>();
|
||||
$$->dtype = dtype_timestamp_tz;
|
||||
$$->length = sizeof(ISC_TIMESTAMP_TZ);
|
||||
$$->flags |= FLD_has_prec;
|
||||
}
|
||||
;
|
||||
|
||||
%type <legacyField> cast_format_type
|
||||
cast_format_type
|
||||
: character_type
|
||||
| date_time_type
|
||||
;
|
||||
|
||||
// case expressions
|
||||
@ -9383,6 +9402,7 @@ non_reserved_word
|
||||
| UNICODE_VAL
|
||||
// added in FB 6.0
|
||||
| ANY_VALUE
|
||||
| FORMAT
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -496,4 +496,6 @@
|
||||
|
||||
#define blr_default_arg (unsigned char) 227
|
||||
|
||||
#define blr_cast_format (unsigned char) 228
|
||||
|
||||
#endif // FIREBIRD_IMPL_BLR_H
|
||||
|
@ -972,3 +972,12 @@ FB_IMPL_MSG(JRD, 969, uninitialized_var, -625, "42", "000", "Variable @1 is not
|
||||
FB_IMPL_MSG(JRD, 970, param_not_exist, -170, "07", "001", "Parameter @1 does not exist")
|
||||
FB_IMPL_MSG(JRD, 971, param_no_default_not_specified, -170, "07", "001", "Parameter @1 has no default value and was not specified or was specified with DEFAULT")
|
||||
FB_IMPL_MSG(JRD, 972, param_multiple_assignments, -170, "07", "001", "Parameter @1 has multiple assignments")
|
||||
FB_IMPL_MSG(JRD, 973, invalid_date_format, -901, "HY", "000", "Cannot recognize \"@1\" part of date format")
|
||||
FB_IMPL_MSG(JRD, 974, invalid_raw_string_in_date_format, -901, "HY", "000", "Cannot find closing \" for raw text in date format")
|
||||
FB_IMPL_MSG(JRD, 975, invalid_data_type_for_date_format, -901, "HY", "000", "It is not possible to use this data type for date formatting")
|
||||
FB_IMPL_MSG(JRD, 976, incompatible_date_format_with_current_date_type, -901, "HY", "000", "Cannot use \"@1\" format with current date type")
|
||||
FB_IMPL_MSG(JRD, 977, value_for_pattern_is_out_of_range, -901, "HY", "000", "Value for @1 pattern is out of range [@2, @3]")
|
||||
FB_IMPL_MSG(JRD, 978, month_name_mismatch, -901, "HY", "000", "@1 is not MONTH")
|
||||
FB_IMPL_MSG(JRD, 979, incorrect_hours_period, -901, "HY", "000", "@1 is incorrect period for 12H, it should be AM or PM")
|
||||
FB_IMPL_MSG(JRD, 980, data_for_format_is_exhausted, -901, "HY", "000", "All data has been read, but format pattern wants more. Unfilled patterns: \"@1\"")
|
||||
FB_IMPL_MSG(JRD, 981, trailing_part_of_string, -901, "HY", "000", "There is a trailing part of input string that does not fit into FORMAT: \"@1\"")
|
||||
|
@ -5704,6 +5704,15 @@ const
|
||||
isc_param_not_exist = 335545290;
|
||||
isc_param_no_default_not_specified = 335545291;
|
||||
isc_param_multiple_assignments = 335545292;
|
||||
isc_invalid_date_format = 335545293;
|
||||
isc_invalid_raw_string_in_date_format = 335545294;
|
||||
isc_invalid_data_type_for_date_format = 335545295;
|
||||
isc_incompatible_date_format_with_current_date_type = 335545296;
|
||||
isc_value_for_pattern_is_out_of_range = 335545297;
|
||||
isc_month_name_mismatch = 335545298;
|
||||
isc_incorrect_hours_period = 335545299;
|
||||
isc_data_for_format_is_exhausted = 335545300;
|
||||
isc_trailing_part_of_string = 335545301;
|
||||
isc_gfix_db_name = 335740929;
|
||||
isc_gfix_invalid_sw = 335740930;
|
||||
isc_gfix_incmp_sw = 335740932;
|
||||
|
Loading…
Reference in New Issue
Block a user