diff --git a/src/common/TimeZoneUtil.cpp b/src/common/TimeZoneUtil.cpp index d806f0b46d..b3ba461bdb 100644 --- a/src/common/TimeZoneUtil.cpp +++ b/src/common/TimeZoneUtil.cpp @@ -292,6 +292,20 @@ ISC_TIMESTAMP TimeZoneUtil::timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeS return TimeStamp::encode_timestamp(×, fractions); } +// Converts a time from local to UTC. +void TimeZoneUtil::localTimeToUtc(ISC_TIME& time, Callbacks* cb) +{ + //// TODO: + ISC_TIMESTAMP_TZ tempTimeStampTz; + tempTimeStampTz.timestamp_date = TimeStamp::getCurrentTimeStamp().value().timestamp_date; + tempTimeStampTz.timestamp_time = time; + tempTimeStampTz.timestamp_zone = cb->getSessionTimeZone(); + + localTimeStampToUtc(tempTimeStampTz); + + time = tempTimeStampTz.timestamp_time; +} + // Converts a time from local to UTC. void TimeZoneUtil::localTimeToUtc(ISC_TIME_TZ& timeTz) { @@ -306,6 +320,20 @@ void TimeZoneUtil::localTimeToUtc(ISC_TIME_TZ& timeTz) timeTz.time_time = tempTimeStampTz.timestamp_time; } +// Converts a timestamp from its local datetime fields to UTC. +void TimeZoneUtil::localTimeStampToUtc(ISC_TIMESTAMP& timeStamp, Callbacks* cb) +{ + ISC_TIMESTAMP_TZ tempTimeStampTz; + tempTimeStampTz.timestamp_date = timeStamp.timestamp_date; + tempTimeStampTz.timestamp_time = timeStamp.timestamp_time; + tempTimeStampTz.timestamp_zone = cb->getSessionTimeZone(); + + localTimeStampToUtc(tempTimeStampTz); + + timeStamp.timestamp_date = tempTimeStampTz.timestamp_date; + timeStamp.timestamp_time = tempTimeStampTz.timestamp_time; +} + // Converts a timestamp from its local datetime fields to UTC. void TimeZoneUtil::localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz) { diff --git a/src/common/TimeZoneUtil.h b/src/common/TimeZoneUtil.h index 4ac302a82b..43b040ac73 100644 --- a/src/common/TimeZoneUtil.h +++ b/src/common/TimeZoneUtil.h @@ -28,6 +28,7 @@ #define COMMON_TIME_ZONE_UTIL_H #include "../common/classes/fb_string.h" +#include "../common/cvt.h" namespace Firebird { @@ -53,7 +54,10 @@ public: static ISC_TIME timeTzToTime(const ISC_TIME_TZ& timeTz, USHORT toTimeZone); static ISC_TIMESTAMP timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT toTimeZone); + static void localTimeToUtc(ISC_TIME& time, Callbacks* cb); static void localTimeToUtc(ISC_TIME_TZ& timeTz); + + static void localTimeStampToUtc(ISC_TIMESTAMP& timeStamp, Callbacks* cb); static void localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz); static void decodeTime(const ISC_TIME_TZ& timeTz, struct tm* times, int* fractions = NULL); diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 43405b4f1e..47803b387d 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -538,8 +538,8 @@ void setParamsDateDiff(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc { if (args[1]->isUnknown() && args[2]->isUnknown()) { - args[1]->makeTimestamp(); - args[2]->makeTimestamp(); + args[1]->makeTimestamp(); //// TODO: + args[2]->makeTimestamp(); //// TODO: } else if (args[1]->isUnknown()) *args[1] = *args[2]; @@ -2128,7 +2128,6 @@ dsc* evlDateAdd(thread_db* tdbb, const SysFunction* function, const NestValueArr } -//// FIXME: dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure) { @@ -2153,17 +2152,25 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr switch (value1Dsc->dsc_dtype) { case dtype_sql_time: - timestamp1.value().timestamp_time = *(GDS_TIME *) value1Dsc->dsc_address; + case dtype_sql_time_tz: + timestamp1.value().timestamp_time = *(GDS_TIME*) value1Dsc->dsc_address; timestamp1.value().timestamp_date = 0; + + if (value1Dsc->dsc_dtype == dtype_sql_time && value2Dsc->isDateTimeTz()) + TimeZoneUtil::localTimeToUtc(timestamp1.value().timestamp_time, &EngineCallbacks::instance); break; case dtype_sql_date: - timestamp1.value().timestamp_date = *(GDS_DATE *) value1Dsc->dsc_address; + timestamp1.value().timestamp_date = *(GDS_DATE*) value1Dsc->dsc_address; timestamp1.value().timestamp_time = 0; break; case dtype_timestamp: - timestamp1.value() = *(GDS_TIMESTAMP *) value1Dsc->dsc_address; + case dtype_timestamp_tz: + timestamp1.value() = *(GDS_TIMESTAMP*) value1Dsc->dsc_address; + + if (value1Dsc->dsc_dtype == dtype_timestamp && value2Dsc->isDateTimeTz()) + TimeZoneUtil::localTimeStampToUtc(timestamp1.value(), &EngineCallbacks::instance); break; default: @@ -2178,17 +2185,25 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr switch (value2Dsc->dsc_dtype) { case dtype_sql_time: - timestamp2.value().timestamp_time = *(GDS_TIME *) value2Dsc->dsc_address; + case dtype_sql_time_tz: + timestamp2.value().timestamp_time = *(GDS_TIME*) value2Dsc->dsc_address; timestamp2.value().timestamp_date = 0; + + if (value2Dsc->dsc_dtype == dtype_sql_time && value1Dsc->isDateTimeTz()) + TimeZoneUtil::localTimeToUtc(timestamp2.value().timestamp_time, &EngineCallbacks::instance); break; case dtype_sql_date: - timestamp2.value().timestamp_date = *(GDS_DATE *) value2Dsc->dsc_address; + timestamp2.value().timestamp_date = *(GDS_DATE*) value2Dsc->dsc_address; timestamp2.value().timestamp_time = 0; break; case dtype_timestamp: - timestamp2.value() = *(GDS_TIMESTAMP *) value2Dsc->dsc_address; + case dtype_timestamp_tz: + timestamp2.value() = *(GDS_TIMESTAMP*) value2Dsc->dsc_address; + + if (value2Dsc->dsc_dtype == dtype_timestamp && value1Dsc->isDateTimeTz()) + TimeZoneUtil::localTimeStampToUtc(timestamp2.value(), &EngineCallbacks::instance); break; default: @@ -2229,7 +2244,7 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr case blr_extract_month: case blr_extract_day: case blr_extract_week: - if (value1Dsc->dsc_dtype == dtype_sql_time || value2Dsc->dsc_dtype == dtype_sql_time) + if (value1Dsc->isTime() || value2Dsc->isTime()) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << Arg::Gds(isc_sysf_invalid_timediff) << @@ -2242,22 +2257,17 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr case blr_extract_second: case blr_extract_millisecond: { - //if (value1Dsc->dsc_dtype == dtype_sql_date || value2Dsc->dsc_dtype == dtype_sql_date) - // status_exception::raise(Arg::Gds(isc_expression_eval_err)); - // ASF: also throw error if one value is TIMESTAMP and the other is TIME // CVC: Or if one value is DATE and the other is TIME. - const int type1 = value1Dsc->dsc_dtype; - const int type2 = value2Dsc->dsc_dtype; - if ((type1 == dtype_timestamp && type2 == dtype_sql_time) || - (type1 == dtype_sql_time && type2 == dtype_timestamp)) + if ((value1Dsc->isTimeStamp() && value2Dsc->isTime()) || + (value1Dsc->isTime() && value2Dsc->isTimeStamp())) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << Arg::Gds(isc_sysf_invalid_tstamptimediff) << Arg::Str(function->name)); } - if ((type1 == dtype_sql_date && type2 == dtype_sql_time) || - (type1 == dtype_sql_time && type2 == dtype_sql_date)) + if ((value1Dsc->dsc_dtype == dtype_sql_date && value2Dsc->isTime()) || + (value1Dsc->isTime() && value2Dsc->dsc_dtype == dtype_sql_date)) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << Arg::Gds(isc_sysf_invalid_datetimediff) <<