0
votes

I have dates stored like String in database. The format is 'yyyy-ww' (example: '2015-43').

I need to get the first day of the week.

I tried to convert this string into date but there is no 'ww' option for the function "to_date".

Do you have an idea to perform this convertion?

EDIT

Test results based on the answers -

Thanks for your anwsers, but I have many problems to apply your solutions to my context:

select
TRUNC ( 2015 + ((43 - 1) * 7), 'IW' )
from dual

==> Error : ORA-01722: invalid number

select
TRUNC(to_date('2015','YYYY')+ to_number('01') *7, 'IW')
from dual

==> 2015-02-02 00:00:00 I waited for a date in january

select
trunc(to_date(regexp_substr('2015-01', '\d+',1,2), 'YYYY') + regexp_substr('2015-01', '\d+') * 7, 'IW') dt2
from dual

==> 0039-09-14 00:00:00

select
regexp_substr('2015-01', '\d+',1,2) as res1,
regexp_substr('2015-01', '\d+') * 7 as res2
from dual

==> res1 = 01 ==> res2 = 14105

6
add yours code please - starko
I realize you may no longer be able to change things, however, for future reference (ie other readers, or for yourself the next time you build a table), never never never store dates in a text. sore dates as dates. always. You'll never regret it if you do, you'll always regret it if you don't (just as you're finding with this question .. much more complex then it should if it was stored as date in the first place ;) ) - Ditto

6 Answers

0
votes

try to use by truncate

with t as (
 select '16-2010' dt from dual
)
--
--
  select dt, 
         trunc(to_date(regexp_substr(dt, '\d+',1,2), 'YYYY') + regexp_substr(dt, '\d+') * 7, 'IW') dt2
 from t
0
votes

I have dates stored like String in database.

You should never do that. It is a bad design. you should store date as DATE and not as a string. For all kinds of requirements for date manipulations Oracle provides the required DATE functions and format models. As and when needed, you could extract/display the way you want.

I need to get the first day of the week.

TRUNC (dt, 'IW') returns the Monday on or before the given date.

Anyway, in your case, you have the literal as YYYY-WW format. You could first extract the year and week number and combine them together to get the date using TRUNC.

TRUNC ( year + ((week_number - 1) * 7)
      , 'IW
      )

So, the above should give you the Monday of the week number passed for that year.

SQL> WITH DATA AS
  2    ( SELECT '2015-43' str FROM dual
  3    )
  4  SELECT TRUNC(to_date(SUBSTR(str, 1, 4),'YYYY')+ to_number(SUBSTR(str, instr(str, '-',1)+1))*7, 'IW')
  5  FROM DATA
  6  /

TRUNC(TO_
---------
23-NOV-15

SQL>
0
votes

Similar to Lalit's, however, I think I've corrected the math (his seemed to be off a bit when I tested .. )

  with w_data as (
     select sysdate + level +200  d  from dual connect by level <= 10
     ),
     w_weeks as (
        select d, to_char(d,'yyyy-iw') c
          from w_data
     )
  SELECT d, c, trunc(d,'iw'),
         TRUNC(
         to_date(SUBSTR(c, 1, 4)||'0101','yyyymmdd')-8+to_char(to_date(SUBSTR(c, 1, 4)||'0101','yyyymmdd'),'d')
         +to_number(SUBSTR(c, instr(c, '-',1)+1)-1)*7 ,'IW')
    FROM w_weeks;

The extra columns help show the dates before, and after.

0
votes

I would do the following:

WITH d1 AS (
    SELECT '2015-43' AS mydate FROM dual
)
SELECT TRUNC(TRUNC(TO_DATE(REGEXP_SUBSTR(mydate, '^\d{4}'), 'YYYY'), 'YEAR') + (COALESCE(TO_NUMBER(REGEXP_SUBSTR(mydate, '\d+$')), 0)-1) * 7, 'IW')
  FROM d1

The first thing the above query does is get the first four digits of the string 2015-43 and truncates that to the closest year (if you convert convert 2015 using TO_DATE() it returns a date within the current month; that is SELECT TO_DATE('2015', 'YYYY') FROM dual returns 01-FEB-2015; we need to truncate this value to the YEAR in order to get 01-JAN-2015). I then add the number of weeks minus one times seven and truncate the whole thing by IW. This returns a date of 01-OCT-2015 (see SQL Fiddle here).

0
votes

According ISO the 4th of January is always in week 1, so your query should look like

Select 
    TRUNC(TO_DATE(REGEXP_SUBSTR(your_column, '^\d{4}')||'-01-04', 'YYYY-MM-DD')
    + 7*(REGEXP_SUBSTR(your_column, '\d$')-1), 'IW') 
from your_table;

However, there is a problem. ISO year used for Week number can be different than actual year. For example, 1st Jan 2008 was in ISO week number 53 of 2007.

I think a proper working solution you get only when you generate ISO weeks from date value.

WITH w AS 
    (SELECT TO_CHAR(DATE '2010-01-04' + LEVEL * INTERVAL '7' DAY, 'IYYY-IW') AS week_number, 
        TRUNC(DATE '2010-01-04' + LEVEL * INTERVAL '7' DAY, 'IW') AS first_day
    FROM dual
    CONNECT BY DATE '2010-01-04' + LEVEL * INTERVAL '7' DAY < SYSDATE)
SELECT your_Column, first_day
FROM w your_table
    JOIN w ON week_number = your_Column;

Your date range must bigger than 2010-01-04 and not bigger than current day.

-1
votes

This is what I used:

select
to_date(substr('2017/01',1,4)||'/'||to_char(to_number(substr('2017/01',6,2)*7)-5),'yyyy/ddd') from dual;