New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add FORMAT clause to convert datetime types to string and vice versa #2388 #7629
Add FORMAT clause to convert datetime types to string and vice versa #2388 #7629
Conversation
This functionality is in the SQL:2016 standard, so it would be nice to add it to Firebird. The only thing that seems to me it would be nice to expand it with named time zones. |
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 It would be good if it could be controlled in the case of the month and day so that it appears with a leading zero. 14.06.2023 15:41:29 is 2460110 Julian day |
Can you quote relevant SQL:2016 section? Also, are you aware of other DBMS engine implementing this? |
Adriano: #2388 |
+1 |
I see 2 variants how to implement this:
Which would be better? |
AFAIR "MM" is used for zero-padded value and "M" for non-padded. The rest the same. |
I thought about it, but it won't work with "DD", because "D" is already taken for Day of the Week. But maybe we can remove Day of the Week, if its not in standard, and use "D" for non-padded "DD". |
the problem concerns not only days and months. Minutes and seconds can also be with or without leading zero. |
The standard does not seem to mention whether DD etc are zero-padded or not, but usually (in other languages) two-letter format abbreviation means exactly that.. So I'd suggest this being our default behaviour for all elements that are affected. This would also be consistent with our default date->char formatting:
note the leading zeroes. |
Oracle uses TZD for time zone abbreviations and TZR for time zone names. They're non-standard, but could be followed. |
I'm wondering where the non-standard format specifiers originate from? Oracle, I guess? BTW, this is the default Oracle behaviour for padding:
So DD is 01, MM is 01, etc |
If im correct, we cannot get timezone abbreviation specifically, its stored in same place as timezone name, so i only add "TZR". But i can not add unit tests for this flag because fb_get_master_interface is not available at this moment. |
I also have a question about truncating string, for example if we do |
I think so. |
I do not have access to the SQL 2016 standard, and in #2388 I see nothing about quotes. Is this standard? Isn't there a way to put literal double quotes in the output? |
This looks incorrectly in relation to time zone:
In the case of |
Is it correct that tokens left in the trailing part is not matched with the format and do not generate error?
|
Please put the documentation (currently in the PR description) in a README formatted as markdown. |
It's not in standard. I saw it in PostgreSQL and I thought it would be a good addition.
No... I will fix it. |
From code perspective - it's correct, because we filled out our FORMAT, and there is no reason to continue parsing the input string. |
if (format.hasData()) | ||
{ | ||
dsqlScratch->appendUChar(blr_cast_format); | ||
dsqlScratch->appendString(0, format); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are ignoring the (implicit or explicit) connection charset (not _WIN1252
in the example):
SELECT CAST(localTIMESTAMP AS VARCHAR(45) FORMAT _WIN1252 'DD.MM.YEAR HH24:MI:SS "is" J "Julian day"') FROM RDB$DATABASE;
src/common/cvt.cpp
Outdated
if (format.isEmpty()) | ||
cb->err(Arg::Gds(isc_sysf_invalid_null_empty) << Arg::Str(STRINGIZE(format))); | ||
|
||
auto invalidPatternException = [](std::string_view pattern, Callbacks* cb) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::string_view
is C++17 feature, not allowed to use in current Firebird code.
But since this should not go to Firebird 5, I will propose adoption of C++17 for Firebird 6 code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see other people agree to move to C++ 17, so I switch back static to inline variables.
Use session timezone if timezone is not specified. Add ability to use + sign in timezone offset. Add truncating string exception.
Also add extra zeros to the year patterns.
69daaa3
to
9b4f135
Compare
I wonder why this code is so much complicated? Template that is instantiated with only one type and only one callback parameter can be a plain function. Single-line callback for callback is mind-screwing. to_upper() is locale-aware and is overkill in this case. If SQL standard doesn't require case insensitive format, I would make patterns case-sensitive. Because you are not going to use locale-specific separators (do you?) everything that doesn't match pattern can be treated as plain text and appear in output as-is. In this case the parser is simplified to comparison with patterns starting from the longest and plain char* does the job. |
Yea, it would be better.
It's not mentioned in standard, but I think it gives more freedom when using format patterns. |
I think you shouldn't. |
| 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) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why TZR
is not supported here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot about this... But this is very tricky to implement, because time zone names contains separators, for example Canada/East-Saskatchewan - it contains '/' and '-', and it's not easy to parse. I've written an implementation of this, but maybe it's too overwhelming for this kind of task. If you think it's not a good solution, I'll try to think of something else.
1. DATETIME TO STRING
The following flags are currently implemented for datetime to string conversion:
The dividers are:
Patterns can be used without any dividers:
However, be careful with patterns like
DDDDD
, it will be interpreted asDDD
+DD
.It is possible to insert raw text into a format string with "":
... FORMAT '"Today is" DAY'
- Today is MONDAY.Also the format is case-insensitive, so
YYYY-MM
==yyyy-mm
.Example:
2. STRING TO DATETIME
The following flags are currently implemented for string to datetime conversion:
Dividers are the same as for datetime to string conversion and can also be omitted.
Example: