Type conversion to strings (Text) is easy, as long as you master the FORMAT command, check it out:

In this video, Erik walks through one of the oldest and most widely used functions in AL: Format. This function takes a variable of virtually any type and converts it into a string representation. Erik demonstrates how it works with decimals, dates, times, GUIDs, JSON objects, and more — and explains the critical formatting parameters that every AL developer should know.
The Problem: Type Conversion
AL is a strongly typed language. Unlike JavaScript, which is very forgiving about types (which is why TypeScript was invented), AL cares a lot about type safety. You can’t simply assign a decimal to a text variable:
procedure FormatDemo()
var
MyDecimal: Decimal;
MyText: Text;
begin
MyDecimal := 123456;
MyText := MyDecimal; // ERROR: Cannot implicitly convert type Decimal to Text
Message(MyText);
end;
The compiler will give you an error: “Cannot implicitly convert type Decimal to Text.” Note that some implicit conversions do work — you can assign an integer to a decimal without any issues, because the compiler knows how to handle that. But decimal to text? You need an explicit conversion.
Enter the Format Function
The Format function is your answer. It takes a value of virtually any type (the signature shows a “Joker” type, meaning it accepts anything) and returns a text representation:
MyText := Format(MyDecimal);
Message(MyText);
The function signature accepts up to three parameters:
- Value — the variable to convert (any type)
- Length — the desired string length
- FormatNumber or FormatString — controls how the value is formatted
The Length Parameter
The second parameter controls the length of the resulting string. If you pass 40, the output will be padded to 40 characters. However, in most cases you’ll just pass 0, which means “give me exactly the length needed to represent the value and nothing more”:
MyText := Format(MyDecimal, 0); // Returns the string with no extra padding
A value of zero does not return an empty string — it simply means “use whatever length is necessary.”
The Format Number Parameter
The third parameter is where things get really interesting. When passed as a number, it controls which formatting standard is used:
- 0 — Standard display format (locale-dependent). A decimal like
1234567will display as1,234,567in the US or Canada, using local conventions for decimal separators and thousands separators. - 2 — AL format. This gives you the value as it would appear in AL code, independent of locale. This is important because if you format with
0, it looks great in your country, but if you’re building a file to send elsewhere, they might interpret the number differently. For example, in Denmark the comma is the decimal separator and the dot is the thousands separator — the exact opposite of the US convention. - 9 — XML format. This is the go-to format for data exchange with external systems.
Format Numbers with Dates
The difference between these format numbers becomes especially clear with dates:
procedure DateFormatDemo()
var
MyDate: Date;
MyText: Text;
begin
MyDate := Today();
MyText := Format(MyDate, 0, 0); // e.g., "7/9/2022" (US locale)
Message(MyText);
MyText := Format(MyDate, 0, 2); // e.g., "070922D" (AL format)
Message(MyText);
MyText := Format(MyDate, 0, 9); // e.g., "2022-07-09" (XML/ISO format)
Message(MyText);
end;
Format 9 produces the YYYY-MM-DD format used in XML and typically in JSON as well. Whenever you need to work with dates at an import/export level with other systems, format 9 is the way to go.
Custom Format Strings
Instead of passing a number as the third parameter, you can pass a custom format string. This is where Erik offers an essential tip: no matter how senior or experienced the AL developer, everyone either consults the documentation or keeps a cheat sheet handy when building custom format strings.
Custom Date Formatting
Format strings use commands enclosed in angle brackets, with literal text between them:
MyText := Format(MyDate, 0, '<Closing><Day>. <Month Text> <Year4>');
// Result: "9. July 2022"
In this format string:
<Closing>— indicates whether this is a closing date<Day>— the day number<Month Text>— the full month name<Year4>— the four-digit year (as opposed to<Year>which gives only two digits)
Custom Time Formatting
Time formatting works similarly, with options for filler characters:
procedure TimeFormatDemo()
var
MyTime: Time;
MyText: Text;
begin
MyTime := 010203T;
// With filler character '0' for 24-hour format
MyText := Format(MyTime, 0, '<Filler Character,0><Hours24,2>:<Minutes,2>:<Seconds,2>');
// Result: "01:02:03"
// Without filler character (default is space)
MyText := Format(MyTime, 0, '<Hours24,2>:<Minutes,2>:<Seconds,2>');
// Result: " 1:02:03" (note the leading space)
end;
The ,2 after a command means “this should take up two characters.” The default filler character is a space, so without specifying <Filler Character,0>, you’ll get a leading space instead of a leading zero.
Other Types
The documentation covers formatting options for many other types:
- Booleans — choose between true/false, uppercase/lowercase, or XML format
- GUIDs — can be shown with curly brackets (the traditional Microsoft style), without dashes, or as a hexadecimal representation with
0xprefix - Enums — display as text or as the underlying number
- Integers — various numeric representations
Formatting Complex Types: JSON Objects
Format can even handle complex types like JSON objects. Under the hood, this works because the .NET runtime provides a ToString method that Format leverages:
procedure JsonFormatDemo()
var
JObj: JsonObject;
MyText: Text;
begin
JObj.Add('value', 123);
JObj.Add('house', 'ABC');
MyText := Format(JObj);
Message(MyText);
// Outputs the JSON as a string: {"value":123,"house":"ABC"}
end;
Erik notes that format numbers like 0, 1, and 2 all produce the same compact JSON output. He makes a tongue-in-cheek feature request to Microsoft: it would be great if format number 3 produced pretty-printed JSON — but alas, that doesn’t exist (yet!).
Important: Format Is for Presentation, Not Storage
Erik closes with a critical best practice: never use Format for internal storage. When you format a date to a string, you do it because you need to present the date to a user, write it into a file, or send it to another system. You should always store the value in its native type (Date, Decimal, Integer, etc.) and only convert to text at the point of presentation or export.
Conclusion
The Format function is one of the foundational tools in AL development. With its three parameters — value, length, and format number/string — it gives you full control over how any data type is represented as text. The key takeaways are:
- Use
0as the length parameter unless you need fixed-width strings - Format number
0gives you locale-dependent display formatting - Format number
2gives you consistent AL formatting, independent of locale - Format number
9gives you XML-standard formatting — ideal for data exchange - Custom format strings give you complete control, but keep the documentation handy
- Always store data in its native type; only use
Formatfor presentation and export