ORA-01848: день года должен быть между 1 и 365 (366 для високосного года) ошибка

У меня есть существующие данные с днем ​​​​года (1-366), хранящиеся в формате DDD.

Теперь, когда я пытаюсь запросить данные и вывести отчет в формате ММ/ДД/ГГГГ, я получаю ORA-01848: day of year must be between 1 and 365 (366 for leap year) для следующего запроса.

select to_CHAR(TO_DATE(MyColumn, 'DDD'),'MM/DD/YYYY') from MyTable;

Как получить дату в формате ММ/ДД/ГГГГ, если год високосный?


person user2488578    schedule 17.01.2018    source источник
comment
А значений столбца › 365 нет?   -  person jarlh    schedule 17.01.2018
comment
Не зная года, который представляет значение DDD, вы не сможете надежно преобразовать его в полную дату. Есть еще столбец с годом?   -  person Alex Poole    schedule 17.01.2018
comment
@jarlh Есть много значений столбца › 365. Это строки, для которых я получаю сообщение об ошибке ORA-01848.   -  person user2488578    schedule 17.01.2018
comment
@AlexPoole Да, я заметил еще один столбец со значением года.   -  person user2488578    schedule 17.01.2018


Ответы (2)


Ваш текущий запрос:

select to_CHAR(TO_DATE(MyColumn, 'DDD'),'MM/DD/YYYY') from MyTable;

по умолчанию используется текущий год. Все преобразованные значения будут показывать 2018 год:

-- CTE for dummy values (<= 365)
with mytable(mycolumn) as (
  select 1 from dual
  union all select 60 from dual
  union all select 365 from dual
)
select to_CHAR(TO_DATE(MyColumn, 'DDD'),'MM/DD/YYYY') from MyTable;

TO_CHAR(TO
----------
01/01/2018
03/01/2018
12/31/2018

Поскольку 2018 год не високосный, 366-й день недействителен. Вы можете заставить его использовать произвольный жестко закодированный високосный год:

select to_CHAR(TO_DATE('2000' || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') from MyTable;

Демо:

-- CTE for dummy values
with mytable(mycolumn) as (
  select 1 from dual
  union all select 60 from dual
  union all select 365 from dual
  union all select 366 from dual
)
select to_CHAR(TO_DATE('2000' || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') from MyTable;

TO_CHAR(TO
----------
01/01/2000
02/29/2000
12/30/2000
12/31/2000

Но если исходная дата не была из високосного года, тогда значения будут отсутствовать на день, а также, конечно, отображаться против неправильного года.

Вы можете отфильтровать значения со значениями> 365 и придерживаться текущего года, но опять же вы, вероятно, получите нереалистичные/бесполезные преобразованные даты. Или вы можете использовать синтаксис 12c default ... on conversion error, чтобы получить, скажем, нулевой результат, когда он не будет преобразован, но опять же другие даты будут несовместимы.

Если вы не знаете год, который представляет каждое значение DDD, вы не сможете получить точную конверсию.

Если у вас есть другой столбец, содержащий год, соедините их вместе, например. если этот столбец года называется MyYear:

select to_CHAR(TO_DATE(MyYear || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') from MyTable;

Демонстрация, показывающая различные результаты:

-- CTE for dummy values
with mytable(mycolumn, myyear) as (
  select 1, 2018 from dual
  union all select 60, 2016 from dual
  union all select 60, 2017 from dual
  union all select 365, 2016 from dual
  union all select 366, 2016 from dual
  union all select 365, 2017 from dual
)
select MyColumn, MyYear,
  to_CHAR(TO_DATE(MyColumn default null on conversion error, 'DDD'),'MM/DD/YYYY') as Y2018,
  to_CHAR(TO_DATE('2000' || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') as Y2000,
  to_CHAR(TO_DATE(MyYear || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') as OK
from MyTable;

  MYCOLUMN     MYYEAR Y2018      Y2000      OK        
---------- ---------- ---------- ---------- ----------
         1       2018 01/01/2018 01/01/2000 01/01/2018
        60       2016 03/01/2018 02/29/2000 02/29/2016
        60       2017 03/01/2018 02/29/2000 03/01/2017
       365       2016 12/31/2018 12/30/2000 12/30/2016
       366       2016            12/31/2000 12/31/2016
       365       2017 12/31/2018 12/30/2000 12/31/2017
person Alex Poole    schedule 17.01.2018

Я создал логику, которая будет вычислять дату в соответствии с входным днем. Когда это не високосный год, он будет отображать дату. В случае високосного года будет отображаться последний день, если номер дня равен 366. Если это не високосный год, он будет отображать последний день года, если номер дня равен 366, поэтому это никогда не приведет к ошибке, с которой вы столкнулись.

with tb(col1) as (Select level 
                  from dual
                  connect by level < 367)
-- Actual Query ..Col1 is the day number being passed.                                                    
Select  Case when col1 < 366 then
        to_char(TO_DATE(col1, 'DDD'),'MM/DD/YYYY') 
        else
         to_char(LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE , 'Year'),11)),'MM/DD/YYYY')
        end col
from tb ;

ДЕМО

person XING    schedule 17.01.2018