Oracle - How to get date from YEAR, WEEKNUM and WEEKDAY? - oracle

I need to find the date of particular year, week, and weekday in oracle.
Is there a built-in function for this in oracle? or how can I achieve this?
Ex: If Year=2019, Week=22, Day=2(Tuesday), Then date should be '28-05-2019'.

Assuming "Week" and "Year" means week and year according to ISO-8601 you can use this function:
CREATE OR REPLACE FUNCTION ISOWeekDate(YEAR INTEGER, WEEK INTEGER, DAY INTEGER) RETURN DATE DETERMINISTIC IS
res DATE;
BEGIN
IF WEEK > 53 OR WEEK < 1 THEN
RAISE VALUE_ERROR;
END IF;
res := NEXT_DAY(TO_DATE( YEAR || '0104', 'YYYYMMDD' ) - INTERVAL '7' DAY, 'MONDAY') + ( WEEK - 1 ) * 7;
IF TO_CHAR(res, 'fmIYYY') = YEAR THEN
RETURN res + DAY - 1;
ELSE
RAISE VALUE_ERROR;
END IF;
END ISOWeekDate;

I think there is no built-in function for this, you will have to build one on your own.
below is one of the solutions you can make use of.
WITH FUNCTION getDate(p_year IN NUMBER, p_weeks IN NUMBER, p_day in NUMBER) RETURN DATE
IS
v_tmp date;
v_day number;
BEGIN
v_tmp := to_date('01/01/'||to_char(p_year),'dd/mm/yyyy');
v_day := to_char(v_tmp,'D');
RETURN v_tmp+(p_weeks-1)*7+p_day-(v_day)+1;
END;
SELECT getDate(2019,22,2)
FROM DUAL;
/
As per my NLS_TERRITORY settings, Oracle numbers the day of the week starting from Sunday, but from your example, you seem to be considering Monday as day 1 of the week. So, the same adjustment is made to the function to return the expected result.
Also, please note that with clause function is a new feature of Oracle 12c, if you happen to use an older version of Oracle, you will have to create the stored function before calling it.

Logically, the ISO week number is the occurrence of Thursdays in that (Gregorian) calendar year. The ISO year of an ISO week, is the Gregorian year in which the Thursday of that ISO week falls.
In the ISO schema, weeks always contain 7 days. Weeks start on a Monday, finish on a Sunday, and the days of that week are numbered 1-7.
This weekday numbering scheme often differs from that returned from built-in WEEKDAY functions and similar in different software packages (or requires particular database settings), which is something to be aware of. You will need available a function that returns the weekday of a particular date according to the ISO numbering scheme.
The first Thursday of the year can occur anywhere in the range 01-Jan to 07-Jan.
If the first Thursday falls on 01-Jan, then the first ISO week of that year will include days (29/31-Dec) from the previous Gregorian year.
If the first Thursday falls on 07-Jan, then the last ISO week of the previous Gregorian year will include days (01/03-Jan) from the current Gregorian year.
If the first Thursday falls on 04-Jan, then by implication the first ISO week of the year runs from Monday 01-Jan.
To devise an algorithm, the easiest approach is probably to establish on what weekday the 04-Jan falls in the given year, then apply the following calculation 4 - weekday. If the weekday of 04-Jan is found to be Thursday, the result will be 0. If it is Monday, the result will be 3 (4-1). If it is Sunday, the result will be -3 (4-7). We will store this offset for use after the next step.
We can derive a provisional day-of-year-offset from the ISO week and day numbers as follows: (((iso_week - 1) * 7) - (4 - iso_day)).
We then apply the offset derived in the first stage for the final day-of-year-offset. This is an offset from 04-Jan of the given Gregorian year. This offset may be a minus figure, if the relevant day falls prior to 04-Jan of the given year.
Given that day-of-year-offset, we can then use built-in functions to produce 04-Jan of the given year, and apply the day-of-year-offset to produce the final Gregorian date.
For example, given 2019-W01-1.
2019-01-04 is a Friday, so it's weekday is 5. The result of the first step is -1 (4-5). The result of the second step is -3 (((1-1)*7) - (4-1)). Added together they produce a day-of-year-offset of -4. 04-Jan-2019 offset by -4 results in 31-Dec-2018.
And that is our result: 2019-W01-1 = 2018-12-31.

Related

How to handle weekday calculation with date operation in Oracle

I need to handle specific scenario in StorProc where I need to do date calculation excluding Sat & Sun. Weekends are holiday I need to handle the data within working days.
I have implemented below code
if (purchase_date = (trunc(sysdate)-2) or purchase_date = (trunc(sysdate)-1)) Then
specific operation
As I have to exclude Sat & Sun by above implementation is giving wrong results obliviously . For example if today is Monday it has to give me back the date of Friday, my implementation is giving me Saturday or Sunday. I need to calculation with dates for weekdays only. Any help would be appreciated.
Thanks
To compare it to the previous week day, you can use:
IF purchase_date = TRUNC(SYSDATE)
- CASE TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')
WHEN 0 THEN 3
WHEN 6 THEN 2
ELSE 1
END
THEN
-- Do something
NULL;
END IF;
TRUNC(date_value) - TRUNC(date_value, 'IW') will count the number of days since the start of the ISO week (which is always midnight on Monday).
Note: Do not use TO_CHAR(date_value, 'D') in an international setting as it will give a different result depending on which country you run it in (the week starts on Friday in Bangladesh, Saturday in some Middle-Eastern countries, Sunday in America and Monday in most of Europe).

Oracle sql how to get the date of a week

I have the following query that gets the week of a date:
SELECT pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww') semana,
SUM (rta.kms_acumulados) kms,
COUNT
(DISTINCT (CASE
WHEN v.secuencia BETWEEN rta.sec_origen AND rta.sec_destino
THEN v.cod_inc
ELSE '0'
END
)
)
- 1 numincidencias
FROM (SELECT ms.tren, ms.fecha_origen_tren, ms.secuencia, ri.cod_inc
FROM r_incidencias ri, mer_sitra ms
WHERE ri.cod_serv = ms.tren
AND ri.fecha_origen_tren = ms.fecha_origen_tren
AND ri.cod_tipoin IN (SELECT cod_tipo_iincidencia
FROM v_tipos_incidencias
WHERE grupo = '45')
AND ri.punto_desde = ms.cod_estacion) v,
r_trenes_asignar rta,
r_maquinas rm,
planificador.pl_dh_material pdm
WHERE rta.fecha BETWEEN TO_DATE ('21/09/2018', 'dd/mm/yyyy') AND TO_DATE ('21/09/2018',
'dd/mm/yyyy'
)
AND rta.serie >= 4000
AND rta.matricula_ant IS NOT NULL
AND rm.matricula_maq = rta.matricula_ant
AND rm.cod_serie = pdm.id_material
AND rta.grafico BETWEEN pdm.desde AND pdm.hasta
AND v.tren(+) = rta.tren
AND v.fecha_origen_tren(+) = rta.fecha
GROUP BY pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww')
ORDER BY pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww')
For example week 1
I want to display
week 1 : 1 january - 7 january
How can I get this?
Oracle offers the TRUNC(datestamp, format) function to manipulate dates this way. You may use a variety of format strings to get the first day of a quarter, year, or even the top of the hour.
Given a particular datestamp value, Oracle returns midnight on the first day of the present week with this expression:
TRUNC(datestamp,'DY')
You can add days to a datestamp. Therefore this expression gives you midnight on the last day of the week
TRUNC(datestamp,'DY') + 6
A WHERE-clause selector for all rows in the present week might be this.
WHERE datestamp >= TRUNC(SYSDATE,'DY')
AND datestamp < TRUNC(SYSDATE,'DY') + 7
Notice that the end of the range is just before (<) midnight on the first day of the next week. You need that because you may have datestamps after midnight on the last day of the week. (Beware using BETWEEN for datestamp ranges.)
And,
SELECT TO_CHAR(TRUNC(SYSDATE,'DY'),'YYYY-MM-DD'),
TO_CHAR(TRUNC(SYSDATE,'DY')+6,'YYYY-MM-DD')
FROM DUAL;
displays the first and last dates of the present week in ISO-like format.
Date arithmetic is cool. It's worth your trouble to study the date-arithmetic functions in your DBMS at least once a year.

plsql - oracle add_months adding additional day

