Ich hatte mir da mal was gebastelt:

<code>
CREATE FUNCTION USMOD_16/DATESERIAL
(YEAR INTEGER,
MONTH INTEGER,
DAY INTEGER)
RETURNS DATE
LANGUAGE RPGLE
EXTERNAL NAME 'USMOD_16/DATSER'
DETERMINISTIC
RETURNS NULL ON NULL INPUT
NO SQL
NO EXTERNAL ACTION
PARAMETER STYLE SQL
ALLOW PARALLEL
NOT FENCED

hactgrp(*caller) dftactgrp(*no) datfmt(*iso)

d SYear s 10I 0
d SMonth s 10I 0
d SDay s 10I 0

d SQLDate s d
d SYear_Ind s 5I 0
d SMonth_Ind s 5I 0
d SDay_Ind s 5I 0

d SQLDate_Ind s 5I 0
d SQLState s 5
d FuncName s 139 varying
d SpecName s 128 varying
d DiagMsg s 70 varying

c *entry plist
c parm SYear
c parm SMonth
c parm SDay
c parm SQLDate
c parm SYear_Ind
c parm SMonth_Ind
c parm SDay_Ind
c parm SQLDate_Ind
c parm SQLState
c parm FuncName
c parm SpecName
c parm DiagMsg
c*
c/free
if SYear_ind <> *zero
or SMonth_ind <> *zero
or SDay_ind <> *zero;
SQLDate_ind = -1; // Ergebnis ist NULL
else;
monitor;
SQLDate = %date('0001-01-01')
+ %Years(SYear-1)
+ %Months(SMonth-1)
+ %Days(SDay-1);
on-error *all;
SQLDate_ind = -1; // Ergebnis ist NULL
endmon;
endif;
/end-free
c*
c return
</code>

Man kann das auch alles native in einer SQL-Prozedur kodieren.

Der Aufruf erfolgt dann in SQL so:

dateserial(JJJJ, MM, TT + n)

Bei Fehlern wird halt NULL geliefert.
Kleine Schmankerln:

1. des Monats: dateseriel(JJJJ, MM, 1)
Letzter des Monats: dateseriel(JJJJ, MM+1, 0)