Преобразование даты и времени в PL/SQL

Как преобразовать дату и время в PL/SQLТеперь, когда вы имеете представление о типах данных Oracle, предназначенных для работы с датой и временем, давайте посмотрим, как присваивать даты значениям переменных этих типов и как использовать такие значения в дальнейшем. Дата и время в понятной для человека форме представляются в виде символьных строк наподобие «5 марта 2009 года» и «10:30», так что речь пойдет о преобразовании значений даты/ времени в PL/SQL — приведении символьных строк в соответствие с внутренним представлением Oracle, и наоборот.



PL/SQL проверяет и хранит даты от 1 января 4712 года до н. э. до 31 декабря 9999 года н. э. (В документации Oracle указана максимальная дата 31 декабря 4712 года). Если ввести дату без времени (во многих приложениях не требуется отслеживать время, поэтому PL/SQL позволяет его не задавать), то время в таком значении по умолчанию будет равно полуночи (00:00:00).

База данных Oracle способна интерпретировать практически все известные форматы даты и времени. Эта возможность основана на использовании маски форматирования даты, которая представляет собой строку специальных символов, определяющих формат даты для Oracle.

В следующем разделе мы рассмотрим несколько разных масок форматирования даты. Полный список всех элементов форматирования масок приводится в приложении В.

 

Преобразование строк в даты

Первой практической задачей, с которой вы столкнетесь при работе с датами, будет присваивание даты (или времени) переменной PL/SQL. Операция выполняется путем преобразования даты/времени во внутренний формат базы данных. Такие преобразования выполняются либо неявно, присваиванием символьной строки переменной типа даты/времени, либо явно, с помощью встроенных функций Oracle.

Неявное преобразование — вещь рискованная, и мы не рекомендуем на него полагаться. Пример неявного преобразования символьной строки при ее присваивании переменной типа DATE:

DECLARE
birthdate DATE;
BEGIN
birthdate := '15-Nov-1961';
END;

Такое преобразование зависит от установленного значения NLS_DATE_FORMAT. Оно будет успешно работать до тех пор, пока администратор базы данных не решит изменить это значение. Как только он это сделает, код перестанет функционировать. Нарушит работу кода и изменение значения параметра NLS_DATE_FORMAT на уровне сеанса.

Вместо того чтобы полагаться на неявные преобразования и установленное значение параметра NLS_DATE_FORMAT, лучше преобразовывать даты явно, с помощью встроенных функций Oracle — таких, как TO_DATE

DECLARE
birthdate DATE;
BEGIN
birthdate := TO_DATE('15-Nov-1961','dd-mon-yyyy');
END;

Обратите внимание на передачу форматной строки 'dd-mon-yyyy' во втором параметре вызова TO_DATE. Форматная строка управляет интерпретацией символов первого параметра функцией TO_DATE.

PL/SQL поддерживает следующие функции преобразования строк в дату и время:

  • TO_DATE(строка[, маска[, язык_nls]]) — преобразует символьную строку в значение типа DATE.
  • TO_DATE(число[, маска[, язык_nls]]) — преобразует строку, содержащую Юлианскую дату, в значение типа DATE.
  • TO_TIMESTAMP(строка[, маска[, язык_nls]]) — преобразует символьную строку в значение типа TIMESTAMP.
  • TO_TIMESTAMP_TZ(строка[, маска[, язык_nls]]) — преобразует символьную строку в значение типа TIMESTAMP WITH TIME ZONE. Функция также используется для преобразования к типу TIMESTAMP WITH LOCAL TIME ZONE.

Эти функции не только ясно показывают, что в коде выполняется преобразование, но и позволяют точно задать используемый формат даты/времени.

Вторая версия TO_DATE может использоваться только с маской форматирования J (Юлианская дата, то есть количество дней, прошедших с 1 января 4712 года до н. э.). Только в этом варианте использования TO_DATE в первом параметре может передаваться число.