Oracle: 12c
Description: Trying to add 99 years to a date using add_months, which works however it adds an additional day to the return date value. How should i properly add years to a date?
declare
toRetDate DATE;
inputDate DATE;
numYears number;
begin
numYears := 99;
inputDate := TO_DATE('28-FEB-85', 'DD-Mon-YY' );
toRetDate := add_months(inputDate, numYears*12);
DBMS_OUTPUT.put_line(toRetDate);
end;
Output:29-FEB-84
ADD_MONTHS does not add a day to the result. Rather, it has this very general feature: if you ADD MONTHS to a date, and that date is the end of the month in that particular month/year, then the result of ADD_MONTHS is the last day of the resulting month.
So, for example, if you add one month to January 31, you always get February 28 (or Feb 29 in a leap year). If you add one month to June 30, you get July 31.
If you add a month to February 28, you get March 31 UNLESS it's in a leap year; in a leap year Feb 28 is not the last day of the month, so if you add a month to it you get March 28.
In all cases, if the result is a day number greater than the number of days in the resulting month, the result is simply the last day of that month.
EDIT: In a comment below this Answer, the OP asks if there is a way to keep the exact same day of the month, with the exception of Feb. 29 (which should become Feb. 28 in a non-leap year).
The answer is YES. As #Xing shows in the other answer, adding interval '99' year will preserve the date, but it may cause trouble when the input date is Feb. 29 and the resulting year is not a leap year. That can be fixed with a preliminary check. It can be done with an IF statement or with a CASE expression. I prefer the latter because it works the same in PL/SQL and in SQL; there is no IF in plain SQL.
toretdate := case when to_char(inputdate, 'mm-dd') = '02-29'
then (inputdate + 1) + interval '99' year - 1
else inputdate + interval '99' year end
The trick is, when the inputdate is 29 February, push it forward one day to make it March 1, then add 99 years (still March 1), and then subtract one day to make it either February 29 (if it's a leap year) or February 28 (otherwise).
You can use this way:
declare
toRetDate DATE;
inputDate DATE;
numYears number;
begin
numYears := 99;
inputDate := TO_DATE('28-FEB-1985', 'DD-Mon-YYYY' );
toRetDate := inputDate + INTERVAL '99' YEAR; --add_months(inputDate, numYears*12);
DBMS_OUTPUT.put_line(to_date(toRetDate,'dd-mon-yyyy'));
end;
Although it's worth noting that the arithmetics with intervals is limited (if not broken) because it simply "increments" the month/year value of the date value. That can lead to invalid dates (e.g. from January to February).
EDIT:
In General Add_months generally adds 30/31(depending on months) to the date on which its applied. The case is slight different for monthends. If add_months is done for month of FEBRUARY, it will add (28/29) days depending its a leap year or not.
If your requirement is to simple add 99 years without checking if the resultant date is a valiad or not, then you can use INTERVAL, but if you really wanted to check if the resultant date should be very well evaluted and correct date then you must your ADD_MONTHS. It does not make any sense to first use INTERVAL then check if the resultant date(using any logic) is a valid date or not. Oracle has already provided solution for such scenarios.

To count the number of weeks between two dates in oracle

I have used the below query to find the number of weeks between two dates:
select count(week_id)
from fw
where week_begin_date >= '2015-01-01'
and week_end_date <= '2015-12-31';
Expected result should be 53 but the actual result is 51.
Kindly help on this.
Can't you just use the week of year function? subtract if needed...
select to_char(to_date('12/31/2015','MM/DD/YYYY'),'WW') from dual;
select To_Number(to_char(to_date('12/31/2015','MM/DD/YYYY'),'WW')) -
To_number(to_char(to_date('01/01/2015','MM/DD/YYYY'),'WW')) +1
from dual;
We have to add +1 because weeks start at 1 not 0.
Now maybe you're after the ISO week format which would be IW instead of WW.
WW: Week of year (1-53) where week 1 starts on the first day of the year and continues to the seventh day of the year.
IW: Week of year (1-52 or 1-53) based on the ISO standard.
I know this is a very old thread, but I used a modified version of this code today that I thought might be beneficial for someone else. My modification solves for the fractional week issue and the removal of the minus sign:
SELECT
CEIL(
ABS(
(
TO_DATE('20160101','YYYYMMDD')
- TO_DATE('20161231','YYYYMMDD')
) / 7
)
) AS DT
FROM DUAL
The ABS function takes the absolute value of the result of subtracting the two dates, thereby eliminating the minus sign if it exists (I switched the order of the dates to demonstrate this). The CEIL function rounds up any fractional week to the next whole week (I changed the year to 2016 to demonstrate this - CEIL is logically equivalent to the ROUNDUP function in Excel). NOTE: We have to apply the ABS function first (inner parenthesis) because CEIL will also round up negative numbers, which would in effect round the weeks down if ABS were applied after CEIL. The result of this calculation is 53 (subtraction of the dates returns about -52.142857, ABS removes the minus sign, CEIL rounds up to 53).
I hope this ends up being useful to someone. Thanks.
Did you try this:
SELECT
REPLACE((
to_date('20151231','yyyymmdd') - to_date('20150101','yyyymmdd')
)/7, '-', '')
FROM
DUAL

Calculate the week ending date in oracle using Saturday as the week end date

Given a field in Oracle that contains dates, how would you calculate what the week ending date is using Sun thru Sat as your week. For example, if the date is 1/26/2015 (which is a Monday), the query should return 1/31/2015 (which is a Saturday. If the date is 1/31/2015, then the query should return 1/31/2015.
Given any particular date / time value, this expression will return midnight of the preceding Sunday.
TRUNC(whatever_time,'DAY')
So, you can do stuff like this:
SELECT TRUNC(whatever_time,'DAY') week_starting,
TRUNC(whatever_time,'DAY') + 6 week_ending,
SUM(sales)
FROM table
GROUP BY TRUNC(whatever_time,'DAY')
and you'll get what you need.
Notice that TRUNC(whatever_time,'DAY') honors the Oracle session initialization parameter called “NLS_TERRITORY”. For example, in Europe Monday is considered the first business day of the week. Try this.
ALTER SESSION SET NLS_TERRITORY=GERMANY;
SELECT TRUNC( DATE '2013-12-31', 'DAY'),
TRUNC( DATE '2014-01-03', 'DAY')
FROM DUAL;
A complete writeup of this is here: http://www.plumislandmedia.net/sql-time-processing/using-sql-report-time-intervals-oracle/

Resources