Using the To_Date function is not converting to the expected format - sql

I'm trying to convert a char (output from a substr operation) into a dateTime format using TO_DATE with the following format - hh24:mi:ss.
The output of the substr looks fine but as soon as I run it through the TO_DATE function it converts every row for this column into 01-MAR-22.
To demonstrate I have the following:-
SUS_TIME2 shows what I get back from the SUBSTR and it looks fine at this point. SUS_TIME3 then shows what I get back after running it through the TO_DATE function, this where it converts it to 01-MAR-22
,SUBSTR((s.resolve_date-suspend_date),-16,9) sus_time2
,TO_DATE(SUBSTR((s.resolve_date-suspend_date),-16,9), 'hh24:mi:ss') sus_time3
Can anyone see what's going on here please? Thanks

Dates are stored in an internal representation and do not have any intrinsic format. What you are seeing is how your client is choosing to format the actual date value as a string for display, using your session's NLS_DATE_FORMAT setting (not always the case, but this is SQL Developer, so it is here.)
When you call to_date() with only the time components it defaults the date part to the first day of the current month, which is why you are seeing March 1st. That is a bit buried in the documentation:
If you specify a date value without a time component, then the default time is midnight. If you specify a date value without a date, then the default date is the first day of the current month.
But it is setting the time properly on that date, which you can see by either explicitly converting the date back to a string:
to_char(<your date>, 'YYYY-MM-DD HH24:MI:SS')
or by changing your session:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'
db<>fiddle
But you should not rely on NLS settings for anything except ad hoc queries; someone else running your code may have different settings. And only convert back to a string at the last moment when you have to display the value in a particular format - store it and pass it around as a native date.
Oracle doesn't have a time-only data type, so if you really only care about the time part then you can use a date (either defaulting to current month, or using an explicit fixed date) and ignore the date part; or potentially use an interval; or use the number of seconds the time represents (i.e. 0-86399). Which is suitable depends on what you'll use the value for.
It looks like you might be substringing the result of subtracting two timestamps; in which case (a) you already have an interval, and (b) you probably need to allow for that difference to span more than one day. You can also extract the individual time components directly from an interval value. So I'd question whether your approach is really appropriate.

You appear to be calculating s.resolve_date-suspend_date which gives you an INTERVAL DAY TO SECOND data type (implying that one or both of s.resolve_date or suspend_date is a TIMESTAMP data type) and then using SUBSTR to extract the time component of the data type and trying to convert that to a date and then display the time component.
Don't do that as using SUBSTR is fragile as it depends on the number of decimal places that the INTERVAL has which, in turn, will depend on the number of fractional seconds that the TIMESTAMP values have.
Just pick a date and add the interval to it and then format it as string to display it:
SELECT s.resolve_date,
suspend_date,
TO_CHAR(
DATE '1900-01-01' + (s.resolve_date-suspend_date),
'hh24:mi:ss'
) sus_time
FROM table_name s
Which, for the sample data:
CREATE TABLE table_name (resolve_date, suspend_date) AS
SELECT CAST(SYSTIMESTAMP AS TIMESTAMP(6)),
CAST(TRUNC(SYSTIMESTAMP) AS TIMESTAMP(6))
FROM DUAL;
Outputs:
RESOLVE_DATE
SUSPEND_DATE
SUS_TIME
2022-03-25 12:59:15.223445
2022-03-25 00:00:00.000000
12:59:15
From your comment:
all i'm trying to do is format the data so that when it's exported to Excel sorting works correctly.
That really is an XY-problem. You can solve it in Excel by either specifying the column format as a time when you import the data into Excel or right-click on the column header and "Format" the column picking the "time" data type with the correct format model.
You can also output the time as a fraction of a day with a numeric data type and then Excel can format it as the correct time using either:
SELECT s.resolve_date,
suspend_date,
MOD(CAST(s.resolve_date AS DATE)-CAST(suspend_date AS DATE), 1) AS sus_time
FROM table_name s;
or
SELECT s.resolve_date,
suspend_date,
TO_CHAR(DATE '1900-01-01' + (s.resolve_date-suspend_date), 'SSSSS')
/ 86400 AS sus_time
FROM table_name s
Which both output:
RESOLVE_DATE
SUSPEND_DATE
SUS_TIME
2022-03-25 13:16:40.204461
2022-03-25 00:00:00.000000
.5532407407407407407407407407407407407407
Which may not be human readable in that format but Excel will reformat it in a time column to 13:16:40.
db<>fiddle here

Related

Adding a day in Oracle but losing hour and minute, and format is also changing

