JSTL Formatting & Functions
JSTL Formatting & Functions
The previous lesson covered JSTL Core tags — the control-flow backbone of any JSP view. This lesson moves into two equally practical libraries: the fmt tag library for locale-aware number and date formatting, and the fn function library for string manipulation inside EL expressions. Together they eliminate the ugly Java scriptlet code that developers historically stuffed into JSP pages to format output.
Setting Up: Adding the fmt and fn Libraries
Both libraries ship in the same jakarta.servlet.jsp.jstl JAR (JSTL 3.x for Jakarta EE 10+). You already have it from the Core lesson. Declare them at the top of each JSP that needs them:
http://java.sun.com/jsp/jstl/... to jakarta.tags.*. If you are on a legacy Tomcat 9 / Java EE stack you still use the old URIs. The tag behaviour is otherwise identical.
Locale and Time Zone Scoping with fmt:setLocale and fmt:setTimeZone
All fmt formatting tags respect the current locale and time zone, which you set once per page (or per request) rather than repeating in every tag:
When neither tag appears, the JSP engine falls back to the server JVM locale and UTC, which is almost never what end users want. Always set both explicitly in production views.
Formatting Numbers with fmt:formatNumber
<fmt:formatNumber> converts any numeric value into a locale-correct string. Its type attribute drives the output style:
type="number"— plain decimal with grouping separators (default)type="currency"— currency symbol + grouping + decimal places from the localetype="percent"— multiplies by 100 and appends a percent sign
With en_US locale and price = 1299.9, the currency tag produces $1,299.90. Switching to de_DE produces 1.299,90 € — the tag handles both symbol and punctuation conventions automatically.
currencyCode="EUR" (or any ISO 4217 code) to display the correct symbol regardless of locale.
To capture the formatted string into a variable instead of writing it immediately, use the var and scope attributes — a pattern shared by most JSTL tags:
Parsing Numbers with fmt:parseNumber
The reverse operation — turning a locale-formatted string back into a number — is handled by <fmt:parseNumber>. You rarely need this in a view layer, but it is useful in filters or tag files that receive string parameters from query strings:
Formatting Dates and Times with fmt:formatDate
<fmt:formatDate> accepts a java.util.Date (or a value that resolves to one through EL coercion) and formats it according to the locale and time zone in scope. The type attribute controls which part of the timestamp is shown:
The dateStyle and timeStyle attributes accept short, medium, long, and full — the same constants as java.text.DateFormat. The pattern attribute takes a SimpleDateFormat pattern and overrides style attributes when both are supplied.
java.time.* (LocalDate, ZonedDateTime, Instant). fmt:formatDate only understands java.util.Date, so you must convert: Date.from(instant) or Date.from(localDate.atStartOfDay(ZoneId.of("UTC")).toInstant()). In Spring MVC you can register a custom converter so that EL resolves java.time objects directly, but fmt:formatDate itself still needs the old type.
Parsing Dates with fmt:parseDate
The companion tag <fmt:parseDate> converts a string back into a java.util.Date:
Internationalising Text with fmt:message
The fmt library also owns the i18n message tag. Load a resource bundle and look up keys by name:
The bundle file messages_en_US.properties would contain lines such as order.confirmation=Your order #{0} has been placed.. This integrates with Java's MessageFormat placeholders.
The fn Function Library
The fn library provides string and collection utility functions that can be called directly inside EL expressions. There is no separate tag — every function is invoked as ${fn:functionName(arg1, arg2)}.
The most commonly used functions:
fn:length(collection)— length of a String, array, or Collectionfn:toUpperCase(str)/fn:toLowerCase(str)fn:trim(str)fn:substring(str, begin, end)— zero-based, exclusive end (mirrorsString.substring)fn:substringBefore(str, delimiter)/fn:substringAfter(str, delimiter)fn:replace(str, before, after)fn:split(str, delimiter)— returns aString[]fn:join(array, separator)— inverse of splitfn:contains(str, substring)— case-sensitive; returns booleanfn:containsIgnoreCase(str, substring)fn:startsWith(str, prefix)/fn:endsWith(str, suffix)fn:indexOf(str, substring)fn:escapeXml(str)— entity-escapes<,>,&,",'
fn Functions in Practice
Practical snippet: display a truncated product description with an ellipsis, guard against null, and show a badge only when a tag list is non-empty:
${review.body} without escaping is a stored XSS vulnerability. fn:escapeXml is the last line of defence in the view layer.
Combining fmt and fn Together
Functions from fn compose with fmt tags through the var attribute. A common pattern is to build a string with fn, store it in a variable, then pass it as a parameter to a message tag:
Trade-offs and When to Move Logic to the Controller
The fmt and fn libraries handle presentation-only transformations cleanly in the view. However, there are cases where you should push formatting back to the Java layer:
- Reuse across multiple views — if six different JSPs display a price the same way, format it once in the controller and pass a pre-formatted string, or use a shared tag file.
- Business logic in the format — choosing between tax-inclusive and tax-exclusive prices based on user geography is not presentation; it belongs in a service class.
- java.time types — format with
DateTimeFormatterin the controller and expose a plain String to the view to avoid thejava.util.Dateconversion workaround.
Summary
The fmt library gives you locale-aware, time-zone-correct formatting for numbers, currencies, percentages, and dates with zero Java in the page. The fn library brings string utilities — length, substring, trim, replace, split, join, escapeXml — directly into EL expressions. Together they cover nearly every output-formatting need in a JSP view layer without a single scriptlet. The next lesson examines the MVC pattern that governs how Servlets and JSPs collaborate.