В остальных случаях параметры имеют следующий смысл:

  • строка — преобразуемая строковая переменная, литерал, именованная константа или выражение;
  • маска — маска форматирования, используемая для преобразования строки. По умолчанию в качестве маски используется значение параметра NLS_DATE_FORMAT.
  • язык_nls — необязательное обозначение языка, который должен использоваться для интерпретации имен и сокращений месяцев и дней в строке, в формате 'NLS_DATE_LANGUAGE=язык'.

Здесь язык — один из языков, распознаваемых вашим экземпляром базы данных. За информацией о допустимых языках обращайтесь к руководству Oracle Globalization Support Guide.

Форматные элементы (см. приложение В) применяются при использовании функций семейства TO_. Например, следующие вызовы TO_DATE и TO_TIMESTAMP преобразуют символьные строки разных форматов в значения DATE и TIMESTAMP

DECLARE
dt DATE;
ts TIMESTAMP;
tstz TIMESTAMP WITH TIME ZONE;
tsltz TIMESTAMP WITH LOCAL TIME ZONE;
BEGIN
dt := TO_DATE('12/26/2005','mm/dd/yyyy');
ts := TO_TIMESTAMP('24-Feb-2002 09.00.00.50 PM');
tstz := TO_TIMESTAMP_TZ('06/2/2002 09:00:00.50 PM EST',
'mm/dd/yyyy hh:mi:ssxff AM TZD');
tsltz := TO_TIMESTAMP_TZ('06/2/2002 09:00:00.50 PM EST',
'mm/dd/yyyy hh:mi:ssxff AM TZD');
DBMS_OUTPUT.PUT_LINE(dt);
DBMS_OUTPUT.PUT_LINE(ts);
DBMS_OUTPUT.PUT_LINE(tstz);
DBMS_OUTPUT.PUT_LINE(tsltz);
END;

Результат:

26-DEC-05
24-FEB-02 09.00.00.500000 PM
02-JUN-02 09.00.00.500000 PM -05:00
02-JUN-02 09.00.00.500000 PM 

Обратите внимание на представление долей секунд (.50) и на использование маски XFF. Элемент форматирования X определяет местоположение десятичного разделителя (в данном случае точки), отделяющего количество целых секунд от дробной части. Можно было бы просто поставить в этом месте точку (.FF), но мы предпочли воспользоваться символом X, поскольку он указывает, что на этом месте должен находиться разделитель, соответствующей текущему значению параметра NLS_TERRITORY.

Ошибки Oracle из диапазона от ORA-01800 до ORA-01899 связаны с преобразованиями дат. Чтобы узнать о некоторых тонкостях обработки дат, просмотрите документацию по этим ошибкам и ознакомьтесь с описаниями их причин. Вот лишь некоторые из них:

  • Литерал даты, переданный TO_CHAR для преобразования в дату, не может быть длиннее 220 символов.
  • Одна маска форматирования не может одновременно содержать элемент даты юлианского календаря (J) и элемент дня года (DDD).
  • Маска не может содержать несколько элементов для одного компонента даты/времени. Например, маска YYYY-YYY-DD-MM недопустима, потому что она включает два элемента года, YYYY и YYY.
  • 24-часовой формат времени (HH24) не может использоваться в маске одновременно с 12-часовым.

Как показывают предыдущие примеры, функция TO_TIMESTAMP_TZ может преобразовывать символьные строки с информацией часового пояса. И хотя часовые пояса на первый взгляд выглядят просто, в действительности все намного сложнее — вы убедитесь в этом в разделе «Часовые пояса».

 

Преобразование даты в строку

Присвоить переменной значение типа даты/времени — только полдела. Еще нужно прочитать его и представить в понятной для человека форме. Для этого в Oracle существует функция TO_CHAR.

Функция TO_CHAR предназначена для преобразования значения даты/времени в строку переменной длины. Она применяется и для типа DATE, и для всех типов семейства TIMESTAMP. Кроме того, она выполняет преобразование чисел в символьные строки. Синтаксис функции TO_CHAR:

