Skip to content

Fix for DATENAME() gives incorrect value with TZOFFSET part#3846

Merged
forestkeeper merged 11 commits intobabelfish-for-postgresql:BABEL_5_X_DEVfrom
amazon-aurora:jira-babel-5846
Jul 1, 2025
Merged

Fix for DATENAME() gives incorrect value with TZOFFSET part#3846
forestkeeper merged 11 commits intobabelfish-for-postgresql:BABEL_5_X_DEVfrom
amazon-aurora:jira-babel-5846

Conversation

@manisha-deshpande
Copy link
Copy Markdown
Contributor

@manisha-deshpande manisha-deshpande commented Jun 17, 2025

Description

Currently, DATENAME function returns a numeric value for TZOFFSET datepart, which does not match SQL Server's text output.

Root cause: The function definition of sys.datename explicitly calls the DATEPART function for most argument values, including TZOFFSET, thereby returning a numerical offset value instead of the required text format (+/-HH:MM).

Fix: Modified the sys function's SQL definition to extract and return the timezone offset substring from the input - similar to how the offset_string is determined in DATETRUNC/DATEBUCKET - if the argument matches tzoffset. Furthermore, translated the function from SQL language to plpgsql in order to enable raising an exception for unsupported input datetime datatypes such as date, time, datetime and smalldatetime.

Additional changes: Updated an existing jdbc test case datepart-vu-verify.sql to avoid 'failing' table insertions of DATENAME(TZOFFSET, cursor(date)) for incompatible date types: datetime, smalldatetime, datetimeoffset.

IF NOT (@datepart = 'tzoffset' AND @datatype NOT IN ('DATETIMEOFFSET', 'DATETIME2'))
Added corresponding version upgrade tests.

Issues Resolved

BABEL-5846

Test Scenarios Covered

  • Use case based -
-- Test positive/negative offsets
SELECT DATENAME(TZOFFSET, '2025-06-03 14:30:15 +01:59');
GO
~~START~~
text
+01:59
~~END~~

select datename(TZoffset, CAST('2016-12-26 23:30:05.523456+8'AS datetimeoffset));
GO
~~START~~
text
+08:00
~~END~~

SELECT DATENAME(TZOFFSET, CAST('2025-06-03 14:30:15 +01:59' AS DATETIMEOFFSET));
GO
~~START~~
text
+01:59
~~END~~

SELECT DATENAME(TZOFFSET, CAST('2025-06-03 14:30:15 -01:59' AS DATETIMEOFFSET));
GO
~~START~~
text
-01:59
~~END~~
  • Boundary conditions -
-- Test case for NULL input
SELECT DATENAME(TZOFFSET, NULL);
GO
~~START~~
text
<NULL>
~~END~~

-- Test case for empty string
SELECT DATENAME(TZOFFSET, '');
GO
~~START~~
text
+00:00
~~END~~

-- Boundary values
SELECT DATENAME(TZOFFSET, CAST('2025-06-03 14:30:15 +14:00' AS DATETIMEOFFSET));
GO
~~START~~
text
+14:00
~~END~~

SELECT DATENAME(TZOFFSET, CAST('2025-06-03 14:30:15 -14:00' AS DATETIMEOFFSET));
GO
~~START~~
text
-14:00
~~END~~

-- Supported date time datatype inputs
SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15' AS DATETIME2)) AS datetime2_type;
GO
~~START~~
text
+00:00
~~END~~

SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15' AS DATETIMEOFFSET)) AS datetimeoffset_type;
GO
~~START~~
text
+00:00
~~END~~
  • Arbitrary inputs -
    -- Invalid date time inputs
SELECT DATENAME(TZOFFSET, '2025-06-0314:30:15+01:59');
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: Conversion failed when converting date and/or time from character string.)~~

-- Test date inputs without offset specified
SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15.1234567' AS DATETIMEOFFSET));
GO
~~START~~
text
+00:00
~~END~~

SELECT datename(TZOFFSET, CAST('2025-06-03' AS DATETIMEOFFSET));
GO
~~START~~
text
+00:00
~~END~~

SELECT datename(TZOFFSET, CAST('14:30:15.1234567' AS DATETIMEOFFSET));
GO
~~START~~
text
+00:00
~~END~~

SELECT datename(TZOFFSET, CAST('' AS DATETIMEOFFSET));
GO
~~START~~
text
+00:00
~~END~~
  • Negative test cases -
-- Error Scenarios
    -- Unsupported date time datatype inputs
SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15 +01:30' AS DATE)) AS date_type;
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: The datepart tzoffset is not supported by date function datename for data type date.)~~


SELECT datename(TZOFFSET, CAST('14:30:15' AS TIME)) AS time_type;
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: The datepart tzoffset is not supported by date function datename for data type time without time zone.)~~


SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15' AS DATETIME)) AS datetime_type;
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: The datepart tzoffset is not supported by date function datename for data type datetime.)~~


SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15' AS SMALLDATETIME)) AS smalldatetime_type;
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: The datepart tzoffset is not supported by date function datename for data type smalldatetime.)~~



    -- Invalid date time inputs
SELECT datename(TZOFFSET, CAST('2025-06-0314:30:15+130' AS DATETIMEOFFSET));
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: invalid input syntax for type datetimeoffset: "2025-06-0314:30:15+130")~~


SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15 -16:00' AS DATETIMEOFFSET));
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: time zone displacement out of range: "2025-06-03 14:30:15 -16:00")~~


SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15 +10:60' AS DATETIMEOFFSET));
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: time zone displacement out of range: "2025-06-03 14:30:15 +10:60")~~


SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15 +01:30' AS DATETIMEOFFSET) AT TIME ZONE 'Invalid Time Zone');
GO
~~START~~
text
~~ERROR (Code: 33557097)~~

~~ERROR (Message: Argument data type or the parameter Invalid Time Zone provided to AT TIME ZONE clause is invalid.)~~


SELECT datename(TZOFFSET, SWITCHOFFSET(CAST('2025-06-03 14:30:15 +01:30' AS DATETIMEOFFSET), 'Invalid Offset'));
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: The timezone provided to builtin function todatetimeoffset is invalid.)~~


SELECT datename(TZOFFSET, TODATETIMEOFFSET(CAST('2025-06-03 14:30:15' AS DATETIME2), 'Invalid Offset'));
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: The timezone provided to builtin function todatetimeoffset is invalid.)~~

  • Integration test cases -
-- Test with other functions
SELECT datename(TZOFFSET, CAST('2025-06-03 14:30:15 +01:30' AS DATETIMEOFFSET) AT TIME ZONE 'Pacific Standard Time');
GO
~~START~~
text
-07:00
~~END~~

SELECT DATENAME(TZOFFSET, SWITCHOFFSET(CAST('2025-06-03 14:30:15 +01:30' AS DATETIMEOFFSET), '-08:00'));
GO
~~START~~
text
-08:00
~~END~~

SELECT DATENAME(TZOFFSET, TODATETIMEOFFSET(CAST('2025-06-03 14:30:15' AS DATETIME2), '-03:30'));
GO
~~START~~
text
-03:30
~~END~~
  • Minor version upgrade tests -

  • Major version upgrade tests -

  • Performance tests -

  • Tooling impact -

  • Client tests -

Check List

  • Commits are signed per the DCO using --signoff

By submitting this pull request, I confirm that my contribution is under the terms of the Apache 2.0 and PostgreSQL licenses, and grant any person obtaining a copy of the contribution permission to relicense all or a portion of my contribution to the PostgreSQL License solely to contribute all or a portion of my contribution to the PostgreSQL open source project.

For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@coveralls
Copy link
Copy Markdown
Collaborator

coveralls commented Jun 18, 2025

Pull Request Test Coverage Report for Build 16008532369

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.003%) to 75.87%

Totals Coverage Status
Change from base Build 16001000140: 0.003%
Covered Lines: 49472
Relevant Lines: 65206

💛 - Coveralls

@manisha-deshpande
Copy link
Copy Markdown
Contributor Author

manisha-deshpande commented Jun 18, 2025

TODO: Will look into version upgrade test failures
Edit: DONE

@manisha-deshpande manisha-deshpande force-pushed the jira-babel-5846 branch 2 times, most recently from a714a34 to 570debd Compare June 19, 2025 00:06
@manisha-deshpande manisha-deshpande force-pushed the jira-babel-5846 branch 2 times, most recently from 63e74ed to 98c517f Compare June 24, 2025 04:45
@manisha-deshpande manisha-deshpande marked this pull request as ready for review June 24, 2025 04:46
@manisha-deshpande manisha-deshpande force-pushed the jira-babel-5846 branch 4 times, most recently from 3311e14 to 97d5efa Compare June 27, 2025 21:14
@manisha-deshpande
Copy link
Copy Markdown
Contributor Author

manisha-deshpande commented Jun 27, 2025

Updating the DATENAME function from STABLE to IMMUTABLE fails an existing test case BABEL_1475 due to disallowing the use of computed columns with DATENAME:

--- /home/runner/work/babelfish_extensions/babelfish_extensions/test/JDBC/./expected/BABEL-1475-vu-prepare.out	2025-06-27 21:14:12.077114693 +0000
+++ /home/runner/work/babelfish_extensions/babelfish_extensions/test/JDBC/./output/BABEL-1475-vu-prepare.out	2025-06-27 21:29:59.986827039 +0000
@@ -44,7 +44,9 @@
 CREATE TABLE BABEL_1475_vu_prepare_datename (dt date, year as DATENAME(year, dt), month as DATENAME(month, dt), weekday as DATENAME(dow, dt), dayofyear as DATENAME(dayofyear, dt), day as DATENAME(day, dt));
 INSERT INTO BABEL_1475_vu_prepare_datename (dt) values ('1912-10-25');
 GO
-~~ROW COUNT: 1~~
+~~ERROR (Code: 33557097)~~
+
+~~ERROR (Message: generation expression is not immutable)~~
 -- Test DATEPART function for computed columns
--- /home/runner/work/babelfish_extensions/babelfish_extensions/test/JDBC/./expected/BABEL-1475-vu-verify.out	2025-06-27 21:14:12.077114693 +0000
+++ /home/runner/work/babelfish_extensions/babelfish_extensions/test/JDBC/./output/BABEL-1475-vu-verify.out	2025-06-27 21:30:00.017827323 +0000
@@ -55,10 +55,9 @@
 -- Test DATENAME function for computed columns
 SELECT year, month, weekday, dayofyear, day from BABEL_1475_vu_prepare_datename;
 GO
-~~START~~
-text#!#text#!#text#!#text#!#text
-1912#!#October#!#Friday#!#299#!#25
-~~END~~
+~~ERROR (Code: 33557097)~~
+
+~~ERROR (Message: relation "babel_1475_vu_prepare_datename" does not exist)~~

cc: @KushaalShroff @Anikait143

KushaalShroff
KushaalShroff previously approved these changes Jul 1, 2025
@KushaalShroff
Copy link
Copy Markdown
Contributor

Please check Test failures!

Currently, DATENAME function returns a numeric value for TZOFFSET
datepart, which does not match SQL Server's text output.

Root cause: The function definition of sys.datename explicitly calls the
DATEPART function for most argument values, including TZOFFSET, thereby
returning a numerical offset value instead of the required text format
(+/-HH:MM).

Fix: Modified the sys function's SQL definition to extract and return
the timezone offset substring from the input - similar to how the
offset_string is determined in DATETRUNC/DATEBUCKET - if the argument
matches tzoffset. Furthermore, translated the function from SQL language
to plpgsql in order to enable raising an exception for unsupported input
datetime datatypes such as date, time, datetime and smalldatetime.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
In the datename function, before returning the tzoffset substring,
called a sys helper function `babelfish_conv_string_to_datetimeoffset` to
validate the date string and raise exceptions for invalid formats.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
DATENAME should support only  datetime2 and datetimeoffset datetime datatypes
as input when the datepart argument is TZOFFSET. SQL Server throws an
unsupported error for datatypes date, time, datetime, smalldatetime when
passed to datename(TZOFFSET, date). Modified a related test case to skip
failing inserts for the mentioned datepart-datatype combinations.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
Moved datepart, datename inserts SQL from the verify file of
datepart-before-16_10-or-17_6 to the prepare file in a view and tested
the view in the verify file. This is to avoid the verify SQL from
referencing the new definition of sys.DATENAME(TZOFFSET,...) and failing
in the version upgrade tests.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
Added datename_tzoffset-before-16_10-or-17_6-vu-prepare/verify/cleanup
test files to represent the difference in DATENAME output format before
function update. Previously, timezone offset output was numeric, now it
is in an offset string format '+/- HH:MM'.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
@forestkeeper forestkeeper merged commit debf90b into babelfish-for-postgresql:BABEL_5_X_DEV Jul 1, 2025
47 checks passed
manisha-deshpande added a commit to amazon-aurora/babelfish_extensions that referenced this pull request Jul 1, 2025
…h-for-postgresql#3846)

Currently, DATENAME function returns a numeric value for TZOFFSET
datepart, which does not match SQL Server's text output.

Root cause: The function definition of sys.datename explicitly calls the
DATEPART function for most argument values, including TZOFFSET, thereby
returning a numerical offset value instead of the required text format
(+/-HH:MM).

