How can I add months to a timestamp value in Oracle? In my query, it's getting converted to date value instead:
SELECT add_months(current_timestamp,2)
FROM dual;
The actual output is:
ADD_MONTH
11-MAR-13
The expected output is:
2013-01-01 00:00:00.000000000+00:00
This will give you the date and the time as a TIMESTAMP data type:
select TO_TIMESTAMP(TO_CHAR(ADD_MONTHS(SYSDATE, 2), 'YYYYMMDD HH24:MI'),
'YYYYMMDD HH24:MI') from dual;
If you need more or less precision (E.G. rounding) than what is above, adjust the date formats (both need to be the same format). For example, this will return 2 months down to the seconds level of precision:
select TO_TIMESTAMP(TO_CHAR(ADD_MONTHS(SYSTIMESTAMP, 2),
'YYYYMMDD HH24:MI:SS'), 'YYYYMMDD HH24:MI:SS') from dual;
This is the closest I can get (as a character) to the format you need:
select TO_CHAR(
TO_TIMESTAMP(TO_CHAR(ADD_MONTHS(SYSTIMESTAMP, 2),
'YYYYMMDD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS'),
'YYYY-MM-DD HH24:MI:SS.FF TZR') from dual;
I think this will about give you what you're looking for:
SELECT TO_CHAR(TO_TIMESTAMP(ADD_MONTHS(CURRENT_TIMESTAMP,2))
+ (CURRENT_TIMESTAMP - TRUNC(CURRENT_TIMESTAMP)),
'YYYY-MM-DD HH:MI:SSxFFTZR') FROM DUAL;
The problem with using the interval methods is that you can get an unexpected error depending on the date you run the query. E.g.
SELECT TO_TIMESTAMP('31-JAN-2012') + NUMTOYMINTERVAL(1,'MONTH') FROM DUAL;
That query returns:
ORA-01839: date not valid for month specified
This is because it attempts to return February 31, which is not a valid date.
ADD_MONTHS is a "safer" way to date math, in that where the interval query would throw an error, ADD_MONTHS will return the last date of the month (Feb 28 or 29 depending on the year) in the above example.
For Oracle:
SELECT
TIMESTAMP'2014-01-30 08:16:32', -- TS we want to increase by 1 month
--TIMESTAMP'2014-01-30 08:16:32' + NUMTOYMINTERVAL(1, 'MONTH'), -- raises ORA-01839: date not valid for month specified
--TIMESTAMP'2014-01-30 08:16:32' + INTERVAL '1' MONTH, -- raises ORA-01839: date not valid for month specified
ADD_MONTHS(TIMESTAMP'2014-01-30 08:16:32', 1), -- works but is a date :(
CAST(ADD_MONTHS(TIMESTAMP'2014-01-30 08:16:32', 1) AS TIMESTAMP) -- works
FROM DUAL
SELECT current_timestamp + INTERVAL '2' MONTH from dual;
To display this in your desired format, use TO_CHAR:
SELECT TO_CHAR(current_timestamp + INTERVAL '2' MONTH,
'YYYY-MM-DD HH24:MI:SS.FF9TZH:TZM') from dual;
2013-03-11 23:58:14.789501000+01:00
For Oracle:
select TO_TIMESTAMP(Sysdate,'DD-Mon-YYYY HH24-MI-SS') + 60
from dual;
select sysdate + interval '2' month from dual;
select TO_TIMESTAMP (Sysdate + interval '2' month, 'DD-Mon-YYYY HH24-MI-SS')
from dual
;
Result1:
| TO_TIMESTAMP(SYSDATE,'DD-MON-YYYYHH24-MI-SS')+60 |
----------------------------------------------------
| March, 12 0013 00:00:00+0000 |
Result2:
| SYSDATE+INTERVAL'2'MONTH |
--------------------------------
| March, 11 2013 21:41:10+0000 |
Result3:
| TO_TIMESTAMP(SYSDATE+INTERVAL'2'MONTH,'DD-MON-YYYYHH24-MI-SS') |
------------------------------------------------------------------
| March, 11 0013 00:00:00+0000 |
sqlffidle demo.
Related
convert into date format oracle there is an identical question in the continuation of this discussion.
I have a field c_day in the table my_table that accepts numeric values from 1 to 31. In this field. I need to add 210 days to today's date, and insert the value from c_day from the released date dd.mm.yyyy. For example, we take today's date 08.02.2023 and add 210 days to it, the date falls on September, and if c_day is 20, then the output should be 20.09.2023. But if c_dayis equal to 31, then of course the date should be set as 30.09.2023, because the last day of September is 30.
Now I settled on cases where September ends on the 30th, and the field takes values from 1 to 31. How can I write a condition in such cases so that it takes the last day of the month?
I tried this one, but it doesn't work:
SELECT
C_DAY,
LEAST(
TO_DATE(TO_CHAR(TRUNC(SYSDATE) + 210, 'YYYY-MM-') || C_DAY, 'YYYY-MM-DD'),
CASE
WHEN C_DAY < TO_CHAR(LAST_DAY(TRUNC(SYSDATE) + 210), 'DD') THEN
last_day(TO_DATE(TO_CHAR(TRUNC(SYSDATE) + 210, 'YYYY-MM-') || C_DAY, 'YYYY-MM-DD'))
END
) as result
FROM MY_TABLE
ORDER BY 1
You can can do it without any string manipulation using:
SELECT C_DAY,
LEAST(
TRUNC(TRUNC(SYSDATE) + 210, 'MM') + C_DAY - 1,
LAST_DAY(TRUNC(SYSDATE) + 210)
) AS result
FROM MY_TABLE
ORDER BY c_day
Which, for the sample data:
CREATE TABLE my_table ( c_day ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 31;
Outputs:
C_DAY
RESULT
1
2023-09-01 00:00:00
2
2023-09-02 00:00:00
3
2023-09-03 00:00:00
...
...
28
2023-09-28 00:00:00
29
2023-09-29 00:00:00
30
2023-09-30 00:00:00
31
2023-09-30 00:00:00
fiddle
I'm trying to extract Days, Hours, Minutes, Seconds from the output of subtracting two timestamps in an Oracle database. I then want to take the extracted values and put them into separate columns. I am able to accomplish this using substr but that doesn't seem efficient. Is there a more efficient and programmatic way of extracting the values? Below is an example query with the current and desired output.
Example:
SELECT
to_timestamp('2019-11-10 15:00:00', 'YYYY-MM-DD hh24:mi:ss') -
to_timestamp('2019-10-25 13:25:00', 'YYYY-MM-DD hh24:mi:ss')
as TIME_DIFF,
SUBSTR(to_timestamp('2019-11-10 15:00:00', 'YYYY-MM-DD hh24:mi:ss') -
to_timestamp('2019-10-25 13:25:00', 'YYYY-MM-DD hh24:mi:ss'), 9, 2)
as DAYS
from dual
Current Output:
TIME_DIFF | DAYS
------------------------------+-----
+000000016 01:35:00.000000000 | 16
Desired Output:
DAYS | HOUR | MIN | SS
-----+------+-----+---+
16 | 01 | 35 | 00
You can use extract() to pull out the required values from the interval:
with t as (
select to_timestamp('2019-11-10 15:00:00', 'YYYY-MM-DD hh24:mi:ss') -
to_timestamp('2019-10-25 13:25:00', 'YYYY-MM-DD hh24:mi:ss')
as TIME_DIFF
from dual
)
select
extract(day from time_diff) days,
extract(hour from time_diff) hours,
extract(minute from time_diff) minutes,
extract(second from time_diff) seconds
from t
Demo on DB Fiddle:
DAYS | HOURS | MINUTES | SECONDS
---: | ----: | ------: | ------:
16 | 1 | 35 | 0
You could take a look at extract() Checkout this answer: TIMESTAMPDIFF in Oracle 11g?
I'm looking to return data in Oracle for the last full week starting Sunday and finishing Saturday. This needs to be able to run any day of the week.
So I know that this is possible in SQL Server as I have reports that do the exact same thing:-
SET #startdate = DATEADD(wk, -1, DATEADD(wk, DATEDIFF(wk, 0,getdate()), -1))
SET #enddate = DATEADD(wk, DATEDIFF(wk, 0, getdate()), -1)
Today being Friday 17th March the above would return data between Sunday 5th March and Saturday 11th March.
I want to do the same thing in Oracle. Everywhere I've looked so far comes back with results like this:-
SELECT TRUNC (SYSDATE) - (SELECT TO_CHAR (SYSDATE, 'D') FROM DUAL),
TRUNC (SYSDATE) - (SELECT TO_CHAR (SYSDATE, 'D') + 1 FROM DUAL)
FROM DUAL
Or
SELECT SYSDATE AS TODAYS_DATE,
NEXT_DAY (SYSDATE - 7, 'SAT') AS PREV_SATURDAY,
NEXT_DAY (SYSDATE - 7, 'SUN') AS PREV_SUNDAY
FROM DUAL
I'm trying to avoid any 'sysdate-7' type code since that's pretty unwieldy in this situation - can anyone help at all?
Thanks
If, at any given point in time, "previous week" means the seven-day period that ENDS on the most recent midnight at the beginning of a Sunday, then something like this should work:
with inputs (dt) as (
select sysdate from dual union all
select sysdate + 1 from dual union all
select sysdate + 2 from dual union all
select sysdate + 3 from dual
)
-- end of test data; SQL solution begins below this line
select to_char(dt, 'Dy dd-Mon-yyyy hh:mi AM') as dt,
trunc(dt + 1, 'iw') - 8 as prev_wk_start,
trunc(dt + 1, 'iw') - 1 as prev_wk_end
from inputs;
DT PREV_WK_START PREV_WK_END
------------------------ ------------------- -------------------
Fri 17-Mar-2017 10:58 AM 03/05/2017 00:00:00 03/12/2017 00:00:00
Sat 18-Mar-2017 10:58 AM 03/05/2017 00:00:00 03/12/2017 00:00:00
Sun 19-Mar-2017 10:58 AM 03/12/2017 00:00:00 03/19/2017 00:00:00
Mon 20-Mar-2017 10:58 AM 03/12/2017 00:00:00 03/19/2017 00:00:00
Note: Whenever we work with time intervals, we must decide if the endpoints are included. In most cases, the best (and most used) convention is that the start date/time is included, while the end date/time is NOT included. The query above is consistent with this interpretation. If the query is run for an input like date '2017-03-19', which is midnight at the beginning of a Sunday, the query will return the week that ENDS exactly at that date and time. All of this "previous week" strictly precedes the input date/time, because the end point of the week is NOT included in the "one-week interval."
Use TRUNC( date_value, 'IW' ) to do it independent of the NLS_TERRITORY or NLS_DATE_LANGUAGE session parameters:
SELECT SYSDATE AS TODAYS_DATE,
TRUNC( SYSDATE, 'IW' ) AS MONDAY_OF_THIS_ISO_WEEK,
TRUNC( SYSDATE, 'IW' ) - INTERVAL '2' DAY AS PREV_SATURDAY,
TRUNC( SYSDATE, 'IW' ) - INTERVAL '8' DAY AS PREV_SUNDAY
FROM DUAL;
Output:
TODAYS_DATE MONDAY_OF_THIS_ISO_ PREV_SATURDAY PREV_SUNDAY
------------------- ------------------- ------------------- -------------------
2017-03-17 15:45:25 2017-03-13 00:00:00 2017-03-11 00:00:00 2017-03-05 00:00:00
Please does anyone have an idea on how to calculate current date (only dd/mm) by counting the number of days since the 1st January?
You can just add 157 to the first day of the year:
select trunc(sysdate, 'YYYY') + 157 from dual;
TRUNC(SYSDATE,'YYYY')+157
-------------------------
2016-06-06
The trunc(sysdate, 'YYYY') part gives you the first day of the year. You can also use a fixed date:
select date '2016-01-01' + 157 from dual;
DATE'2016-01-01'+157
--------------------
2016-06-06
And you can use an interval rather than a plain number of days if you prefer:
select date '2016-01-01' + numtodsinterval(157, 'DAY') from dual;
DATE'2016-01-01'+NUMTODSINTERVAL(157,'DAY')
-------------------------------------------
2016-06-06
Read more about datetime/interval arithmetic.
Either way of you only want DD/MM then use to_char():
select to_char(date '2016-01-01' + 157, 'DD/mM') from dual;
TO_CH
-----
06/06
What I am trying to do is to create two timestamps a StartDate timestamp which will be 09/08/2015 00:00:00 and an EndDate time stamp which should be 09/08/2015 23:59:59 as easy as it is to achieve in MS SQL, I have not been able to find a Make_Date function or Add_Days function to get either of the timestamps in Oracle PL SQL.
Can anyone help me out?
Rather than using fractional numbers 86399 / 86400 (which requires some working out when reviewing the code to see why you picked those magic numbers) to get the end date you can explicitly state the time periods using INTERVALS (which is easy to see at a glance what you are doing):
SQL Fiddle
Oracle 11g R2 Schema Setup:
Query 1:
SELECT TRUNC( CURRENT_DATE ) AS START_DATE,
TRUNC( CURRENT_DATE ) + INTERVAL '1' DAY - INTERVAL '1' SECOND AS END_DATE
FROM DUAL
Results:
| START_DATE | END_DATE |
|-----------------------------|-----------------------------|
| September, 08 2015 00:00:00 | September, 08 2015 23:59:59 |
Use TO_DATE to convert string into DATE.
SQL> alter session set nls_date_format='mm/dd/yyyy hh24:mi:ss';
Session altered.
SQL> SELECT to_date('09/08/2015 00:00:00' ,'mm/dd/yyyy hh24:mi:ss') start_date,
2 to_date('09/08/2015 23:59:59' ,'mm/dd/yyyy hh24:mi:ss') end_date
3 FROM dual;
START_DATE END_DATE
------------------- -------------------
09/08/2015 00:00:00 09/08/2015 23:59:59
SQL>
You could also use the ANSI TIMESTAMP Literal.
SQL> SELECT TIMESTAMP '2015-08-09 00:00:00' start_date,
2 TIMESTAMP '2015-08-09 23:59:59' end_date
3 FROM dual;
START_DATE END_DATE
---------------------------- -------------------------------
09-AUG-15 12.00.00.000000000 09-AUG-15 11.59.59.000000000 PM
SQL>
Update OP wants the date literal to be dynamic.
SQL> SELECT TRUNC(SYSDATE) start_date,
2 TRUNC(SYSDATE) + 86399 / 86400 end_date
3 FROM dual;
START_DATE END_DATE
------------------- -------------------
09/08/2015 00:00:00 09/08/2015 23:59:59
Update 2 OP wants to know why the time part is hidden in the date.
SQL> alter session set nls_date_format='mm/dd/yyyy';
Session altered.
SQL> SELECT sysdate FROM DUAL;
SYSDATE
----------
09/08/2015
SQL> alter session set nls_date_format='mm/dd/yyyy hh24:mi:ss';
Session altered.
SQL> SELECT sysdate FROM DUAL;
SYSDATE
-------------------
09/08/2015 15:46:14
So, what happened above? The same SYSDATE returns two different values. The reason is that the DATE has both datetime elements, what you see depends on the display properties driven by your locale-specific NLS settings.
Use TO_CHAR to convert the date into string to display it in your
desired format.
Using values from table:
SELECT
DATE_VALUE,
TRUNC(DATE_VALUE) START_DATE,
TRUNC(DATE_VALUE) + 86399 / 86400 END_DATE
FROM
(SELECT SYSDATE - LEVEL + 1 DATE_VALUE FROM DUAL CONNECT BY LEVEL <= 10)