ORA-01850: hour must be between 0 and 23 - sql

Need assistance on Converting a Military to Standard Time.
I have the code below: However, it gives me error ORA-01850
select to_char (to_date(tst_time, 'HH24MI'), 'HH:MI AM') integer_time FROM STATUS_HISTORY
where tst_id = '75344'
I'm thinking that its because the value on tst_time only has 3 characters for the first 2 entry. How can I add 0 on the beginning of the tst_time if it contains only 3 numbers?
Table Value Expected Result
TST_Time integer_time
958 09:58 AM
541 05:41 AM
1609 04:09 PM

If I understand correctly, you can use lpad():
select to_char(to_date(lpad(tst_time, 4, '0'), 'HH24MI'), 'HH:MI AM') integer_time
from (select '1609' as tst_time from dual union all
select '948' from dual
)
This adds a zero in front when necessary.

Assuming tst_time is a number - which seems likely; if it was a string it would make sense for it to already have the leading zero - then you are doing an implicit conversion to a string with the to_date() call.
If you make that an explicit conversion then you can introduce the leading zero(s) at that point:
to_char(to_date(to_char(tst_time, 'FM0000'), 'HH24MI'), 'HH:MI AM')
The to_char(tst_time, 'FM0000') converts 958 to 0958. (The FM part stops it having a leading space to allow for a +/- sign; doesn't really matter here though.)
db<>fiddle

Related

Ora-01821 date format not recognized

I want to change the string into date field but I received the error ora- 01821 date format not recognized while executing the following query.also I need am/pm in the db .how to achieve that
update bucket set closed = to_date(closed_on,'dd-mon-yy hh.mi.ss.ff a.m')
Closed_on value will be like 29-MAY-19 09.01.16.00000000 PM
I think you want to use TO_TIMESTAMP - date cannot store your milliseconds, so it doesn't understand the FF
select to_timestamp('29-MAY-19 09.01.16.00000000 PM','dd-mon-yy hh.mi.ss.FF8 AM') from dual
Note, as GMB has pointed out, you can't get oracle to parse a.m - you'll have to make it either a.m. or am. No half measures :) (hopefully its a copy pasta error)
If you're desperate to use TO_DATE, cut the milliseconds off:
select
to_date(
SUBSTR('29-MAY-19 09.01.16.00000000 PM', 1, 18) || SUBSTR('29-MAY-19 09.01.16.00000000 PM', -2, 2),
'dd-mon-yy hh.mi.ssAM'
)
from dual
If your time str is a.m. make the second SUBSTR use , -4, 4) - "start from right, 4 places left, then take 4 chars"
If the millis are always 00000000 you could neaten this up with a REPLACE(timeStr, '.00000000 ', '') instead
Fractional seconds (FF) do not with data type DATE. So if your table's column closed_on is of type Date, it will not work.
Caius Jard have suggested good alternative solutions.
Another option is to skip the characters using # as follows:
SQL> SELECT
2 TO_DATE('29-MAY-19 09.01.16.00000000 PM', 'DD-MON-YYYY HH.MI.SS.######## AM')
3 FROM
4 DUAL;
TO_DATE('29-MAY-1909.01
-----------------------
29-MAY-0019 09.01.16 PM
SQL>
The number of # should be number of characters to be skipped.

How to convert decimal to time in Oracle SQL?

In a certain table I'm querying, time is stored like this:
09:30 is stored as 9,3
14:25 is stored as 14,25
How can I, using SQL, convert 9,3 to 09:30 (hh:mm format)?
So far, the only thing I could find in other questions are people who need to convert 9,5 to 09:30 (in my case, 9,5 means 09:50).
Right now, I'm using PHP to convert this. But my DB server is better than my application server. Since I need to get a lot of data, it would be better to solve this using SQL.
Sorry if this is a stupid question, but I'm new to SQL. Feel free to ask for more questions if you need to.
Assuming those are numeric values, you can get the hour part with trunc(your_column) and the minutes part with 100 * mod(your_column, 1). That gives you two numbers; you can format those and concatenate them, again assuming you want a string result, e.g.:
to_char(trunc(your_column), 'FM00') ||':' || to_char(100 * mod(your_column, 1), 'FM00')
Or more simply, format the whole number as a single string in one step, by telling it to use a colon as the decimal separator (with anything as the group separator - that isn't used):
to_char(your_column, 'FM00D00', 'NLS_NUMERIC_CHARACTERS=:,')
Demo with sample data in a CTE:
-- CTE for sample data
with your_table (time) as (
select 9.3 from dual
union all select 9.03 from dual
union all select 14.25 from dual
)
-- actual query
select time,
to_char(trunc(time), 'FM00') ||':' || to_char(100 * mod(time, 1), 'FM00') as new_time1,
to_char(time, 'FM00D00', 'NLS_NUMERIC_CHARACTERS=:,') as new_time2
from your_table;
TIME NEW_TIME1 NEW_TIME2
---------- --------- ---------
9.3 09:30 09:30
9.03 09:03 09:03
14.25 14:25 14:25
If you actually want an interval data type result then you can use the same split with trunc/mod but handle the two numbers differently:
select time,
numtodsinterval(trunc(time), 'HOUR')
+ numtodsinterval(100 * mod(time, 1), 'MINUTE') as new_time
from your_table;
TIME NEW_TIME
---------- -------------------
9.3 +00 09:30:00.000000
9.03 +00 09:03:00.000000
14.25 +00 14:25:00.000000
Or you can convert to that time on a nominal date if you prefer, e.g.:
date '2000-01-01'
+ trunc(your_column), 'HOUR')
+ numtodsinterval(100 * mod(your_column, 1), 'MINUTE')
At some point you'll run into problems if you have values that don't really represent valid times - for example, 9.60 or 25.3. Either of the queries above will handle that to some extent - at least, up to 99.99 for the first one - but won't necessarily give useful results. The string would give you '09:60' and '25:30', while the interval would give you '0 10:00:00' and '1 01:30:00', which is slightly more sensible perhaps. That's the danger of using an inappropriate data type though.
Preserving a comment from #BobJarvis, if you actually want a date data type result then you can convert the number to a string without any separator and then convert from that to a date:
to_date(to_char(your_column, '00V00'), 'HH24MI')
which will give you that time on the first day of the current month. However, that will error with 9.60 or 25.3.

convert timestamp format to integer in oracle

I have timestamp value in the format "01-JAN-00 10.05.54.170489000 AM". When I tried to convert to integer using the format "yyyymmddhhmm24missff":
select to_number(to_char(date_column,'yyyymmddhhmm24missff') from table_name;
I am getting the value as 20000101100554170489.The last 3 digits are missing. I need exact value as 20000101100554170489000.
How can I keep those last three digits in my number?
Your query (with minor correction) gets the result you wanted when I run it with a plain timestamp, supplied via a CTE:
set numwidth 23
alter session set nls_timestamp_format = 'DD-MON-RR HH:MI:SS.FF AM';
with table_name (date_column) as (
select to_timestamp('01-JAN-00 10.05.54.170489000 AM', 'DD-MON-RR HH:MI:SS.FF AM')
from dual
)
select date_column, to_number(to_char(date_column,'yyyymmddhh24missff')) as result
from table_name;
DATE_COLUMN RESULT
------------------------------- -----------------------
01-JAN-00 10:05:54.170489000 AM 20000101100554170489000
The only way I can see to get the result you actually have is if your timestamp column is actually constrained to that precision; i.e. date_column timestamp(6):
with table_name (date_column) as (
select cast(to_timestamp('01-JAN-00 10.05.54.170489000 AM', 'DD-MON-RR HH:MI:SS.FF AM')
as timestamp(6))
from dual
)
select date_column, to_number(to_char(date_column,'yyyymmddhh24missff')) as result
from table_name;
DATE_COLUMN RESULT
------------------------------- -----------------------
01-JAN-00 10:05:54.170489000 AM 20000101100554170489
It seems odd to want to show more precision than the value is actually allowed to have, but if you really want to always include those last three zeros then you can override the column precision by using ff9:
with table_name (date_column) as (
select cast(to_timestamp('01-JAN-00 10.05.54.170489000 AM', 'DD-MON-RR HH:MI:SS.FF AM')
as timestamp(6))
from dual
)
select date_column, to_number(to_char(date_column,'yyyymmddhh24missff9')) as result
from table_name;
DATE_COLUMN RESULT
------------------------------- -----------------------
01-JAN-00 10:05:54.170489000 AM 20000101100554170489000
A timestamp (or date) doesn't have an intrinsic format, you are just seeing it converted to a string by your client, probably based on your session NLS settings. I've run an alter session to match what you seem to be using. Notice that even when I constrained the data type to timestamp(6), the client's default formatting using ff still showed nine digits of precision. Seeing all nine when you query the table doesn't mean the values can actually have that much precision, and the last three digits displayed will always be zeros in this case.

Oracle SQL: Converting a flexible integer into time

i can convert a 4-digit-number (2300 e.g.) into (23:00) with the following Statement:
select to_char ( to_date ( 2300, 'HH24MI'), 'HH:MI') integer_time from dual;
Result:
INTEGER_TIME
--------
11:00
But how can I help myself if the values in the database are in addition stored as three digit value, if the first value is below then 10 (e.g. '937' for 9:37).
I kinda Need a double check Statement for this, anyone got a clue?
You need to LPAD it with '0' first
SELECT LPAD('937', 4, '0') FROM dual
A call to LPAD(.., 4) with a 4 character string is a no-op
SELECT LPAD('1937', 4, '0') FROM dual
You can use TO_CHAR with the format 'fm0999' if the value is stored as a NUMBER
select to_date(to_char(937, 'fm0999'), 'HH24MI') from dual;
see format models
As an alternative, you could avoid bouncing through a nominal date and just use number and string manipulation:
select to_char(trunc(937/100), 'FM00') ||':'|| to_char(mod(937, 100), 'FM00') from dual;
TO_CHAR
-------
09:37
... though if your starting value is actually a string rather than a number there's another level of implicit conversion going on. (If it is a string you probably have bigger problems with validation, but the lpad route might be the simplest way if you assume the data is always as expected).
With a range of values:
with t (n) as (
select 0 from dual
union all select 1 from dual
union all select 59 from dual
union all select 937 from dual
union all select 2300 from dual
union all select 2359 from dual
)
select n, to_char(trunc(n/100), 'FM00') ||':'|| to_char(mod(n, 100), 'FM00')
as integer_time
from t;
N INTEGER
---------- -------
0 00:00
1 00:01
59 00:59
937 09:37
2300 23:00
2359 23:59
If you don't want the leading zero - i.e. 937 instead of 09:37 - then change the first format model to FM90.
If your data isn't constrained to be integers in the range 0-2359 then this would format 'bad' times, possibly with hashes for the hour part, while bouncing through a date would error. Neither is ideal, but hopefully is a moot point.

Using TO_DATE() with AM/PM Formatting

I am trying to select some dates from a table where the format of the dates is like this:
14-APR-14 10.35.00.0000000000 AM
01-NOV-16 02.43.00.0000000000 PM
Note that the dates can be either AM or PM, but when I try to do a simple SELECT from the table such as:
SELECT * FROM MyTable
WHERE TO_DATE(MyDate, 'DD-MON-YYYY HH:MI:SS AM') > '31-DEC-2016 08:00:00 AM';
I get the error:
ORA-01855: AM/A.M. or PM/P.M. required
I've been trying to get this work for some time but with no luck. Any help here would be appreciated.
Several problems.
Your inputs are obviously strings, since they have ten decimal places and timestamps in Oracle have at most 9. Then, strings with fractions of a second can't be converted to a date with to_date - you need to use to_timestamp or else you need to remove all the fractional parts. In the solution below I only remove the last (the tenth) decimal, since you may have non-zero fractional parts in the table - although not in the sample you posted.
Then, your format mask has yyyy but your inputs have only two digits for the year (which probably means 93 means 1993 and not 2093, so the correct thing to use would be rr rather than yy). And you use : in the format mask where your inputs use .
Finally, don't even compare dates in string format: in string comparisons, 01-JAN-2015 is before 20-NOV-2013.
You probably want something like this:
select mydate
from (
select '14-APR-14 10.35.00.0000000000 AM' as mydate from dual
union all
select '01-NOV-16 02.43.00.0000000000 PM' from dual
) mytable
where to_timestamp(substr(mydate, 1, 28) || substr(mydate, -3), 'dd-MON-rr hh.mi.ss.ff AM')
> to_timestamp('31-DEC-2016 08:00:00 AM', 'dd-MON-yyyy hh:mi:ss AM');
This query compiles correctly, and it produces no rows in the output (for obvious reasons).
NOTE: In a comment you (the OP) say the mydate field is a timestamp(6) datatype. Hard to believe (you show ten decimal places), but if indeed it is a timestamp or date, then you don't need to wrap it within any to_timestamp or to_date function, it should stand alone in the left-hand side of the inequality.
From your comment:
It's actually a timestamp; not a string. Timestamp(6) to be precise
You can just use a TIMESTAMP literal:
SELECT *
FROM MyTable
WHERE MyDate > TIMESTAMP '2016-12-31 08:00:00';