| BIF | Description |
|---|---|
| %TRIM | Trim leading and trailing blanks: %TRIM(string [: characters] [: mode]). The optional characters argument replaces the blank rather than adding to it - %TRIM(s : '0') strips leading and trailing zeros and leaves surrounding blanks in place. Because a fixed-length CHAR field arrives blank-padded, passing one as characters puts the blank in the set along with whatever was keyed into it. See The character-counting mode for mode. |
| %TRIML | Trim leading blanks: %TRIML(string [: characters] [: mode]), same argument rules as %TRIM. |
| %TRIMR | Trim trailing blanks: %TRIMR(string [: characters] [: mode]), same argument rules as %TRIM. |
| %CHAR | Convert to character. With an explicit format operand - %CHAR(date : *fmt) - the date is rendered in that exact format, including the century forms (*CYMD → 124/07/04, *CMDY → 107/04/24, *CDMY → 104/07/24) and the Julian forms (*JUL → 24/186, *LONGJUL → 2024/186) for the date 2024-07-04. The century forms encode the century as a single digit, so they can only represent years 1900 - 2899; an explicit %CHAR(date : *CYMD/*CMDY/*CDMY) on a year outside that window raises the date-range error RNX0114 (%STATUS = 00114), which a MONITOR/ON-ERROR block can trap and which halts the program if left unmonitored. With no format operand, a date is rendered in the field's own declared DATFMT (e.g. a DATE(*MDY) field holding 2026-12-25 gives 12/25/26, *DMY gives 25/12/26, *EUR gives 25.12.2026, *JUL gives 26/359). Because the century formats (*CYMD/*CMDY/*CDMY) and *LONGJUL cannot be declared on a field, only the traditional external formats and *JUL appear this way; the century and long-Julian forms are available only through the explicit two-operand %CHAR(date : *fmt) conversion. A time operand behaves the same way: %CHAR(time : *fmt) renders it in the requested time format - *HMS (09:05:07) and *JIS (09:05:07) use colons, *ISO and *EUR (09.05.07) use periods, and *USA (09:05 AM / 09:30 PM) is the 12-hour clock with a leading-zero hour, no seconds, and a trailing AM/PM; with no format operand a time is rendered in the field's declared TIMFMT (default *ISO). For a FLOAT operand the result is the float external representation in its long 23-character form (+d.dddddddddddddddE+nnn, 16 significant digits, 3-digit exponent) for both FLOAT(8) and FLOAT(4) - a FLOAT(4) value is shown as its double-promoted value (e.g. %CHAR(%HIVAL(f4)) = +3.402823466385289E+038). This differs from %EDITFLT, which uses the compact 14-character form for a 4-byte float. An indicator-valued operand - an indicator field, or a relational or logical expression such as qty > 0 - renders as 1 or 0, because RPG has no boolean data type and a comparison carries an indicator's value. %DEC, %INT, %UNS and %FLOAT convert the same operand to the numeric 1 or 0. %EDITC is the exception: an indicator cannot be edited, and it is rejected with TRN1544, matching IBM i. |
| %LEN | Get length. For a character operand this is its length in bytes (or double-bytes): the declared length for a fixed-length field, the current length for a VARYING one. For a numeric operand it is the figure the field was declared with - the digit count for packed, zoned, integer and unsigned (PACKED(7:2) → 7, INT(20) → 20, UNS(10) → 10), and the byte size for a float (FLOAT(8) → 8). On the left-hand side of an assignment, %LEN(field) = n sets the current length of a variable-length field - shrinking truncates, growing exposes the new characters as blanks. Only a VARYING operand may be assigned to; a fixed or numeric operand is rejected with TRN1548 |
| %CHARCOUNT | Get character count. For single-byte data, same as %LEN. For multi-byte data (UTF-8, UCS-2), returns the number of characters, not bytes |
| %DEC | Convert to packed decimal. Takes the operand alone, a date/time plus a format, or an explicit precision and scale - see %DEC and %DECH |
| %DECH | Convert to packed decimal with half-adjust. Always takes all three operands - see %DEC and %DECH |
| %INT | Convert to integer |
| %GRAPH | Convert to Graphic (DBCS). On JVM this is an identity conversion (Java strings are UTF-16). Optional second argument (CCSID) is accepted but ignored. |
| %UCS2 | Convert to UCS-2. On JVM this is an identity conversion. Optional second argument (CCSID) is accepted but ignored. |
| %DIV | Integer quotient, e.g. %DIV(n : d); the fractional part is discarded (truncated toward zero). Operands must have zero decimal positions |
| %REM | Integer remainder, e.g. %REM(n : d); the result carries the sign of the dividend. Operands must have zero decimal positions |
| %ABS | Absolute value of a numeric expression, preserving its precision and scale |
| %SUBST | Substring: %SUBST(string : start [: length] [: mode]). Without length, the result runs to the end of the value. As an assignment target, %SUBST(field : start [: length]) = value splices value into that window of the field in place - left-justified and blank-padded to the window width, the rest of the field preserved. The target may be a plain field or a qualified DS subfield (%SUBST(ds.fld : ...) = value). |
| %SUBARR | Reference a contiguous slice of an array: %SUBARR(array : start [: number]). start is 1-based; number is optional and defaults to the remaining elements. As an assignment target, %SUBARR(array : start [: number]) = source writes the slice element by element - a scalar source broadcasts to every element, and an array or a %SUBARR(...) source is copied from its own start. Also usable as a SORTA target and a FOR-EACH iterable. |
| %LEFT | Return leftmost N characters: %LEFT(string : length). Returns the entire string if length >= string length |
| %RIGHT | Return rightmost N characters: %RIGHT(string : length). Returns the entire string if length >= string length |
| %SCAN | Scan for substring: %SCAN(searchArg : source [: start [: length]] [: mode]). The scan begins at start (1-based, default 1) and, when length is given, is confined to that many characters; a match must lie wholly inside the window. The answer is always a position in the whole source, not an offset into the window, so moving start does not renumber a match. Returns 0 when there is no match. |
| %UPPER | Convert to uppercase: %UPPER(string [: start [: length]] [: mode]). The optional start and length confine the fold to one window and the rest of the value is copied unchanged, so the result keeps the operand's full length - %UPPER('abcdef' : 3 : 2) is abCDef. |
| %LOWER | Convert to lowercase: %LOWER(string [: start [: length]] [: mode]), same window rules as %UPPER. |
| %EOF | End-of-file indicator. %EOF(file) for per-file state |
| %FOUND | Record-found indicator. %FOUND(file) is set by CHAIN (disk files and subfile records). The no-argument form %FOUND reads the most recent relevant operation (CHAIN, SETLL, SETGT, or DELETE), and is *OFF before any such operation |
| %DATE | Convert to date. %DATE() returns the current date; %DATE(value) converts a character value in *ISO form. With an explicit format - %DATE(value : *fmt) - the character value is parsed in that format: *ISO/*JIS, *USA, *EUR, the four-digit-year forms *MDYY/*DMYY/*YYMD, the two-digit-year forms *MDY/*DMY/*YMD, the Julian forms *JUL (yy/ddd) and *LONGJUL (yyyy/ddd), the job-relative *LONGJOBRUN (the long-year form of the job's own date format - a *MDY job reads mm/dd/yyyy, a *DMY job dd/mm/yyyy, a *YMD job yyyy/mm/dd, a *JUL job yyyy/ddd; it keeps the job's separator, so it is not *USA or *EUR, and it may be written where a value is converted but not as a field's declared DATFMT), and the century-digit forms *CYMD/*CMDY/*CDMY (a single leading century digit: 0→1900s, 1→2000s, so %DATE('115/04/15':*CYMD) = 2015-04-15). The two-digit-year formats use a 1940 - 2039 window, so %DATE('04/15/40':*MDY) = 1940-04-15 and %DATE('04/15/39':*MDY) = 2039-04-15. The operand may also be numeric: a numeric value carries no separators, so its digits are the date components in the format's field order, zero-padded on the left to the format's digit width (6 for *MDY/*DMY/*YMD, 5 for *JUL, 7 for *CYMD/*CMDY/*CDMY and *LONGJUL, 8 for *ISO/*JIS/*USA/*EUR/*MDYY/*DMYY/*YYMD). For example %DATE(41540:*MDY) = 1940-04-15 (041540 → mmddyy), %DATE(20241231:*ISO) = 2024-12-31, and %DATE(1231225:*CYMD) = 2023-12-25; the same field-order, year-window, and century rules apply as for character values. A value that is not a valid calendar date for the format - an out-of-range day such as Feb 30, Feb 29 in a non-leap year, or an out-of-range month, or a numeric value with more digits than the format allows - raises the date error %STATUS 00112 (trappable via MONITOR / (E) / %ERROR); it is never silently rolled to a nearby valid date. An unsupported format code is rejected with an error rather than silently parsed as *ISO. |
| %DIFF | Difference between two date, time or timestamp values: %DIFF(op1 : op2 : unit), returning op1 minus op2 so reversed operands give a negative. Units: *MSECONDS *SECONDS *MINUTES *HOURS *DAYS *MONTHS *YEARS. *MSECONDS is MICROseconds, not milliseconds, despite the spelling - two timestamps 1,476,915.25 seconds apart answer 1476915250000. The result truncates toward zero: that same pair answers 1476915 for *SECONDS, dropping the quarter-second rather than rounding. A timestamp operand supplies its date part where a date unit is asked for, so *DAYS between two timestamps is a whole number of days. |
| %DAYS | Day duration for date arithmetic, e.g. date + %DAYS(30) |
| %MONTHS / %YEARS | Month and year durations for date arithmetic, e.g. date + %MONTHS(3) |
| %HOURS / %MINUTES / %SECONDS / %MSECONDS | Time durations for time and timestamp arithmetic, e.g. ts + %HOURS(3); %MSECONDS is microseconds |
| %SQRT | Square root of a numeric expression: %SQRT(n) |
| %XFOOT | Sum of every element of a numeric array: %XFOOT(arr) |
| %SUBDT | Extract a date component, e.g. %SUBDT(date : *MONTHS) |
| %EDITC | Format a numeric value with an edit code, e.g. %EDITC(amount : '1'), returning the fixed-width edited image (leading blanks fill zero-suppressed positions). An optional third argument selects a fill or floating-currency character: *ASTFILL replaces leading blanks with asterisks (check protection), *CURSYM places the currency symbol from the CURSYM CTL-OPT keyword (default $) immediately before the first significant digit, and a character literal (e.g. '$') works like *CURSYM with that character. The third argument adds one reserved position to the result width (like the floating minus for N-Q codes). Supported codes: 1-4 (no sign), A-D (trailing CR for negatives), J-M (trailing -), N-Q (floating - before the first significant digit); within each group the four codes select grouping commas and zero-balance vs. blank-for-zero. X - no editing: the field's digits at full declared width, leading zeros retained, no point, with the sign carried as the EBCDIC zoned overpunch on the units digit for negatives (a 5,2 field holding -12.34 yields 0123M); a positive value shows no sign glyph. Y - date edit: mm/dd/yy (or mm/dd/yyyy) slashes with the leftmost zero suppressed. Z - strip sign and decimal point, suppress leading zeros. The decimal separator and thousands grouping character are controlled by the DECEDIT CTL-OPT keyword (see below). An unsupported edit code is rejected with a clear diagnostic. |
| %EDITFLT | Convert a numeric value to its float external representation. Returns a 23-character string in scientific notation (+d.dddddddddddddddE+nnn). For FLOAT(4) input, the result is 14 characters (+d.dddddddE+nn). |
| %MAX | Return the maximum of 2 or more numeric values. Variadic: %MAX(a : b [: c ...]). |
| %MIN | Return the minimum of 2 or more numeric values. Variadic: %MIN(a : b [: c ...]). |
| %HIVAL | Highest value representable by the operand's type: %HIVAL(field). Unlike the *HIVAL figurative constant (type taken from context), the type comes from the operand. Packed/zoned return all 9s (e.g. 99999.99); signed integers return the binary maximum (INT(5)=32767, INT(10)=2147483647, INT(20)=9223372036854775807); unsigned return 2^n - 1 (UNS(5)=65535, UNS(20)=18446744073709551615); float returns the largest finite IEEE 754 value, rendered by %CHAR in the long external form (FLOAT(4)=+3.402823466385289E+038, FLOAT(8)=+1.797693134862316E+308); indicators return *ON; character fields return a field full of x'FF'; times the end-of-day value 24.00.00, timestamps 9999-12-31-24.00.00.000000 (end-of-day time, zero fractional seconds). Dates honor the field's date format: the two-digit-year formats (*MDY, *DMY, *YMD, *JUL) return 2039-12-31; every other format - default *ISO, the four-digit-year formats, and the century formats (*CYMD/*CMDY/*CDMY) - returns 9999-12-31. The operand may also be a numeric DCL-ENUM group, in which case %HIVAL returns the largest enumerator value (by value, not declaration order). |
| %LOVAL | Lowest value representable by the operand's type: %LOVAL(field). Packed/zoned return negated all 9s (e.g. -99999.99); signed integers return the binary minimum (INT(5)=−32768); unsigned return 0; float returns the negative maximum, i.e. the most negative value, not the smallest positive, rendered by %CHAR in the long external form (FLOAT(4)=-3.402823466385289E+038, FLOAT(8)=-1.797693134862316E+308); indicators return *OFF; character fields return a field full of x'00'; times midnight 00.00.00, timestamps 0001-01-01-00.00.00.000000. Dates honor the field's date format: the two-digit-year formats (*MDY, *DMY, *YMD, *JUL) return 1940-01-01; every other format returns 0001-01-01. The operand may also be a numeric DCL-ENUM group, in which case %LOVAL returns the smallest enumerator value. A character enumeration is not valid (its collating sequence is CCSID-dependent). |
| %XLATE | Translate characters: %XLATE(from : to : string [: startpos]). Each character in from is replaced with the corresponding character in to. Optional start position (1-based, default 1) leaves characters before it unchanged. |
| %SCANRPL | Scan and replace all occurrences: %SCANRPL(scanFor : replaceWith : string [: start [: length]]). Replaces every occurrence of scanFor with replaceWith within the scan region. Optional start (1-based, default 1) and length limit the scan range. |
| %REPLACE | Replace a portion of a string by position: %REPLACE(replacement : source [: start [: length]]). Inserts replacement into source starting at start (1-based, default 1), replacing length characters (default: the length of replacement; length 0 inserts without removing). %REPLACE('X' : 'Hello World' : 1 : 5) yields X World. The result is a character value, so it may be stored into a fixed CHAR field (padded/truncated to its width) or a varying field. |
| %LOOKUP | Search an array, e.g. %LOOKUP(value : array : start : count); returns 1-based index or 0. Ordered variants: %LOOKUPGT (closest >), %LOOKUPGE (closest >=), %LOOKUPLT (closest <), %LOOKUPLE (closest <=) - require ASCEND or DESCEND keyword on the array. %LOOKUPNE (first not-equal) does not require ordering. Every array element type is searchable: character arrays collate under the array's CCSID (see Character Comparison Semantics) and numeric arrays - packed, zoned, integer, unsigned and float - compare by numeric value, so a search argument of one numeric type matches an element of another with the same value. An UNS(20) array compares by its full unsigned magnitude, so a value at or above 2^63 is the largest, not the smallest |
| %TLOOKUP | Search a CTDATA table: %TLOOKUP(arg : searchTable {: altTable}); returns *ON if found, *OFF otherwise. Positions the search table (and optional alternate table) at the matched entry - a subsequent unindexed reference to the table name yields the found element. Ordered variants: %TLOOKUPGT, %TLOOKUPGE, %TLOOKUPLT, %TLOOKUPLE. Not-equal variant: %TLOOKUPNE |
| %ELEM | Number of elements in an array (its DIM) or occurrences in an OCCURS data structure. For varying-dimension arrays (DIM(*AUTO:max) or DIM(*VAR:max)), returns the current active element count. %ELEM(arr:*MAX) returns the maximum dimension. %ELEM(arr) = n sets the active count (for *VAR or *AUTO) |
| %OCCUR | Get or set the active occurrence of a multiple-occurrence DS. curOc = %OCCUR(ds) reads; %OCCUR(ds) = n writes. Out-of-range raises status 00122 |
| %KDS | Key data structure for keyed I/O. %KDS(ds) uses the subfields of a LIKEREC(format:*KEY) data structure as the composite search key for CHAIN, SETLL, SETGT, READE, READPE. Optional second arg limits the number of key fields: %KDS(ds:2) uses only the first two subfields. Replaces fixed-format KLIST/KFLD |
| %EQUAL | Exact-match indicator. %EQUAL(file) is *ON when SETLL finds a record whose key exactly equals the search argument, *OFF otherwise. Only SETLL changes it - it keeps the last SETLL result across an intervening CHAIN or SETGT, and is *OFF before any SETLL. The no-argument form %EQUAL reads the most recent SETLL (or LOOKUP), and is *OFF before any such operation |
| %OPEN | File open condition. %OPEN(file) returns *ON when the file is open, *OFF when closed. Useful with USROPN files to guard OPEN/CLOSE operations |
| %ERROR | True when the most recent (E)-extended operation failed; may be written without parentheses |
| %STATUS | Status code of the most recent operation (0 = success); may be written without parentheses |
| %CHECK | %CHECK(comparator : base { : start }) returns the 1-based position of the first character in base (at or after start, default 1) that is not in the comparator set, or 0 when every scanned character is allowed |
| %SIZE | %SIZE(name) returns the storage size in bytes of a scalar variable (one byte per alpha character, precision / 2 + 1 for packed decimal, full digit count for zoned, 1/2/4/8 for INT/UNS by size, etc.). A varying-length field also counts its length prefix - 2 bytes normally, 4 when the declaration asks for it - so %SIZE of a VARCHAR(5) is 7 and of a VARCHAR(5 : 4) (equivalently CHAR(5) VARYING(4)) is 9; contrast %LEN, which reports the current data length and excludes the prefix. The prefix is a byte count even for double-byte data, so a VARGRAPH(3) is 8 (three 2-byte characters plus 2). For a data structure it returns the structure's total byte size - the sum of its subfield sizes - including a plain, non-qualified named DS (%SIZE(dsName)). %SIZE(name) is the size of one element or occurrence; %SIZE(name : *ALL) returns the whole allocation - that size times the DIM count for an array or DIM data structure, or the OCCURS count for a multiple-occurrence data structure. On a scalar, *ALL changes nothing (one element). For a varying-dimension array (DIM(*AUTO:n) / DIM(*VAR:n)) *ALL reports the declared maximum, since the allocated storage does not shrink as elements are removed. Both forms are compile-time constants |
| %SPLIT | %SPLIT(string : separator {: *ALLSEP}) splits a string into an array. Each character in the separator string is treated as a delimiter. By default (or with *SPLTSEP), consecutive separators are collapsed (no empty elements). With *ALLSEP, every separator marks a boundary - consecutive separators produce empty substrings. Default separator is space. Assign the result to a DIM'd array: Parts = %SPLIT('A,,B' : ',' : *ALLSEP) produces 'A', '', 'B'. The result array may be character (fixed or varying), graphic, or UCS-2; each token is placed in its element at the array's declared element length (a token longer than that width is truncated to it, matching IBM i). |
| %CONCAT | %CONCAT(separator : val1 : val2 {: val3 ...}) concatenates two or more character values with a separator between each pair: %CONCAT(', ' : 'A' : 'B' : 'C') produces A, B, C. The separator is a character expression; each value is trimmed of trailing blanks before joining. Use *BLANK or ' ' for a single space, or *NONE for no separator at all (%CONCAT(*NONE : 'A' : 'B') produces AB). The result is varying-length. |
| %CONCATARR | %CONCATARR(separator : array) concatenates all elements of an array with a separator between each pair: %CONCATARR(', ' : names) produces Cat, Dog, Pony. Elements are trimmed of trailing blanks before joining. The separator follows the same rules as %CONCAT - *NONE for no separator, *BLANK for a single space. The array operand may be a DIM standalone, a data-structure array, %SUBARR, %LIST, or %SPLIT. The result is varying-length. |
| %LIST | %LIST(val1 : val2 : ...) creates a temporary array from literal values and assigns it to a DIM'd array: Names = %LIST('Mary' : 'Jack' : 'Alice'). Excess values beyond the target DIM are dropped; fewer values leave remaining elements unchanged. Also usable with the IN operator for membership testing: IF x IN %LIST(1 : 2 : 3) |
| %RANGE | %RANGE(lower : upper) defines a range for membership testing with the IN operator: IF score IN %RANGE(90 : 100). Equivalent to score >= 90 AND score <= 100. Works with numeric, character, and date types |
| %FIELDS | %FIELDS(field1 : field2 : ...) restricts which fields an UPDATE writes back. Free-format only |
| %ADDR | %ADDR(x) returns a basing pointer to the storage of x. For a procedure parameter, the address compares equal to *NULL when the caller passed *OMIT for an OPTIONS(*OMIT) parameter - use it to detect an omitted argument: if %addr(p) = *null;. For a module-level (global) variable, the returned pointer aliases the variable's storage: assign it to a basing pointer and declare a BASED field over that pointer, and writes through either the variable or the BASED field are visible through the other (bidirectional). %ADDR of a data-structure subfield (%ADDR(ds.sub)) is likewise supported: the pointer aliases just that subfield's bytes within the data structure, so a BASED field over it reads and writes the subfield bidirectionally without touching neighboring subfields. %ADDR of a whole data structure by name (%ADDR(ds), qualified or not) returns a pointer to the data structure's storage - the leftmost byte of its contiguous buffer - so it equals %ADDR of the first subfield; a BASED field over it aliases the structure's leading bytes bidirectionally. A nested LIKEDS subfield is addressable the same way, indexed or not: %ADDR(ds.sub) and %ADDR(ds.sub(i)) point at that structure's first byte within the parent, so %ADDR(ds.arr(2)) - %ADDR(ds.arr(1)) is the element stride and a BASED structure over the address aliases the parent's bytes in both directions. %SIZE(ds.sub) reports one element, as it does for an array. Taking the address of a procedure-local variable is supported. %ADDR of a standalone array element (%ADDR(arr(i))) is likewise supported: address-taken arrays are backed by contiguous storage, and %ADDR(arr(i)) returns a window at the element's byte offset - so %ADDR(arr(2)) - %ADDR(arr(1)) yields the element stride. Pointer arithmetic across separately-allocated storage (two unrelated variables, or a pointer that has left its item) has no answer on the JVM - it is rejected at runtime with status 222. |
| %OMITTED | %OMITTED(param) returns *ON when the caller passed *OMIT for an OPTIONS(*OMIT) parameter, *OFF otherwise |
| %THIS | Returns the Java object instance (this reference). Always returns the program instance since every compiled program is a Java class |
| %PROC | Returns the current procedure name. Inside ON-EXIT: %PROC(*OWNER) returns the owning procedure name; %PROC(*ONEXIT) returns the ON-EXIT implementation name (IBM convention: _QRNI_ON_EXIT_ + procedure name) |
The status built-in functions %EOF, %EQUAL, %ERROR, %FOUND, and %STATUS
may be written with or without parentheses (for example, IF %ERROR;). After a
successful (E)-extended operation, %ERROR is *OFF and %STATUS is 0; a
failing operation sets %ERROR *ON and %STATUS to the operation's status code.
The character-counting mode
Sixteen string built-ins accept *NATURAL or *STDCHARSIZE as an optional
final argument, choosing whether positions and lengths are counted in
characters or in standard character size:
%CHECK %CHECKR %LEFT %LOWER %REPLACE %RIGHT %SCAN %SCANR
%SCANRPL %SPLIT %SUBST %TRIM %TRIML %TRIMR %UPPER %XLATE
The mode always goes last, after that built-in's other arguments - third on
%TRIM, fourth on %SUBST, fifth on %SCAN, sixth on %SCANRPL. Arguments
before it may still be omitted, so %SCAN('b' : s : 1 : *NATURAL) is valid with
the length left out. A mode word written in any earlier position is occupying an
argument slot that holds a real operand and is rejected with
TRN2147.
For a single-byte operand the two words select the same positions - one character is one byte - so either may be written and the result is the same. Passing the mode on single-byte data is therefore always accepted, and is the common case in practice.
An operand declared CCSID(*UTF8) is accepted with either mode: *NATURAL
counts characters and *STDCHARSIZE counts standard character size.