I have a timestamp column in one of my tables. I am doing the following and its displayed as 12.00.00.100000 AM but when the time passes 12AM its displayed in military time as I want it.
How can I display the date as military time 12AM time as 00:00:00.100000
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF';
SEQ_NUM DT
1 01-JAN-22 12.00.00.000000 AM
2 01-JAN-22 12.05.00.100000 AM
3 01-JAN-22 12.10.00.200000 AM
4 01-JAN-22 12.15.00.300000 AM
5 01-JAN-22 12.20.00.400000 AM
6 01-JAN-22 12.25.00.500000 AM
7 01-JAN-22 12.30.00.600000 AM
8 01-JAN-22 12.35.00.700000 AM
9 01-JAN-22 12.40.00.800000 AM
10 01-JAN-22 12.45.00.900000 AM
11 01-JAN-22 12.50.00.000000 AM
12 01-JAN-22 12.55.00.100000 AM
13 01-JAN-22 01.00.00.200000 AM
14 01-JAN-22 01.05.00.300000 AM
If your data type is TIMESTAMP then use:
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF';
If your data type is TIMESTAMP WITH TIME ZONE then use:
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF TZR';
or
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM';
However, any user can change their own session parameters at any time so if you want a particular format then you are better to change from outputting a TIMESTAMP to outputting a string containing the value in the given format using TO_CHAR:
SELECT seq_num,
TO_CHAR(dt, 'YYYY-MM-DD HH24:MI:SS.FF') AS dt
FROM table_name;
db<>fiddle here
You appear to have a plain TIMESTAMP column, not a TIMESTAMP WITH TIME ZONE column. (Or it could be TIMESTAMP WITH LOCAL TIME ZONE, if your session is set up as UTC/GMT.)
You are setting NLS_TIMESTAMP_TZ_FORMAT, but that applies to TIMESTAMP WITH TIME ZONE, not plain TIMESTAMP.
If you set the relevant NLS parameter instead (without the _TZ part):
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF';
then you get:
SEQ_NUM
DT
1
2022-01-01 00:00:00.000000
2
2022-01-01 00:05:00.100000
3
2022-01-01 00:10:00.200000
4
2022-01-01 00:15:00.300000
5
2022-01-01 00:20:00.400000
6
2022-01-01 00:25:00.500000
7
2022-01-01 00:30:00.600000
8
2022-01-01 00:35:00.700000
9
2022-01-01 00:40:00.800000
10
2022-01-01 00:45:00.900000
11
2022-01-01 00:50:01.000000
12
2022-01-01 00:55:01.100000
13
2022-01-01 01:00:01.200000
14
2022-01-01 01:05:01.300000
db<>fiddle with plain TIMESTAMP, or with TIMESTAMP WITH LOCAL TIMEZONE, which gets the same output as the session time zone is GMT.
Or you can use to_char() with the same format mask, so you aren't relying on session NLS settings.
Related
I have a table, which contains a timestamp. I want each row an INTERVAL apart. In my example below I am using a15 minutes interval
My first solution appears to work perfectly except that the fractional part of the timestamp is always .000000, which I expect but don't want. I'd like it to contain some other numbers.
In my second attempt I'm trying to INCREMENT the fractional part of the timestamp by .100000 the problem with this solution is if I'm creating many rows (1344) in my example, the seconds part of the timestamp gets incremented by 1 second after 10 rows are inserted. See the second solution below. I don't want that either.
Thirdly, I thought perhaps a regexp_replace solution would work where I could chop off the integer part of the second solution (keep the fractional part) and then add that to my interval. That attempt failed with an error. See third attempt below.
Is there a way I can get this to work? Where I can change the fractional part of the timestamp without it affecting the MMDDYYYY HH24:MI:SS part of the date.
Below is my code and attempts along with an example of the sample output I'm looking to generate.
Attempt #1 fractional part always .000000
CREATE TABLE t3 (
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
dt TIMESTAMP );
/
INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00',
numtodsinterval(15,'MINUTE') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/
SELECT * FROM T3 ORDER BY SEQ_NUM
SEQ_NUM DT
1 01-JAN-22 12.00.00.000000 AM
2 01-JAN-22 12.15.00.000000 AM
3 01-JAN-22 12.30.00.000000 AM
4 01-JAN-22 12.45.00.000000 AM
5 01-JAN-22 01.00.00.000000 AM
6 01-JAN-22 01.15.00.000000 AM
…
...
1342 14-JAN-22 11.15.00.000000 PM
1343 14-JAN-22 11.30.00.000000 PM
1344 14-JAN-22 11.45.00.000000 PM
Attempt #2 notice the seconds change at seq_num 1355. It went from :00 to :01
TRUNCATE TABLE T3;
/
INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00',
numtodsinterval(15,'MINUTE') +
numtodsinterval( (rownum * .100000), 'SECOND') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/
SELECT * FROM T3 ORDER BY SEQ_NUM
SEQ_NUM DT
1345 01-JAN-22 12.00.00.000000 AM
1346 01-JAN-22 12.15.00.100000 AM
1347 01-JAN-22 12.30.00.200000 AM
1348 01-JAN-22 12.45.00.300000 AM
1349 01-JAN-22 01.00.00.400000 AM
1350 01-JAN-22 01.15.00.500000 AM
1351 01-JAN-22 01.30.00.600000 AM
1352 01-JAN-22 01.45.00.700000 AM
1353 01-JAN-22 02.00.00.800000 AM
1354 01-JAN-22 02.15.00.900000 AM
1355 01-JAN-22 02.30.01.000000 AM
1356 01-JAN-22 02.45.01.100000 AM
…
…
Attempt #3 failed
TRUNCATE TABLE T3;
/
INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00',
numtodsinterval(15,'MINUTE') +
regexp_replace(
numtodsinterval( (rownum * .100000), 'SECOND'), '[^.]+\.(.*)$', '0.\1') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/
ORA-30081: invalid data type for datetime/interval arith
Desired output
SEQ_NUM DT
1345 01-JAN-22 12.00.00.000000 AM
1346 01-JAN-22 12.15.00.100000 AM
1347 01-JAN-22 12.30.00.200000 AM
1348 01-JAN-22 12.45.00.300000 AM
1349 01-JAN-22 01.00.00.400000 AM
1350 01-JAN-22 01.15.00.500000 AM
1351 01-JAN-22 01.30.00.600000 AM
1352 01-JAN-22 01.45.00.700000 AM
1353 01-JAN-22 02.00.00.800000 AM
1354 01-JAN-22 02.15.00.900000 AM
1355 01-JAN-22 02.30.00.000000 AM
1356 01-JAN-22 02.45.00.100000 AM
1357 01-JAN-22 03.00.00.200000 AM
…
…
You can use:
INSERT into t3 (dt)
SELECT TIMESTAMP '2022-01-01 00:00:00'
+ (LEVEL - 1) * INTERVAL '15' MINUTE
+ MOD(LEVEL - 1, 10) * INTERVAL '0.1' SECOND
FROM DUAL
CONNECT BY
TIMESTAMP '2022-01-01 00:00:00'
+ (LEVEL - 1) * INTERVAL '15' MINUTE
+ MOD(LEVEL - 1, 10) * INTERVAL '0.1' SECOND < DATE '2022-01-15';
or:
INSERT into t3 (dt)
SELECT TIMESTAMP '2022-01-01 00:00:00'
+ NUMTODSINTERVAL((LEVEL-1)*15*60 + MOD(LEVEL-1, 10)/10, 'SECOND')
FROM DUAL
CONNECT BY
TIMESTAMP '2022-01-01 00:00:00'
+ NUMTODSINTERVAL((LEVEL-1)*15*60 + MOD(LEVEL-1, 10)/10, 'SECOND')
< DATE '2022-01-15';
Which both give the values:
SEQ_NUM
DT
1
2022-01-01 00:00:00.000000
2
2022-01-01 00:15:00.100000
3
2022-01-01 00:30:00.200000
4
2022-01-01 00:45:00.300000
5
2022-01-01 01:00:00.400000
6
2022-01-01 01:15:00.500000
7
2022-01-01 01:30:00.600000
8
2022-01-01 01:45:00.700000
9
2022-01-01 02:00:00.800000
10
2022-01-01 02:15:00.900000
11
2022-01-01 02:30:00.000000
db<>fiddle here
It looks like you just want to add an interval of 15 minutes and 0.1 seconds
select level seq_num,
timestamp '2022-01-01 00:00:00' +
(level-1) * interval '15' minute +
(level-1) * interval '0.1' second dt
from dual
connect by level <= 10
Here's a dbfiddle that shows it producing the output you want.
I have 2 columns in my table both of varchar2. I have a query like
SELECT MYCOLUMN_TIME||' '||MYCOLUMN_TIME_AMPM
FROM MYTABLE
I am getting output 0910 am. I have tried
SELECT TO_CHAR(TO_DATE(MYCOLUMN_TIME,'hh24miss'), 'hh24:mi:ss')||' '||MYCOLUMN_TIME_AMPM
FROM MYTABLE
With this query I am getting Output 09:10:00 pm.
I want Output like 21:10:00 pm. How can we achieve this? Please help.
When using HH24 format mask, 21 hours equals 9 PM. There's no point in having PM along with 21, is there?
When you convert a string (0910) concatenated with am/pm to a date, you use TO_DATE function with appropriate format mask. Date values - in Oracle - contain both date and time component (see datum_1 in the following example). Then apply TO_CHAR to such a result in order to display it as you want (again, by applying desired format mask) - that's datum_2.
For example:
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh:mi pm';
Session altered.
SQL> with mytable (mycolumn_time, mycolumn_time_ampm) as
2 (select '0910', 'am' from dual union all
3 select '1150', 'pm' from dual
4 )
5 select mycolumn_time,
6 mycolumn_time_ampm,
7 to_date(mycolumn_time ||' '||mycolumn_time_ampm, 'hhmi pm') datum_1,
8 --
9 to_char(to_date(mycolumn_time ||' '||mycolumn_time_ampm, 'hhmi pm'), 'hh24:mi') datum_2
10 from mytable;
MYCO MY DATUM_1 DATUM_2
---- -- ------------------- -------
0910 am 01.09.2020 09:10 AM 09:10
1150 pm 01.09.2020 11:50 PM 23:50
SQL>
If you add PM format mask, you'd get
<snip>
9 to_char(to_date(mycolumn_time ||' '||mycolumn_time_ampm, 'hhmi pm'), 'hh24:mi pm') datum_2
10 from mytable; ^^
here
MYCO MY DATUM_1 DATUM_2
---- -- ------------------- --------
0910 am 01.09.2020 09:10 AM 09:10 AM
1150 pm 01.09.2020 11:50 PM 23:50 PM
SQL>
but - as I previously said - it doesn't make sense. There's no e.g. 23:50 AM, but it makes perfect sense in 11:50 AM or 11:50 PM.
I would like to calculate time diff between to dates in a different time zone. I am converting this two times to Greenwich time. But when I am converting dates in Europe/Moscow timezone the offset is different although there is no daylight saving.
This is the case I checked:
SELECT TO_DATE('5/20/2018 10:05:00 PM','mm/dd/yyyy hh:mi:ss am'),
TO_DATE('5/20/2018 10:05:00 PM','mm/dd/yyyy hh:mi:ss am')
- extract(TIMEZONE_HOUR from from_tz (TO_TIMESTAMP('5/20/2018 10:05:00 PM','mm/dd/yyyy hh:mi:ss am') ,'GMT' ) at time zone 'Europe/Moscow')/24 deptime_GRINICH
from dual
UNION
SELECT TO_DATE('5/24/2018 11:35:00 PM','mm/dd/yyyy hh:mi:ss am'),
TO_DATE('5/24/2018 11:35:00 PM','mm/dd/yyyy hh:mi:ss am')
- extract(TIMEZONE_HOUR from from_tz (TO_TIMESTAMP('5/24/2018 11:35:00 PM','mm/dd/yyyy hh:mi:ss am') ,'GMT' ) at time zone 'Europe/Moscow')/24 deptime_GRINICH
from dual
And the results are:
Date date converting to Greenwich time zone
5/20/2018 10:05:00 PM 5/20/2018 6:05:00 PM --> hour difference 4 hours
5/24/2018 7:35:00 PM 5/24/2018 3:35:00 PM --> hour difference 4 hours
but 2018 Time Zones - Sochi is UTC + 3h
In October 2014 Russia changed their rule for Daylight-Saving. Your Timezone file at Oracle Database could be an old one which does not cover the recent changes. Verify version with
SELECT * FROM V$TIMEZONE_FILE;
and consider an upgrade, see Upgrading the Time Zone File and Timestamp with Time Zone Data
However, it seems to be a bug in Oracle. Have a look at my query which is a bit more clear than your example:
SELECT *
FROM NLS_SESSION_PARAMETERS
WHERE parameter LIKE 'NLS_TIMESTAMP_TZ_FORMAT';
PARAMETER VALUE
-------------------------- --------------------------------------
NLS_TIMESTAMP_TZ_FORMAT YYYY-MM-DD HH24:MI:SSfmXFF3 fmTZH:TZM
1 row selected.
SELECT
EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow') AS TZ_HOUR,
TO_CHAR(TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM') AS ts2,
TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow' AS ts3
FROM dual;
TZ_HOUR TS2 TS3
---------- ------------------------------ ----------------------------------
4 2018-05-20 22:05:00 +04:00 20.05.2018 22:05:00.000000000 +03:00
1 row selected.
That's really strange, because TZH:TZM, resp. EXTRACT(TIMEZONE_HOUR FROM ...) returns different value than default NLS_TIMESTAMP_TZ_FORMAT = ... TZH:TZM. For other time zones (e.g. Europe/Zurich) I get always 02:00 - as expected.
You may open a ticket at Oracle support.
I have two databases, an old and a new one. The old one does not reflect recent changes in Russian Daylight-Saving times, the newer one does. However, the error as above appears on both:
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS TZH:TZM';
SELECT filename, VERSION,
TO_CHAR(TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow', 'TZH:TZM TZD') AS ts1,
TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow' AS ts2,
TO_CHAR(TIMESTAMP '2018-01-20 22:05:00 Europe/Moscow', 'TZH:TZM TZD') AS ts3,
TIMESTAMP '2018-01-20 22:05:00 Europe/Moscow' AS ts4
FROM V$TIMEZONE_FILE;
Old (Daylight-Saving times still existing due to old timezlrg_14.dat file):
FILENAME VERSION TS1 TS2 TS3 TS4
---------------- -------- ----------- ------------------------------------ ----------- -------------------------------------
timezlrg_14.dat 14 +04:00 MSD 20.05.2018 22:05:00.000000000 +03:00 +03:00 MSK 20.01.2018 22:05:00.000000000 +03:00
New (no Daylight-Saving time changes):
FILENAME VERSION TS1 TS2 TS3 TS4
---------------- -------- ----------- ------------------------------------ ----------- -------------------------------------
timezlrg_18.dat 18 +04:00 MSK 20.05.2018 22:05:00.000000000 +03:00 +04:00 MSK 20.01.2018 22:05:00.000000000 +03:00
Tonight there is going to be a leap second added to the clocks and there will be 61 seconds in the last minute of the last hour of the day.
2015-06-30 23:59:60
However, Oracle only supports up to 60 seconds in a minute:
TO_DATE( '2015-06-30 23:59:60', 'YYYY-MM-DD HH24:MI:SS' )
Errors with:
ORA-01852: seconds must be between 0 and 59
and
SELECT TO_DATE( '2015-06-30 23:59:59', 'YYYY-MM-DD HH24:MI:SS' ) + INTERVAL '1' SECOND AS Incr_Second_Before,
TO_DATE( '2015-07-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS' ) - INTERVAL '1' SECOND AS Decr_Second_After
FROM DUAL
Gives the output:
| INCR_SECOND_BEFORE | DECR_SECOND_AFTER |
|------------------------|------------------------|
| July, 01 2015 00:00:00 | June, 30 2015 23:59:59 |
Is there any way to handle a leap second in Oracle?
From MOS-
Insert leap seconds into a timestamp column fails with ORA-01852 (Doc
ID 1553906.1)
APPLIES TO:
Oracle Database - Enterprise Edition - Version 8.1.7.4 and later
Oracle Database - Standard Edition - Version 8.1.7.4 and later
Information in this document applies to any platform.
SYMPTOMS:
An attempt to insert leap seconds into a timestamp column, fails with:
ORA-01852: seconds must be between 0 and 59
CAUSE
It is not possible to store >59 sec value in a date or timestamp
datatype
SOLUTION
To workaround this issue, the leap second record can be stored in a
varchar2 datatype instead e.g.
SQL> create table test (val number, t varchar2(30));
Table created.
SQL> insert into test values(123, '2012-06-30T23:59:60.000000Z');
1 row created.
Not the best solution, but the only solution.
How are dates stored in Oracle? For example I know most systems use Epoch time to determine what time it is. By calculating how many seconds away from January 1st 1970. Does Oracle do this as well?
The reason I am asking this is I noticed if you take two dates in Oracle and subtract them you get a floating point of how many days are between.
Example
(Sysdate - dateColumn)
would return something like this (depending on the time)
3.32453703703703703703703703703703703704
Now is Oracle doing the conversion and spitting that format out, or does Oracle store dates with how many days it is away from a certain time frame? (Like Epoch time)
There are two types 12 and 13
http://oraclesniplets.tumblr.com/post/1179958393/my-oracle-support-oracle-database-69028-1
Type 13
select dump(sysdate) from dual;
Typ=13 Len=8: 220,7,11,26,16,41,9,0
The format of the date datatype is
Byte 1 - Base 256 year modifier : 220
2 - Base 256 year : 256 * 7 = 1792 + 220 = 2012
3 - Month : 11
4 - Day : 26
5 - Hours : 16
6 - Minutes : 41
7 - Seconds : 09
8 - Unused
2012-11-26 16:41:09
Type 12
select dump(begindate) from tab;
Typ=12 Len=7: 100,112,2,7,1,1,1
The format of the date datatype is
byte 1 - century (excess 100) 100 - 100 = 00
byte 2 - year (excess 100) 112 - 100 = 12
byte 3 - month = 2
byte 4 - day = 7
byte 5 - hour (excess 1) 1 - 1 = 0
byte 6 - minute (excess 1) 1 - 1 = 0
byte 7 - seconds (excess 1) 1 - 1 = 0
0012-02-07 00:00:00
From the manual at http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements001.htm#sthref151
For each DATE value, Oracle stores the following information: year, month, day, hour, minute, and second
So apparently it's not storing an epoch value which is also confirmed by this chapter of the manual:
The database stores dates internally as numbers. Dates are stored in fixed-length fields of 7 bytes each, corresponding to century, year, month, day, hour, minute, and second
How are dates stored in Oracle?
The two data types 12 and 13 are for two different purposes.
Type 12 - Dates stored in table
Type 13 - Date returned by internal date functions like SYSDATE/CURRENT_DATE, also when converting a string literal into date using TO_DATE or ANSI Date literal DATE 'YYYY-MM-DD'.
Test cases:
Basic table setup for type 12:
SQL> CREATE TABLE t(col DATE);
Table created.
SQL> INSERT INTO t SELECT SYSDATE FROM dual;
1 row created.
SQL> COMMIT;
Commit complete.
Check the different cases:
SQL> SELECT DUMP(col) FROM t;
DUMP(COL)
--------------------------------------------------------------------------------
Typ=12 Len=7: 120,116,3,17,18,6,55
SQL> SELECT DUMP(SYSDATE) FROM dual;
DUMP(SYSDATE)
--------------------------------------------------------------------------------
Typ=13 Len=8: 224,7,3,17,17,5,54,0
SQL> SELECT DUMP(CURRENT_DATE) FROM dual;
DUMP(CURRENT_DATE)
--------------------------------------------------------------------------------
Typ=13 Len=8: 224,7,3,17,17,14,20,0
SQL> SELECT DUMP(TO_DATE('17-DEC-1980 12:12:12','DD-MON-YYYY HH24:MI:SS')) FROM dual;
DUMP(TO_DATE('17-DEC-198012:12:12','
------------------------------------
Typ=13 Len=8: 188,7,12,17,12,12,12,0
Using ANSI Date literal, just like TO_DATE:
SQL> SELECT DUMP(DATE '2016-03-17') FROM dual;
DUMP(DATE'2016-03-17')
--------------------------------
Typ=13 Len=8: 224,7,3,17,0,0,0,0
SQL> INSERT INTO t SELECT to_date('17-DEC-1980 12:13:14','DD-MON-YYYY HH24:MI:SS') FROM dual;
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT DUMP(col) FROM t;
DUMP(COL)
--------------------------------------------------------------------------------
Typ=12 Len=7: 120,116,3,17,18,6,55
Typ=12 Len=7: 119,180,12,17,13,14,15
SQL>
As you can see, while storing a date in the table, it uses type 12. The second type 13 is used when converting a string literal into date using date functions or when date returned by internal date functions like SYSDATE/CURRENT_DATE.