skip to content

How does Spring Boot bind strings like "30s" or "10MB" to Duration and DataSize, and how do you control default units?

level: middleimportance: should knowfreq 30%

answer

  1. Duration: s/m/h/d or PT.. ISO-8601
  2. DataSize: B/KB/MB/GB, base-1024
  3. @DurationUnit / @DataSizeUnit for bare numbers
  4. suffix always overrides default unit
  5. convert first, then validate

basics

~10 s

The binder's converters turn suffixed strings into java.time.Duration and org.springframework.util.unit.DataSize. Suffixes like s/m/h and B/KB/MB/GB are parsed automatically; use @DurationUnit and @DataSizeUnit to set the unit for bare numbers.

solid answer

~30 s

Spring Boot registers converters in its ApplicationConversionService that the @ConfigurationProperties binder uses. A String like "30s", "5m", "2h" (or ISO-8601 "PT30S") binds to java.time.Duration; "10MB", "512KB", "1GB" binds to org.springframework.util.unit.DataSize. When the value is a bare number with no suffix, the default unit applies: annotate the field with @DurationUnit(ChronoUnit.SECONDS) or @DataSizeUnit(DataUnit.MEGABYTES) (both in org.springframework.boot.convert) to say what a unit-less number means. An explicit suffix always overrides the default. A key gotcha: DataSize is binary — DataUnit.KILOBYTES is 1024 bytes, so 1MB = 1024*1024 bytes. Conversion happens before validation, so JSR-380 constraints (and Hibernate's @DurationMin/@DurationMax) run against the already-converted value.

code

java · 30 lines
java
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties(prefix = "app.cache")
@Validated
public class CacheProperties {

    // "30s" -> 30 seconds; bare "30" -> 30 seconds (default unit)
    @DurationUnit(ChronoUnit.SECONDS)
    private Duration ttl = Duration.ofSeconds(30);

    // "10MB" -> 10 * 1024 * 1024 bytes; bare "10" -> 10 MB (default unit)
    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize maxSize = DataSize.ofMegabytes(10);

    // getters/setters omitted
}

/*
app:
  cache:
    ttl: 5m        # explicit suffix overrides the SECONDS default
    max-size: 512KB # 512 * 1024 bytes
*/

go deeper

for a junior

Know Boot converts strings like 30s and 10MB to Duration/DataSize automatically.

for a middle

Explain @DurationUnit/@DataSizeUnit for default units, suffix precedence, and the base-1024 DataSize gotcha.

for a senior

Locate the converters in ApplicationConversionService (org.springframework.boot.convert) and note convert-then-validate ordering plus Hibernate @DurationMin/@DurationMax.

for a principal

Prefer semantic value types over raw longs across a config surface and standardize unit conventions to avoid ambiguity.

## The types - **`java.time.Duration`** — a time span. Boot binds human-friendly strings to it. - **`org.springframework.util.unit.DataSize`** — Boot's own value type for byte sizes (with `DataUnit`: B, KB, MB, GB, TB). ## How conversion is wired The `@ConfigurationProperties` **binder** uses Boot's `ApplicationConversionService`, which registers dedicated converters — e.g. a String→Duration converter and a String→DataSize converter (`org.springframework.boot.convert` package). So no manual parsing is needed; you just declare the target type on the property. ## Duration formats accepted - **Suffixed simple format**: `ns` (nanos), `us` (micros), `ms` (millis), `s` (seconds), `m` (minutes), `h` (hours), `d` (days) → `"30s"`, `"5m"`, `"2h"`, `"1d"`. - **ISO-8601**: `"PT30S"`, `"PT5M"`, `"P1D"`. - **Bare number**: `"30"` — interpreted using the **default unit**. ### Setting the Duration default unit `@DurationUnit(ChronoUnit.SECONDS)` (from `org.springframework.boot.convert`) says a unit-less number means seconds. If the string carries a suffix, the suffix wins and the annotation is ignored for that value. ## DataSize formats accepted - **Suffixed**: `"10MB"`, `"512KB"`, `"1GB"`, `"2TB"`, `"1024B"`. - **Bare number**: `"1048576"` — interpreted using the **default unit** (bytes by default). ### Setting the DataSize default unit `@DataSizeUnit(DataUnit.MEGABYTES)` says a unit-less number means megabytes. ### Binary gotcha `DataUnit` is **base-1024**: `KILOBYTES = 1024`, `MEGABYTES = 1024^2`, etc. So `"1MB"` = 1,048,576 bytes, not 1,000,000. Don't assume decimal (SI) units. ## Also: Period Analogously, `java.time.Period` is supported with `@PeriodUnit(ChronoUnit.DAYS)` and suffixes `y/m/w/d`. ## Order: convert then validate Binding first **converts** the string to `Duration`/`DataSize`, then any JSR-380 constraints run on the resulting object. Note that `@Min`/`@Max` apply to numeric types, not `Duration`; to bound a `Duration` you use **Hibernate Validator's** `@DurationMin`/`@DurationMax` (Hibernate-specific, not core JSR-380). A malformed string (e.g. `"10 megabytes"`) fails at the *conversion* stage as a bind error, distinct from a constraint violation, but both abort startup. ## When to use Use `Duration`/`DataSize` types (instead of `long millis` / `long bytes`) so config reads naturally (`timeout: 30s`, `max-file-size: 10MB`) and is self-documenting, while the binder handles parsing and the default-unit annotations remove ambiguity.

  • Is DataSize decimal or binary — how many bytes is 1MB?
    Binary. DataUnit is base-1024, so 1MB = 1024*1024 = 1,048,576 bytes, not 1,000,000.
  • What does @DurationUnit(SECONDS) do when the value is "5m"?
    Nothing — the explicit m suffix wins, giving 5 minutes. The default unit only applies to unit-less numeric strings.

saying these in an interview costs you the question

  • Assuming DataSize uses decimal 1000-based units instead of 1024
  • Thinking @Min/@Max can bound a Duration (need Hibernate @DurationMin/@DurationMax)
  • Believing you must manually parse timeout/size strings

context