FUNCTION TO_CHAR
(входная_дата IN DATE
[, маска IN VARCHAR2
[, язык_nls IN VARCHAR2]])
RETURN VARCHAR2

Здесь входная_дата — символьная строка, представляющая значение даты/времени; маска — строка из одного или нескольких элементов формата; язык_nls — строка, задающая язык отображения даты. Параметры маска и язык_nls не являются обязательными.

Чтобы результирующая строка была представлена символами национального набора, вместо функции TO_CHAR следует использовать функцию TO_NCHAR. Но имейте в виду, что в этом случае маска форматирования тоже должна быть задана в символах национального набора. В противном случае произойдет ошибка.

Если маска не задана, по умолчанию используется формат даты, установленный для базы данных. Это формат 'DD-MON-RR', если только администратор не задал другой формат, изменив значение параметра NLS_DATE_FORMAT. Как упоминалось ранее, лучше не зависеть от неявных преобразований даты. Изменения серверных настроек NLS (а в клиентском коде — изменения клиентских настроек NLS) приводят к появлению логических ошибок, если ваш код зависит от неявных преобразований. Если приложение всегда выполняет преобразования явно, такие ошибки исключены.

Несколько примеров использования функции TO_CHAR для преобразования даты:

  • Обратите внимание на два пробела между месяцем и днем, а также на начальный нуль перед номером месяца:
       TO_CHAR (SYSDATE, 'Month DD, YYYY') --> 'February 05, 1994' 
  • Для подавления пробелов и нулей используется элемент FM:
       TO_CHAR (SYSDATE, 'FMMonth DD, YYYY') --> 'February 5, 1994' 
  • Обратите внимание на различие регистра в обозначении месяца в следующих двух примерах (при работе с форматами данных Oracle вы получаете в точности то, что запрашиваете):
       TO_CHAR (SYSDATE, 'MON DDth, YYYY') --> 'FEB 05TH, 1994'
       TO_CHAR (SYSDATE, 'fmMon DDth, YYYY') --> 'Feb 5TH, 1994' 
  • На элемент форматирования TH не распространяются правила задания регистра. Даже если ввести его в нижнем регистре (th), то в выходной строке база данных будет использовать TH.
  • Вывод дня года, дня месяца и дня недели для заданной даты:
       TO_CHAR (SYSDATE, 'DDD DD D ') --> '036 05 7'
       TO_CHAR (SYSDATE, 'fmDDD fmDD D ') --> '36 05 7' 
  • Нетривиальное форматирование для построения отчета:
       TO_CHAR (SYSDATE, '"In month "RM" of year "YEAR')
       --> 'In month II of year NINETEEN NINETY FOUR' 
  • Для переменных типа TIMESTAMP время можно задавать с точностью до миллисекунд:
       TO_CHAR (A_TIMESTAMP, 'YYYY-MM-DD HH:MI:SS.FF AM TZH:TZM')
       --> значение вида 2002-02-19 01:52:00.123457000 PM -05:00 

Будьте внимательны при работе со значениями, включающими доли секунд. В маске форматирования доли секунды представляются элементом FF, и кто-то решит, что количество букв F должно соответствовать количеству десятичных цифр в выходной строке. Но это не так! Для обозначения от 1 до 9 десятичных цифр следует использовать конструкции FF1–FF9. Например, в следующем блоке конструкция FF6 запрашивает вывод с точностью до шести десятичных цифр: 

DECLARE
ts TIMESTAMP WITH TIME ZONE;
BEGIN
ts := TIMESTAMP '2002-02-19 13:52:00.123456789 -5:00';
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ts,'YYYY-MM-DD HH:MI:SS.FF6 AM TZH:TZM'));
END;

Результат:

2002-02-19 01:52:00.123457 PM -05:00 

Обратите внимание на выполненное округление. Во входных данных было указано количество секунд 00.123456789. Значение было округлено (не усечено, а именно округлено) до шести цифр: 00,123457.

