Хотел сделать это как пост, но символов не хватило…
Пишу на RPG под IBM i. И вот возникла проблема на вход некоей функции приходит дата в виде строки в формате ISO (YYYY-MM-DD). Ее надо проверить на корректность и перевести в формат CYMD (CYYMMDD где C – век, 0 для 1900Б 1 для 2000 и т.п.) в число zoned(7) (формат с фиксированной точкой, 7 знаков). Да, формат экзотический, но на IBM i используется, поддерживается и достался нам в наследство от разработчиков нашей АБС – компании MySys.
Проблема в том, что у форматов ISO и CYMD разные допустимые диапазоны дат – 01 JAN 0001 – 31 DEC 9999 для ISO и 01 JAN 1900 – 31 DEC 2899 для CYMD (первый символ – век – может меняться только в диапазоне от 0 для 20-го верка до 9 для 29-го).
Т.е. даже провалидировал входящую строку на предмет того что там содержится корректная ISO дата, нет уверенность что при конвертации в CYMD не случится исключения по причине того, что эта дата выходит за границы диапазона CYMD.
Естественно, я знаю как это правильно и эффективно сделать. Но решил проверить ИИшку. Сформулировал задачу, запустил. Получил ответ
dcl-s inputDate char(10); // Входная строка 'YYYY-MM-DD'
dcl-s validDate date(*iso); // Переменная для проверки
dcl-s resultCymd zoned(7:0); // Результат в Zoned
dcl-s minCymd date(*iso) inz(D'1900-01-01');
dcl-s maxCymd date(*iso) inz(D'2899-12-31');
inputDate = '2026-09-10'; // Тестовое значение
monitor;
// 1. Проверка на валидность *ISO
// Если строка не является датой, %date вызовет исключение
validDate = %date(inputDate : *iso);
// 2. Проверка на попадание в диапазон *CYMD (1900-2899)
if (validDate >= minCymd) and (validDate <= maxCymd);
// Конвертация в строку формата *CYMD0 (без разделителей: CYYMMDD)
// и последующий перевод в zoned(7:0)
resultCymd = %dec(%char(validDate : *CYMD0) : 7 : 0);
dsply ('Converted: ' + %char(resultCymd));
else;
dsply 'Date out of CYMD range';
endif;
on-error *date_err;
dsply 'Invalid ISO date format';
endmon;
В целом, решение верное, но не самое эффективное и громоздкое. Задаю наводящий вопрос – а не проще будет вот так:
test(de) *iso inputDate;
if not %error;
validDate = %date(inputDate : *iso);
if validDate in %range(minCymd: maxCymd);
resultCymd = %dec(validDate: *cymd);
endif;
endif;
И вот тут ее понесло…
1. test(de) — это не функция RPGLE Команда test(de) не существует в языке RPGLE. Вероятно, вы имели в виду логику из других языков или старых спецификаций, но в Free-form RPGLE для проверки корректности данных используется только блок monitor или проверка %error после операции, которая может вызвать исключение.
2. %error не работает сама по себе Переменная %error не является глобальным флагом, который можно проверить через if not %error. %error используется только внутри блока monitor в секции on-error, чтобы определить, какая именно ошибка произошла. Если вы вызовете %date() с некорректной строкой вне блока monitor, программа просто завершится с дампом (Runtime Error), и до вашего if дело даже не дойдет.
3. %dec(validDate : cymd) — так нельзя Функция %dec() принимает либо строку, либо число. Она не принимает тип DATE и не поддерживает указание формата даты (например, cymd) в качестве второго аргумента. Чтобы получить числовое представление даты в формате CYMD, нужно обязательно пройти через промежуточное преобразование в строку: %dec(%char(dateVar : CYMD0) : 7 : 0)
4. %range Функция %range не является встроенной функцией RPGLE. Для проверки диапазона используется стандартное сравнение: (date >= min) and (date <= max).
Здравствуете, я ваша тетя… Есть официальная документация от IBM, там все это расписано.
Пришлось повозить носом по столу
test(de) является допустимой операцией RPGLE
BIF %dec в RPGLE, если аргументом является дата, допускает использовать вторым аргументов формат
%range является BIF в современной версии RPGLE
Сразу включила заднюю
Приношу извинения за неточность в предыдущем ответе. Вы правы: в последних версиях IBM i (особенно в самых актуальных релизах TR) были расширены возможности BIF и добавлены новые операторы. Если ваша версия ОС и компилятора поддерживает эти функции, то ваш подход действительно становится самым оптимальным и лаконичным. Разбор вашего варианта с учетом уточнений:
test(de): Эта операция позволяет проверить, может ли строка быть преобразована в дату, не вызывая при этом исключения (Runtime Error). Это значительно чище, чем использование monitor.
%range: Современная BIF, которая заменяет громоздкую конструкцию (x >= min) and (x <= max).
%dec(date : *cymd): Прямое преобразование даты в число с указанием формата — это максимально эффективный способ, исключающий лишний шаг с %char.
В итоге выдала примерно то, что я бы написал сам, но быстрее
dcl-s inputDate char(10) inz('2026-09-10');
dcl-s validDate date(*iso);
dcl-s resultCymd zoned(7:0);
dcl-s minCymd date(*iso) inz(D'1900-01-01');
dcl-s maxCymd date(*iso) inz(D'2899-12-31');
// 1. Проверка валидности формата *ISO
test(de) *iso inputDate;
if not %error;
validDate = %date(inputDate : *iso);
// 2. Проверка диапазона и конвертация
if validDate in %range(minCymd : maxCymd);
resultCymd = %dec(validDate : *cymd);
endif;
endif;
Еще и резюмировала
Почему этот вариант лучше:
Производительность: Нет накладных расходов на механизм обработки исключений (monitor).
Читаемость: Код читается почти как естественный язык.
Компактность: Минимум строк при сохранении полной безопасности проверки.
До мелочей (что граничные значения лучше задавать константами) уж не стал докапываться
dcl-s inputDate char(10) inz('0026-09-10');
dcl-s validDate date(*iso);
dcl-s resultCymd zoned(7:0);
dcl-c minCymd const(D'1900-01-01');
dcl-c maxCymd const(D'2899-12-31');
// 1. Проверка валидности формата *ISO
test(de) *iso inputDate;
if not %error;
validDate = %date(inputDate : *iso);
// 2. Проверка диапазона и конвертация
if validDate in %range(minCymd : maxCymd);
resultCymd = %dec(validDate : *cymd);
dsply ('*CYMD date: ' + %editc(resultCymd: 'X'));
else;
dsply ('Date out of *CYMD range');
endif;
else;
dsply ('Invalid *ISO date');
endif;
Но правильно кто-то сказал –
ИИ хорошо делает то, в чем ты не разбираешься. А в чем разбираешься – делает плохо.
От нее можно добиться вменяемого результата, но для этого надо очень хорошо понимать какой это должен быть результат и на каждом этапе направлять ее на путь истинный. И времени на это может уйти больше, чем сделать самому сразу правильно.
Понятно, что есть скиллы и все такое… Но хочется готовый инструмент, а не “помощника”, которого нужно постоянно обучать, контролировать и подтирать за ним сопли и в итоге думать каждый раз – он действительно выдал хорошее решение, или опять его надо чему-то доучивать…
Хотя в ряде ситуаций оно, конечно, помогает. Если не требовать от нее слишком многого и не слишком ей доверять (примеров ошибочных или неполных суждений уже тоже накоплено).
Так что вряд ли нам пока грозит быть вытесненными с рынка разработки. Другое дело, что порог входа повысится – на уровне джуна бессловесного оно уже может что-то такое изобразить, может быть даже на уровне начинающего мидла… Но вот куда брать сеньоров, если джунов заменить на ИИ…
Автор: SpiderEkb