I have a table tab1 in which a column col1 has data type VARCHAR2(50 BYTE) and this column has values like '9/27/21 18:05'
I want to add 1 day to this and I am expecting a result like '9/28/21 18:05'
If I do
TO_TIMESTAMP(col1,'MM/DD/YYYY HH24:MI') + INTERVAL '1' DAY
then I get '28-SEP-21 06.24.00.000000000 PM', and if I do
TO_DATE(col1,'MM/DD/YYYY HH24:MI') + INTERVAL '1' DAY'
then I get '28-SEP-21'.
Please note in both the above cases format is changing.
How can I get the result I want?
DATE and TIMESTAMP values are both binary data types that do NOT have a given format; therefore, when you convert a string to a DATE or a TIMESTAMP then the format you use is NOT stored.
If you want to convert it to a DATE or TIMESTAMP and then back to a string in the same format then, after adding the interval, you want to use TO_CHAR to convert back to a string with the required format.
For example:
SELECT TO_CHAR(
TO_DATE(col1, 'MM/DD/RR HH24:MI') + INTERVAL '1' DAY,
'MM/DD/RR HH24:MI'
)
FROM tab1
Note: If you use YYYY in the format model then 21 will be parsed as 21 AD and not as 2021 AD. Instead, you need to use YY or RR (depending on how you want values from the end of the last century to be handled).
Please note in both the above cases format is changing.
The format of your column is not changing, you have converted the strings to DATE and TIMESTAMP which are binary data type and do not have any format.
The user interface you are using (i.e. SQL/Plus or SQL Developer) tries to be helpful and rather than presenting you, the user, with binary data will use its internal rules to format the binary data as something you can read. SQL/Plus and SQL Developer will use the NLS_DATE_FORMAT session parameter for DATE values and the NLS_TIMESTAMP_FORMAT session parameter for TIMESTAMP values. These parameters can be set to different values for each user in each of their sessions so you should not rely on them to be consistent.
If you want a consistent format then wrap the date/timestamp in TO_CHAR to apply that consistent format.
You are converting your string into a date or timestamp, and adjusting it by a day. Your client then decides how to format that for display, usually using you session setting like NLS_DATE_FORMAT.
If you want to display (or store*) the value in a particular format then you should specify that, with to_char(), e.g.:
TO_CHAR(TO_DATE(col1,'MM/DD/YYYY HH24:MI') + INTERVAL '1' DAY,'MM/DD/YYYY HH24:MI')
09/28/0021 18:05
or if you want to suppress some leading zeros to match your original string you can toggle those with the FM modifier:
TO_CHAR(TO_DATE(col1,'MM/DD/YYYY HH24:MI') + INTERVAL '1' DAY,'FMMM/DD/YYYY HH24:FMMI')
9/28/21 18:05
As you can see in the output of first of those, and as #Aitor mentioned, the year comes out as 0021 rather than 21. That's because you used a four-digit YYYY mask for a 2-digit year value. In the second one the FM suppresses that, so it's less obvious. As you don't seem to care about the century it usually doesn't matter whether you use YY or RR - the exception maybe being if you happen to hit a leap year/day; but it's still better to have the mask match the string, so with RR:
TO_CHAR(TO_DATE(col1,'MM/DD/RR HH24:MI') + INTERVAL '1' DAY,'FMMM/DD/RR HH24:FMMI')
9/28/21 18:05
db<>fiddle
* But you should not be storing dates as strings. They should be stored as dates, and formatted as strings for display only. You shouldn't really be using 2-digit years any more either.
You said your column has this: 9/27/21, but you put a mask like YYYY. Be careful with that, because with YYYY, the year will be 21 BC...
Maybe you want RRRR in your date mask. RRRR means 2-digit years in the range 00 to 49 are assumed to be in the current century:
select to_char(to_date('9/27/2021 18:05','MM/DD/RRRR HH24:MI')+ INTERVAL '1' DAY,'MM/DD/YYYY HH24:MI') result
from dual;
Result: 09/28/2021 18:05
I don't know what is your output format, but anyway, if you want your date formatted like VARCHAR, try this. With your column, is something like that
select to_char(to_date(col1,'MM/DD/RRRR HH24:MI') + 1,'MM/DD/YYYY HH24:MI') result
from your_table;
Also you can use, instead of INTERVATL '1' DAY, a simple +1

No proper output for this SQL query

I m using this query to get a result of the difference between the start time and end time of an activity.
SELECT end_time,
NVL(end_time, (TO_Date(sysdate, 'dd/mm/yyyy hh24:mi:ss'))),
(end_time - start_time) * 24 * 60 difference_in_minutes
FROM NCR;
NCR is the name of the table. Start time and end time are in date datatype. Where the end time is NULL, I wanted to put the value as current system date and time. I am getting the results for every row except for the ones where the end time is NULL. Please help and guide
You appear to know the NVL function but you're not using it in your calculation. If you want the NVL to be a part of your calculation, it would need to be part of the calculation
(nvl(end_time,sysdate) - start_time) * 24 * 60 difference_in_minutes
sysdate is already a date so it makes no sense to pass it in to to_date. to_date doesn't take a date parameter, it only takes a string. If you pass a date to to_date, Oracle has to first convert the date to a string using your session's NLS_DATE_FORMAT. Then, to_date runs with whatever format mask you specified. At best, you're just converting it back into the same date you started with. If your session's NLS_DATE_FORMAT doesn't match the format mask you're passing in to to_date, however, you may get a different date back or you may get an error.

