public class EventForm {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate eventDate;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime createdAt;
@DateTimeFormat(pattern = "HH:mm:ss")
private LocalTime startTime;
@DateTimeFormat(style = "MM") // Medium date, medium time
private LocalDateTime timestamp;
@DateTimeFormat(pattern = "yyyy-MM-dd",
fallbackPatterns = {"dd/MM/yyyy", "MM-dd-yyyy"})
private LocalDate flexibleDate;
}public class ProductForm {
@NumberFormat(style = NumberFormat.Style.CURRENCY)
private BigDecimal price;
@NumberFormat(style = NumberFormat.Style.PERCENT)
private Double discountRate;
@NumberFormat(pattern = "#,###.##")
private Double quantity;
@NumberFormat(pattern = "$#,##0.00;-$#,##0.00")
private BigDecimal profit;
}public class TaskConfig {
@DurationFormat(style = DurationFormat.Style.ISO8601)
private Duration timeout; // PT1H30M
@DurationFormat(style = DurationFormat.Style.SIMPLE)
private Duration cacheRefresh; // 1h 30m
@DurationFormat(style = DurationFormat.Style.COMPOSITE)
private Duration maxExecution; // 1h12m27s
}public class PhoneNumberFormatter implements Formatter<PhoneNumber> {
@Override
public PhoneNumber parse(String text, Locale locale) throws ParseException {
String digits = text.replaceAll("[^0-9]", "");
if (digits.length() != 10) {
throw new ParseException("Invalid phone number", 0);
}
return new PhoneNumber(digits);
}
@Override
public String print(PhoneNumber number, Locale locale) {
String digits = number.getDigits();
return String.format("(%s) %s-%s",
digits.substring(0, 3),
digits.substring(3, 6),
digits.substring(6, 10));
}
}@Configuration
public class FormattingConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// Register formatter
registry.addFormatter(new PhoneNumberFormatter());
// Register by field type
registry.addFormatterForFieldType(
Currency.class,
new CurrencyFormatter()
);
// Register annotation factory
registry.addFormatterForFieldAnnotation(
new DateTimeFormatAnnotationFormatterFactory()
);
// Date/time registrar
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}public class CurrencyFormatterFactory
implements AnnotationFormatterFactory<CurrencyFormat> {
@Override
public Set<Class<?>> getFieldTypes() {
return Set.of(BigDecimal.class, Double.class);
}
@Override
public Printer<?> getPrinter(CurrencyFormat annotation, Class<?> fieldType) {
return new CurrencyPrinter(annotation.currencyCode());
}
@Override
public Parser<?> getParser(CurrencyFormat annotation, Class<?> fieldType) {
return new CurrencyParser(annotation.currencyCode());
}
}
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrencyFormat {
String currencyCode() default "USD";
}// Number style
NumberStyleFormatter formatter = new NumberStyleFormatter("#,###.00");
formatter.setLenient(true);
// Currency style
CurrencyStyleFormatter currencyFormatter = new CurrencyStyleFormatter();
currencyFormatter.setFractionDigits(2);
currencyFormatter.setRoundingMode(RoundingMode.HALF_UP);
currencyFormatter.setCurrency(Currency.getInstance("USD"));
// Percent style
PercentStyleFormatter percentFormatter = new PercentStyleFormatter();// Requires JSR-354 implementation (e.g., Moneta)
<dependency>
<groupId>org.javamoney</groupId>
<artifactId>moneta</artifactId>
<version>1.4.2</version>
</dependency>
// Formatters
MonetaryAmountFormatter amountFormatter = new MonetaryAmountFormatter();
CurrencyUnitFormatter currencyFormatter = new CurrencyUnitFormatter();
// Usage
MonetaryAmount amount = Money.of(99.99, "USD");
String formatted = amountFormatter.print(amount, Locale.US); // "$99.99"