Ничего не стоит по ошибке задать неверный формат даты, и с появлением типа данных TIMESTAMP вероятность этого даже увеличилась. Некоторые элементы форматирования, использующиеся со значениями типа TIMESTAMP, не могут применяться со значениями типа DATE. Например, если попытаться преобразовать значения типа DATE в символьную строку с помощью элементов FF, TZH и TZM, получится следующее:

DECLARE
dt DATE;
BEGIN
dt := SYSDATE;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(dt,'YYYY-MM-DD HH:MI:SS.FF AM TZH:TZM'));
END;
Результат будет таким:
dt := SYSDATE;
*
ORA-01821: date format not recognized
ORA-06512: at line 5

Сообщение об ошибке (формат даты не распознается) только сбивает с толку. Формат даты абсолютно нормален — проблема в том, что он применяется не к тому типу данных. Поэтому при получении такого сообщения следует проверить не только формат даты, но и тип данных, который вы собираетесь преобразовывать.

 

Часовые пояса

Возможное присутствие информации о часовом поясе несколько усложняет использование функции TO_TIMESTAMP_TZ по сравнению с функциями TO_DATE и TO_TIMESTAMP. Информация о часовом поясе задается одним из следующих способов:

  • Как положительное или отрицательное смещение в часах и минутах относительно времени UTC; например, значение –5:00 эквивалентно Восточному стандартному времени США. Значения смещения должны находиться в диапазоне от ?12:59 до +13:59.
  • В виде названия часового пояса, например: US/Eastern, US/Pacific и т. д.
  • В виде сочетания названия и сокращения часового пояса, например: US/Eastern EDT.

Рассмотрим несколько примеров. Начнем с простейшего случая, в котором часовой пояс вообще не указан: 

TO_TIMESTAMP_TZ ('12312005 083015.50', 'MMDDYYYY HHMISS.FF')

Дата и время здесь определены как 31 декабря 1998 года, 8 часов 30 минут 15 с половиной секунд до полудня. Поскольку часовой пояс не указан, Oracle считает, что время относится к текущему часовому поясу. Если часовой пояс намеренно не указан, то программа получается менее выразительной, чем следует. Если приложение должно использовать часовой пояс сеанса (вместо явно заданного часового пояса), лучше сначала определить часовой пояс сеанса при помощи функции SESSIONTIMEZONE, а затем явно использовать его при вызове функции TO_TIMESTAMP_TZ. Явное выражение ваших намерений поможет разработчику (которым можете быть вы сами) разобраться в вашем коде два года спустя, при добавлении новых функций или исправлении ошибок.

 

Дата или время?

Учтите, что каждое значение даты/времени содержит как дату, так и время. Стоит вам забыть об этом, и в вашем коде появятся ошибки. Рассмотрим пример: допустим, я написал код PL/SQL, который должен быть выполнен 1 января 2015 года: 

IF SYSDATE = TO_DATE('1-Jan-2015','dd-Mon-yyyy')
THEN
Apply2015PriceChange;
END IF;

Запускаемая процедура должна изменить цены на предстоящий год, но вероятность добиться желаемого результата минимальна. Блок кода должен быть выполнен ровно в полночь, с точностью до секунды; дело в том, что SYSDATE вместе с датой возвращает время. Чтобы блок работал так, как задумано, следует усечь значение, возвращаемое функцией SYSDATE, до полуночи соответствующего дня: 

IF TRUNC(SYSDATE) = TO_DATE('1-Jan-2015','dd-Mon-yyyy');

Теперь в обеих сторонах сравнения указывается время суток, но этим временем является полночь. Функция TO_DATE также возвращает время суток, которое по умолчанию соответствует полуночи (то есть 00:00:00). Итак, в какое бы время 1 января 2015 года ни был выполнен этот блок, сравнение будет выполнено успешно, и процедура Apply2009PriceChange будет выполнена.

Функцию TRUNCATE также удобно использовать для удаления времени из временных штампов.

В следующем примере часовой пояс задается смещением в часах и минутах относительно UTC. Обратите внимание на элементы TZH и TZM, отмечающие, где во входной строке указывается смещение времени в часах и минутах: 

TO_TIMESTAMP_TZ ('1231200 083015.50 -5:00', 'MMDDYY HHMISS.FF TZH:TZM')

В этом примере значение даты/времени интерпретируется как Восточное стандартное время США (независимо от часового пояса сеанса).

Следующий пример показывает, как задать часовой пояс по имени региона. В нем задается регион EST, что соответствует Восточному времени в Соединенных Штатах. Обозначение TZR в маске форматирования указывает местоположение имени региона во входной строке:

TO_TIMESTAMP_TZ ('02-Nov-2014 01:30:00 EST',
'dd-Mon-yyyy hh:mi:ss TZR')

Этот пример интересен тем, что он представляет не Восточное стандартное время, а просто Восточное время. Разница между ними заключается в том, что термин Восточное время может обозначать как Восточное стандартное время, так и Восточное летнее время — в зависимости от того, действует ли переход на летнее время. Я специально сформулировал этот пример так, чтобы он был неоднозначным. 2 ноября 2014 года — это дата завершения действия Восточного летнего времени, когда время 2:00 превращается в 1:00. Поэтому в указанный день 1:30 наступает дважды: по Восточному летнему и по Восточному стандартному времени. Так какое же время имеется в виду при определении 1:30 2 ноября 2014 года?

Названия региона недостаточно для различения стандартного и летнего времени. Для устранения этой неоднозначности необходимо задать сокращение часового пояса, как это сделано в двух следующих примерах. Восточное летнее время обозначается сокращением EDT (Eastern Daylight Time): 

TO_TIMESTAMP_TZ ('02-Nov-2014 01:30:00.00 US/Eastern EDT',
'dd-Mon-yyyy hh:mi:ssxff TZR TZD')

Если параметр сеанса ERROR_ON_OVERLAP_TIME равен TRUE (по умолчанию используется значение FALSE), база данных будет выдавать ошибку при задании неоднозначного времени.

Восточное стандартное время обозначается сокращением EST (Eastern Standard Time): 

TO_TIMESTAMP_TZ ('02-Nov-2014 01:30:00.00 US/Eastern EST',
'dd-Mon-yyyy hh:mi:ssxff TZR TZD')

Для предотвращения неоднозначности рекомендуется либо задавать смещение часового пояса в часах и минутах (например, –5:00), либо использовать сочетание названия и сокращения часового пояса. Если указано только имя региона, то при наличии неоднозначности в определении времени (летнее или стандартное) Oracle будет считать, что задано стандартное время.

Полный список поддерживаемых Oracle названий регионов и часовых поясов содержит представление V$TIMEZONE_NAMES, доступное для всех пользователей базы данных. Анализируя информацию этого представления, обратите внимание на то, что сокращения часовых поясов не уникальны (см. следующую врезку).

 

О СТАНДАРТЕ ЧАСОВЫХ ПОЯСОВ

Часовые пояса настолько важны, что, казалось бы, должен существовать некий международный стандарт, определяющий их названия и сокращения. Тем не менее такого стандарта нет. Названия часовых поясов и сокращения не только не стандартизированы, но среди них даже встречаются повторения. Например, сокращение EST обозначает Восточное стандартное время и в США, и в Австралии, то есть соответствует двум часовым поясам с разным смещением времени. Сокращение BST применяется для нескольких часовых поясов, включая Тихоокеанский/Мидуэй и Лондонский, отличающиеся на 12 часов в режиме летнего времени или на 11 часов в остальное время года. Вот почему функция TO_TIMESTAMP не позволяет задавать часовой пояс лишь по сокращению названия.

Поскольку единого стандарта часовых поясов не существует, резонно спросить — откуда взяты имена регионов в представлении V$TIMEZONE_NAMES? Источником информации Oracle являются документы из каталога ftp://elsie.nci.nih.gov/pub/. Ищите файлы с именами вида tzdataxxx.tar.gz, где XXX — версия данных. Обычно в архиве хранится страница с именем tz-link.htm, содержащая дополнительную информацию и ссылки на URL-адреса других документов, относящихся к часовым поясам.

 

Точное совпадение маски форматирования

При преобразовании символьной строки в дату/время функции преобразования TO_*обычно руководствуются несколькими предположениями:

  • Лишние пробелы в символьной строке игнорируются.
  • Числовые значения (например, день года) не обязаны включать начальные нули для заполнения маски.
  • Знаки препинания в преобразуемой строке могут просто совпадать со знаками препинания в маске по длине и позиции.

Такая гибкость очень удобна — до того момента, когда вы захотите ограничить пользователя или даже пакетный процесс от ввода данных в нестандартном формате. В некоторых ситуациях разделение дня и месяца символом «^» вместо дефиса («-») попросту недопустимо. В таких ситуациях можно включить в маску форматирования модификатор FX, чтобы потребовать точного совпадения между строкой и маской форматирования.

С модификатором FX какая-либо гибкость в интерпретации строки полностью отсутствует. Строка не может содержать лишние пробелы, если их нет в модели. Ее числовые значения должны включать начальные нули, если модель форматирования включает дополнительные цифры. Наконец, знаки препинания и литералы должны точно соответствовать знакам препинания и заключенному в кавычки тексту маски форматирования (не считая регистра символов, который всегда игнорируется). Все эти правила продемонстрированы в следующих примерах: 

TO_DATE ('1-1-4', 'fxDD-MM-YYYY')
TO_DATE ('7/16/94', 'FXMM/DD/YY')
TO_DATE ('JANUARY^1^ the year of 94', 'FXMonth-dd-"WhatIsaynotdo"yy')

PL/SQL выдает одну из следующих ошибок:

ORA-01861: literal does not match format string
ORA-01862: the numeric value does not match the length of the format item 

Однако следующий пример выполняется успешно, потому что регистр символов не учитывается, и модификатор FX этого факта не меняет:

TO_DATE ('Jan 15 1994', 'fxMON DD YYYY') 

Модификатор FX может задаваться в верхнем регистре, нижнем регистре или со смешением регистров; его действие от этого не изменяется.

Модификатор FX работает как переключатель и может многократно встречаться в маске форматирования. Пример: 

TO_DATE ('07-1-1994', 'FXDD-FXMM-FXYYYY')

Каждый раз, когда модификатор FX встречается в маске форматирования, происходит переключение. В приведенном примере точное совпадение обязательно для дня и года, но не для месяца.

 

Ослабление требований к точности совпадения

Модификатор FM (Fill Mode, «режим заполнения») в маске форматирования при вызове TO_DATE или TO_TIMESTAMP используется для заполнения строки пробелами или нулями, чтобы обеспечить успешное прохождение проверки FX. Пример: 

TO_DATE ('07-1-94', 'FXfmDD-FXMM-FXYYYY')

Преобразование проходит успешно, потому что модификатор FM дополняет год 94 нулями, и тот превращается в 0094 (хотя трудно представить, чтобы вам когда-нибудь понадобилось делать что-то подобное). День 1 дополняется одним нулем и превращается в 01. Модификатор FM работает в режиме переключателя, как и FX.

Казалось бы, такое использование FM противоречит самой цели FX. Зачем использовать оба модификатора? Например, модификатор FX может применяться для принудительного использования конкретных ограничителей, тогда как FM ослабляет требование о вводе начальных нулей.

 

Интерпретация года из двух цифр

Переход в новое тысячелетие породил интерес к хранению года в формате из четырех цифр, потому что разработчики внезапно осознали неоднозначность часто встречающегося формата с двумя цифрами. Например, к какому году относится дата 1-Jan- 45 — к 1945 или 2045? В таких ситуациях лучше всего использовать однозначный год с четырьмя цифрами. Но несмотря на это понимание, старые привычки изменяются с трудом, а внесение изменений в существующие системы сопряжено с большими трудностями. Возможно, ваши пользователи предпочитают вводить год из двух цифр, а не из четырех. Для подобных случаев Oracle предоставляет форматный элемент RR, интерпретирующий год из двух цифр в скользящем окне.

В последующем обсуждении термин «век» используется в его житейском понимании. К 20-му веку относятся годы 1900–1999, а к 21-му — годы 2000–2099. Я понимаю, что такое определение не совсем корректно, но оно упрощает объяснение поведения RR.

Если текущий год относится к первой половине века (годы с 0 по 49), то:

  • при вводе даты, относящейся к первой половине века (то есть от 0 до 49) RR возвращает текущий век;
  • при вводе даты, относящейся ко второй половине века (то есть от 50 до 99), RR возвращает предыдущий век.

Если же текущий год относится ко второй половине века (годы с 50 по 99), то:

  • при вводе даты, относящейся к первой половине века, RR возвращает следующий век;
  • при вводе даты, относящейся ко второй половине века, RR возвращает текущий век.

Запутались? Я тоже разобрался не сразу. Правила RR пытаются предположить, какой век подразумевал пользователь, если он не был указан явно.

Рассмотрим несколько примеров воздействия RR. Обратите внимание: для годов 88 и 18 SYSDATE возвращает текущую дату из 20 и 21 века соответственно:

SQL> SELECT TO_CHAR (SYSDATE, 'MM/DD/YYYY') "Current Date",
2 TO_CHAR (TO_DATE ('14-OCT-88', 'DD-MON-RR'), 'YYYY') "Year 88",
3 TO_CHAR (TO_DATE ('14-OCT-18', 'DD-MON-RR'), 'YYYY') "Year 18"
FROM dual;
Current Date Year 88 Year 18
------------ ------- -------
02/25/2014      1988    2018
Когда мы достигаем года 2050, RR интерпретирует те же даты по-другому:
SQL> SELECT TO_CHAR (SYSDATE, 'MM/DD/YYYY') "Current Date",
2 TO_CHAR (TO_DATE ('10/14/88', 'MM/DD/RR'), 'YYYY') "Year 88",
3 TO_CHAR (TO_DATE ('10/14/18', 'MM/DD/RR'), 'YYYY') "Year 18"
4 FROM dual;
Current Date Year 88 Year 18
------------ ------- -------
02/25/2050      2088    2118

Логику RR в текущих приложениях можно активизировать несколькими способами. Самый простой и элегантный способ — изменение маски форматирования дат по умолчанию в экземпляре базы данных. Собственно, Oracle уже делает это за нас. В стандартной установке Oracle параметр NLS_DATE_FORMAT задается следующим образом: 

ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-RR';

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

 

Преобразование часовых поясов в символьные строки

Часовые пояса усложняют преобразование значений даты/времени в символьные строки. Информация часового пояса состоит из следующих элементов.

  • Смещение относительно UTC в часах и минутах.
  • Имя региона часового пояса.
  • Сокращение часового пояса.

Все эти элементы хранятся отдельно в переменной TIMESTAMP WITH TIME ZONE. Смещение относительно UTC присутствует всегда, но возможность вывода названия региона или сокращения зависит от того, задана эта информация или нет. Рассмотрим следующий пример: 

DECLARE
ts1 TIMESTAMP WITH TIME ZONE;
ts2 TIMESTAMP WITH TIME ZONE;
ts3 TIMESTAMP WITH TIME ZONE;
BEGIN
ts1 := TO_TIMESTAMP_TZ('2002-06-18 13:52:00.123456789 5:00',
'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM');
ts2 := TO_TIMESTAMP_TZ('2002-06-18 13:52:00.123456789 US/Eastern',
'YYYY-MM-DD HH24:MI:SS.FF TZR');
ts3 := TO_TIMESTAMP_TZ('2002-06-18 13:52:00.123456789 US/Eastern EDT',
'YYYY-MM-DD HH24:MI:SS.FF TZR TZD');
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ts1,
'YYYY-MM-DD HH:MI:SS.FF AM TZH:TZM TZR TZD'));
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ts2,
'YYYY-MM-DD HH:MI:SS.FF AM TZH:TZM TZR TZD'));
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ts3,
'YYYY-MM-DD HH:MI:SS.FF AM TZH:TZM TZR TZD'));
END;
Результат:
2002-06-18 01:52:00.123457000 PM -05:00 -05:00
2002-06-18 01:52:00.123457000 PM -04:00 US/EASTERN EDT
2002-06-18 01:52:00.123457000 PM -04:00 US/EASTERN EDT

В этом коде следует обратить внимание на некоторые аспекты, относящиеся к информации часовых поясов:

  • В значении, присвоенном переменной ts1, часовой пояс задается смещением относительно UTC. Соответственно при выводе ts1 выводится только смещение.
  • При отсутствии имени региона для ts1 Oracle предоставляет смещение часового пояса. Это все же лучше, чем полное отсутствие информации.
  • В значении, присвоенном переменной ts2, указывается регион часового пояса.

Внутреннее преобразование переводит его в смещение относительно UTC, но имя региона при этом сохраняется. Таким образом, для ts2 может быть выведено как смещение UTC, так и имя региона.

  • Для переменной ts2 Oracle правильно распознает действие летнего времени в июне. В результате значение ts2 неявно связывается с сокращением EDT.
  • В значении, присвоенном переменной ts3, задается как регион, так и сокращение часового пояса; соответственно, оба значения могут быть выведены.

Смещения относительно UTC и регионы часовых поясов связаны отношениями «один ко многим»; смещения недостаточно для однозначного определения имени региона. Вот почему невозможно вывести имя региона, если оно не было задано изначально.

 

Дополнение вывода с модификатором FM

Модификатор FM, описанный в разделе «Ослабление требований к точности совпадения», также может использоваться для преобразования даты/времени в символьную строку для подавления дополняющих пробелов и начальных нулей, которые могут быть возвращены функцией TO_CHAR.

По умолчанию следующая маска форматирования генерирует как дополняющие пробелы, так и начальные нули (название месяца отделяется от дня пятью пробелами):

TO_CHAR (SYSDATE, 'Month DD, YYYY') --> 'April 05, 1994'

Однако с модификатором FM в начале маски и лишний пробел, и начальные нули исчезают:

TO_CHAR (SYSDATE, 'FMMonth DD, YYYY') --> April 5, 1994' 

Модификатор может задаваться в верхнем регистре, нижнем регистре или со смешением регистров; его действие от этого не изменяется.

Помните, что модификатор FM работает как переключатель и может многократно встречаться в маске форматирования. Каждый раз, когда он встречается в маске форматирования, происходит переключение. По умолчанию (то есть если модификатор FM вообще не встречается в маске) пробелы не подавляются, а начальные нули включаются в итоговое значение.

 

Вас заинтересует / Intresting for you:

Управление приложениями PL/SQL...
Управление приложениями PL/SQL... 3065 просмотров Stas Belkov Thu, 16 Jul 2020, 06:20:48
Встроенные методы коллекций PL...
Встроенные методы коллекций PL... 6915 просмотров sepia Tue, 29 Oct 2019, 09:54:01
Тип данных RAW в PL/SQL
Тип данных RAW в PL/SQL 5666 просмотров Doctor Thu, 12 Jul 2018, 08:41:33
Символьные функции и аргументы...
Символьные функции и аргументы... 10753 просмотров Анатолий Wed, 23 May 2018, 18:54:01
Войдите чтобы комментировать

Программер аватар
Программер ответил в теме #9103 07 июль 2018 06:29
Хороший урок по программированию на PL/SQL. Обработка даты и времени весьма актуальная тема в кодинге!