Is it possible to create a timestamp column which includes the meridian indicator in PostgreSQL?

I'm trying to create a column of type TIMESTAMP such that would store timestamps in the following format (i.e. including the "AM"/"PM" indicator):
20-04-2013 06:56:37 AM
I suppose I could use the to_char(..) function and store the timestamps as TEXT values, however isn't it possible to achieve the same effect with a vanilla TIMESTAMP column?
If you want to format a timestamp, use to_char when you SELECT it, or format the date in the client.
Timestamps don't have a format. They're stored as the number of fractional seconds since 1970-01-01 UTC inside the database and they're formatted into ISO timestamps for input and output. Inside the database they're just a number.
There is no way to override the default timestamp output format, since that would lead to a setting that could confuse applications that expect a single consistent format.
If the app wants a different date output format it has to ask for it with an appropriate to_char call.
Is this roughly what you are looking for?
select to_char(now(), 'DD-MM-YYYY HH12:MI:SS AM')

Issue with date formatting SQL

SELECT LISTING_EOD.LOCATION, LISTING_EOD.APPTTIME, LISTING_EOD.PERSON_ID,
LISTING_EOD.FORENAME, LISTING_EOD.SURNAME, LISTING_EODS.STATUS,
LISTING_EOD.DBDATE
FROM DBNAME.LISTING_EOD LISTING_EOD;
This query returns a list of data processed today, I need to modify to check yesterday's data. I have tried add the below line of code, but it doesn't return anything. Does anyone know how I can achieve this?
where LISTING_EOD.DBDATE = '18-OCT-2012';
If you always want yesterday's data, rather than hard-coding the date you can use:
WHERE LISTING_EOD.DBDATE >= TRUNC(SYSDATE) - 1
AND LISTING_EOD.DBDATE < TRUNC(SYSDATE)
TRUNC(SYSDATE) gives you midnight this morning, so if run today it would give a range between 18-Oct-2010 00:00:00 and 18-Oct-2012 23:59:59.
It's generally not a good idea to use implicit date format masks; your original code assumes your NLS_DATE_FORMAT is set to DD-MON-YYYY, but that might not be correct now (if you're seeing the time in the existing select then it probably isn't), and may well not be in the future. Always use an explicit date format mask, like TO_DATE('18-OCT-2012', 'DD-MON-YYY'), to avoid ambiguity and unexpected behaviour.
If the field is actually VARCHAR2 rather than a DATE - which is bad - then you'll need to convert the date range to a string to get a match:
WHERE LISTING_EOD.DBDATE >= TO_CHAR(TRUNC(SYSDATE) - INTERVAL '1' DAY, 'DD-MON-YYYY HH24:MI:SS')
AND LISTING_EOD.DBDATE <= TO_CHAR(TRUNC(SYSDATE) - INTERVAL '1' SECOND, 'DD-MON-YYYY HH24:MI:SS')
That will work for a single day, just, but you'd have problems looking for a date range. It's much better and safer to store data in a column of the appropriate type.
Dates in Oracle by default contain time as well. If you just specify '18-OCT-2012', it will only match 18-OCT-2012 00:00:00'. One way to get around this is to format your database date to what you are comparing it to, e.g. to_char(LISTING_EOD.DBDATE, 'DD-MON-YYYY') and compare this to '18-OCT-2012'. This comparison will disregard time completely.
If you had a date variable to compare with instead of a string, format this using the same date mask used for the database date. This also gets around any assumptions abut default date format on the database in question.
I realised the 'table' I was querying was a view, examined it inside sqldeveloper, and added '-1' to the sysdate. This query then returned the previous days results.
I successfully retrieved the correct data, thanks for all help received.

How to save date into 24 hours format in oracle

I am new to Oracle, and I need to save date and time in an Oracle database.
I am using time stamp as datatype for row. But now my problem is it saves date and time in 12 hours format like this 17/11/2011 10:10:10 PM.
But I need it in 24 hours format like 17/11/2011 22:10:10. I didn't understand the results that Google search result provided. Can any one please help me by posting some code.
Oracle always stores timestamps (and dates) in a packed binary format that is not human readable. Formatting is done only when a timestamp (or a date) is converted to a string.
You can control the formatting of your output by coding an explicit to_char. For example
SELECT to_char( your_timestamp_column, 'DD/MM/YYYY HH24:MI:SS' )
FROM your_table
Oracle stores timestamps in an internal format (with a default representation).
You can customize this representation on output like with the to_char() function.
For input (into the database) you can use to_date().