Fix: Modified the sys function's SQL definition to extract and return
the timezone offset substring from the input - similar to how the
offset_string is determined in DATETRUNC/DATEBUCKET - if the argument
matches tzoffset. Furthermore, translated the function from SQL language
to plpgsql in order to enable raising an exception for unsupported input
datetime datatypes such as date, time, datetime and smalldatetime.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
(cherry picked from commit debf90b)
manisha-deshpande added a commit to amazon-aurora/babelfish_extensions that referenced this pull request Jul 2, 2025
…h-for-postgresql#3846)

Currently, DATENAME function returns a numeric value for TZOFFSET
datepart, which does not match SQL Server's text output.

Root cause: The function definition of sys.datename explicitly calls the
DATEPART function for most argument values, including TZOFFSET, thereby
returning a numerical offset value instead of the required text format
(+/-HH:MM).

Fix: Modified the sys function's SQL definition to extract and return
the timezone offset substring from the input - similar to how the
offset_string is determined in DATETRUNC/DATEBUCKET - if the argument
matches tzoffset. Furthermore, translated the function from SQL language
to plpgsql in order to enable raising an exception for unsupported input
datetime datatypes such as date, time, datetime and smalldatetime.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
(cherry picked from commit debf90b)
forestkeeper pushed a commit that referenced this pull request Jul 3, 2025
* Fix for DATENAME() gives incorrect value with TZOFFSET part (#3846)

Currently, DATENAME function returns a numeric value for TZOFFSET
datepart, which does not match SQL Server's text output.

Root cause: The function definition of sys.datename explicitly calls the
DATEPART function for most argument values, including TZOFFSET, thereby
returning a numerical offset value instead of the required text format
(+/-HH:MM).

Fix: Modified the sys function's SQL definition to extract and return
the timezone offset substring from the input - similar to how the
offset_string is determined in DATETRUNC/DATEBUCKET - if the argument
matches tzoffset. Furthermore, translated the function from SQL language
to plpgsql in order to enable raising an exception for unsupported input
datetime datatypes such as date, time, datetime and smalldatetime.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
(cherry picked from commit debf90b)

* Fixed DATENAME to use datetimeoffset cast instead of sys.babelfish_conv_string_to_datetimeoffset

While sys.babelfish_conv_string_to_datetimeoffset is a helpful way to
validate datetimeoffset values before returning the offset string in
DATENAME(TZOFFSET,...) it is crrently not present in 4_X. (BABEL-4896).

Switching to a normal pg cast to datetimeoffset works as well, but does
not validate out of boundary offset hour values above +/-14:00 since PG
accepts upto hour 15.

The incorrect output in the out-of-boundary test case in
`datename+tzoffset-v-verify.sql` can be updated to the expected error
(SQL Server), once the root cause jira BABEL-5827 is fixed.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
(cherry picked from commit 3140b6c)
sharathbp pushed a commit to amazon-aurora/babelfish_extensions that referenced this pull request Sep 22, 2025
…h-for-postgresql#3895)

* Fix for DATENAME() gives incorrect value with TZOFFSET part (babelfish-for-postgresql#3846)

Currently, DATENAME function returns a numeric value for TZOFFSET
datepart, which does not match SQL Server's text output.

Root cause: The function definition of sys.datename explicitly calls the
DATEPART function for most argument values, including TZOFFSET, thereby
returning a numerical offset value instead of the required text format
(+/-HH:MM).

Fix: Modified the sys function's SQL definition to extract and return
the timezone offset substring from the input - similar to how the
offset_string is determined in DATETRUNC/DATEBUCKET - if the argument
matches tzoffset. Furthermore, translated the function from SQL language
to plpgsql in order to enable raising an exception for unsupported input
datetime datatypes such as date, time, datetime and smalldatetime.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
(cherry picked from commit debf90b)

* Fixed DATENAME to use datetimeoffset cast instead of sys.babelfish_conv_string_to_datetimeoffset

While sys.babelfish_conv_string_to_datetimeoffset is a helpful way to
validate datetimeoffset values before returning the offset string in
DATENAME(TZOFFSET,...) it is crrently not present in 4_X. (BABEL-4896).

Switching to a normal pg cast to datetimeoffset works as well, but does
not validate out of boundary offset hour values above +/-14:00 since PG
accepts upto hour 15.

The incorrect output in the out-of-boundary test case in
`datename+tzoffset-v-verify.sql` can be updated to the expected error
(SQL Server), once the root cause jira BABEL-5827 is fixed.

Task: BABEL-5846
Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
(cherry picked from commit 3140b6c)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants