Oracle - Get date from date time using to_date() - date

I getting an XML date/time from an XML file and need to load it in a datatype of just Date, so I have to truncate the time.
I would like to try something like this in order to let Oracle do the truncation:
TO_DATE('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD')
I verify this failed by running this:
SELECT TO_DATE('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD') FROM DUAL
It throws error: ORA-01830: data format picture ends before converting entire string.
I'm trying to minimize change to my C# program that is building the SQL statements. If I need to, I can change my C# code to generate this:
TO_DATE('2015-11-04','YYYY-MM-DD')
Can Oracle handle the truncation or must I do it in my program?

If you're storing your information in a DATE column in Oracle (which accepts date-and-time), then the following should help you see what you need to do:
select to_timestamp_tz('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD hh24:mi:ss.ff3tzh:tzm') tz,
to_date(to_char(to_timestamp_tz('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD hh24:mi:ss.ff3tzh:tzm'), 'dd/mm/yyyy hh24:mi:ss'), 'dd/mm/yyyy hh24:mi:ss') tz_char_date,
cast(to_timestamp_tz('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD hh24:mi:ss.ff3tzh:tzm') as date) tz_cast_date
from dual;
TZ TZ_CHAR_DATE TZ_CAST_DATE
---------------------------------------- ------------------- ---------------------
04/11/2015 13:45:19.387000000 -05:00 04/11/2015 13:45:19 2015-11-04 13:45:19
An alternative is to use substr to shorten the string to get the portion you're interested in:
select to_date(substr('2015-11-04 13:45:19.387-05:00', 1, 19), 'yyyy-mm-dd hh24:mi:ss') substr_date
from dual;
SUBSTR_DATE
---------------------
2015-11-04 13:45:19

You can use the to_timestamp_tz() function to convert the string from XML into a timestamp with timezone value:
SELECT TO_TIMESTAMP_TZ('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD HH24:MI:SS.FFTZH:TZM')
FROM DUAL;
TO_TIMESTAMP_TZ('2015-11-0413:45:19.387-05:00','YYYY-MM-DDHH24:MI:SS.FFTZH:TZM')
--------------------------------------------------------------------------------
04-NOV-15 13.45.19.387000000 -05:00
You can then use the trunc() function to truncate the time portion to midnight; this also converts it implicitly from a timestamp to a date:
SELECT TRUNC(TO_TIMESTAMP_TZ('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD HH24:MI:SS.FFTZH:TZM'))
FROM DUAL;
TRUNC(TO_TIMESTAMP_TZ('2015-11-0413:45:19.387-05:00','YYYY-MM-DDHH24:MI:SS.FFTZH
--------------------------------------------------------------------------------
04-NOV-15
This ignores the actual time zone - essentially assuming the values are in your system timezone (i.e. you are in the same -05:00 region).
You could also use a substring to strip the time and timezone part from the raw string before converting:
SELECT TO_DATE(SUBSTR('2015-11-04 13:45:19.387-05:00', 1, 10), 'YYYY-MM-DD')
FROM DUAL;
TO_DATE(SUBSTR('2015-11-0413:45:19.387-05:00',1,10),'YYYY-MM-DD')
-----------------------------------------------------------------
04-NOV-15
... or performing the substring in C# if you prefer, assuming that is parsing the XML document.
(These are intentionally displaying in a format that is different from the ISO standard so they don't look like the original string value from your XML; they are actual date/timestamp with time zone types, my client is just using my NLS settings.)
You also have the option of using Oracle's built-in XML handling to extract relational data from your document, but that's rather off-topic and might not be appropriate.

Related

how to convert "MMM-YY" from varchar to date?

Does anyone have any idea how to convert a date formatted "mmm-yy" (varchar) to "dd-mm-yyyy" or only from varchar data type to date data type.
It actually depends on what language or tool you are using, but there is almost certainly a builtin function that will help you do this.
Checkout
MySQL: DATE_FORMAT()
Oracle: SELECT TO_CHAR(SYSDATE, 'DD-MM-YYYY') FROM dual;
SQL Server: SELECT CONVERT(VARCHAR(10), GETDATE(), 120);
Or if you are doing this using a programing language i.e., Python, JavaScript etc. Just use the built in string replace methods to change the date into your desired format.

How to parse a ZoneDateTime format?

I'm currently trying to parse a timestamp that looks like: 2020-08-03T11:37:42.529Z[UTC]
This timestamp was generated with Java's ZoneDateTime data type
What I have already tried was to parse it via:
SELECT '2020-08-03T11:37:42.529Z[UTC]'::timestamp with time zone;
But that would fail with an exception (unless I parse up until timezone Z).
Edit
For clarification, this is currently a String that is saved in a file, so this application has no direct interaction with Java.
As explained by Adrian, the below code (using at time zone) does not really work. The only other alternative I can think of is to replace the timezone abbreviation with a proper offset. To do that, a function is probably the easiest solution:
create or replace function replace_tz_abbrev(p_input text)
returns text
as
$$
declare
l_offset_hours text;
l_tz_abbrev text;
begin
l_tz_abbrev := substring(p_input from '\[([A-Z]+)\]');
select to_char(utc_offset, 'hh24:mi')
into l_offset_hours
from pg_catalog.pg_timezone_abbrevs
where abbrev = l_tz_abbrev;
return regexp_replace(p_input, '\[[A-Z]+\]', l_offset_hours);
end;
$$
language plpgsql;
This is a rough sketch, the function needs some error checking in case the abbreviation doesn't exist. Maybe checking pg_timezone_names as a fallback to deal with names like Europe/Berlin.
The result of replace_tz_abbrev() can be cast to a timestamptz (at least with the given example). This can either be done in the function itself (and changing it to returns timestamptz or when calling it.
The below is not correct
(I'll just leave it here for reference, so that the comments
One way I can think of is to extract the time zone from the string and use it together with the to_timestamp() function:
with data (input) as (
values ('2020-08-03T11:37:42.529Z[UTC]')
)
select to_timestamp(input, 'yyyy-mm-dd"T"hh24:mi:ss.ms') at time zone substring(input from '\[([A-Z]+)\]')
from data;
Assuming the timestamp string always ends with [timezone] then:
select regexp_replace('2020-08-03T11:37:42.529Z[UTC]', '\[[^\]]*\]', '')::timestamp with time zone;
regexp_replace
-----------------------------
08/03/2020 04:37:42.529 PDT
Where regexp_replace() replaces the [timezone] with an empty string and then you cast the 2020-08-03T11:37:42.529Z portion to a timestamp with time zone.
You can't directly convert this format to a timestamp with timezone. if you are allowed to manipulate the string to get the timedate separtly from the timezone you can do this:
select TIMESTAMP 'The date and time go here'AT TIME ZONE 'timezone name or abbreviation go here';
select TIMESTAMP '2020-08-03T11:37:42.529'AT TIME ZONE 'Africa/Dar_es_Salaam';
select TIMESTAMP '2020-08-03T11:37:42.529'AT TIME ZONE 'UTC';
The reason you have to split the string is that like you discovered a simple conversion does not work and the function TO_TIMESTAMP() that allows you to specify the format does not support specifiying in the format that the string will contain a timezone, only a time offset (example: -03 hours).
-- No way to include the timezone name in the format param.
-- if you insist to add 'TZ'or 'tz' you will get "ERROR: "TZ"/"tz"/"OF" format patterns are not supported in to_date"
select TO_TIMESTAMP('2020-08-03T11:37:42.529Z[UTC]','YYYY-MM-DD HH24:MI:SS.MS')
The difference between the a timezone and a time offset is also why you might not want to do what you are trying to do, postgres does not store timezones (or time offsets). Instead it converts to UTC, and later on reads the column as UTC if the column is 'timestamp without timezone', if the column is 'timestamp with timezone' it converts from UTC to what ever the current session is set to. if you care about the timezone you should store it in another column.

Casting String type to Unix Date Amazon Athena

I'm looking to get a result in Amazon Athena were I can count the quantity of users created by day (or maybe by month)
But previous that I have to convert the unix timestamp to another date format. And this is where i fail.
My last goal is to convert this type of timestamp:
1531888605109
In something like:
2018-07-18
According to Epoch Converter
But when I try to apply the solution i saw in this quiestion: Casting unix time to date in Presto
I got the error:
[Simba]AthenaJDBC An error has been thrown from the AWS Athena client. SYNTAX_ERROR: line 1:13: Unexpected parameters (varchar) for function from_unixtime. Expected: from_unixtime(double) , from_unixtime(double, bigint, bigint) , from_unixtime(double, varchar(x)) [SQL State=HY000, DB Errorcode=100071]
This is my query:
select cast(from_unixtime(created)as date) as date_creation,
count(created)
from datalake.test
group by date_creation
Maybe I've to cast over the sctring because the data type of the field is not a date.
My table description: Link to the table description
line 1:13: Unexpected parameters (varchar) for function from_unixtime. Expected: from_unixtime(double)
This means that your timestamps -- even though they appear numeric -- are varchars.
You need to add a CAST to cast(from_unixtime(created)as date), like:
CAST(from_unixtime(CAST(created AS bigint)) AS date)
Note: When dealing with time-related data, please have in mind that https://github.com/prestosql/presto/issues/37 is not resolved yet in Presto.

Date Conversion Function PostgreSQL

I have the following function:
CREATE function SEMANA_ISO (fecha date) returns text as $$
select to_char(fecha, 'mm-dd-yyyy');
$$ LANGUAGE sql;
It works with:
Select SEMANA_ISO ('28/12/2014');
Select SEMANA_ISO ('01/01/2015');
Select SEMANA_ISO ('01/07/2015');
As you may see below
But not with:
Select SEMANA_ISO ('12/31/2014');
It shows:
********** Error **********
ERROR: The value of time / date is out of range "12/31/2014"
SQL state: 22008
Hint: You may need a different configuration of "dateStyle".
Character: 20
Do you have any suggestion without having to change the datestyle so I can enter
Select SEMANA_ISO ('12/31/2014');
And get an output of:
12-31-2014
using just one function to "parse" all dates?
Your function is declared to get a parameter of type date so you also need to pass such a value. '12/31/2014' is a character value, not a date.
When you pass a character literal (aka string) to the function Postgres is forced to do an implicit data type conversion based on the current datestyle - not something you should rely on.
If you want to call the function independently of the datestyle you need to pass a proper date literal, e.g. DATE '2014-12-31'
For more details on specifying date values, please see the manual: http://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-INPUT
If you want 12/31/2014 accepted as a date set datestyle to iso, dmy;
if you want 31/12/2014 accepted as a date set datestyle to iso, mdy;
you can't have both at the same time, else 4/1/2015 is ambiguos, and must be rejected.
There is to_date() to input date literals with an arbitrary (given) format:
SELECT to_date('12/31/2014', 'MM/DD/YYYY') AS date1
, to_date('31/12/2014', 'DD/MM/YYYY') AS date2;
That's "without changing any datestyle". Obviously, you need to provide a matching format pattern, though.
To output the same in any desired format, use to_char():
SELECT to_char(to_date('12/31/2014', 'MM/DD/YYYY'), 'DD/MM/YYYY') AS date_as_text

Perl DBD::ODBC Issues with Oracle Date Formats

I am using Perl's DBD::ODBC to connect to an Oracle database. However, an issue arises when I try to execute a select query using a date in the where clause. It seems this issue occurs because of the database's date format being DD-MON-RR (see DBD::ODBC::FAQ). Since I cannot change the database's settings, can anyone suggest a workaround?
The database's default date format only matters if you depend on it, which you should not in general. You can:
1) Specify the format of the date in your query:
select *
from news
where news_date = to_date ('01-DEC-2009','DD-MON-RRRR');
2) Use the ANSI standard for date literals:
select *
from news
where news_date = DATE '2009-12-01';
One option is to use the TO_DATE() function (or the ANSI 'DATE' keyword) to convert the format in every query:
WHERE date_field > TO_DATE('2009-11-01', 'YYYY-MM-DD');
-- or
WHERE date_field > DATE '2009-11-01'
If you have to do this a lot, a better option would be to set the format for the session:
$dbh->do("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
$dbh->do("ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SSxFF'");
$dbh->do("ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='YYYY-MM-DD HH24:MI:SSxFF'");
Then:
my $sth = $dbh->prepare(<<EOT);
SELECT date_field
FROM some_table
WHERE date_field > '2009-11-01'
EOT
Don't rely on implicit datatype conversion. You can always specify the date format in the where clause:
WHERE your_column = to_date(:your_parameter, 'yyyy/mm/dd')