Spring Boot - Inject Duration/Period Property

Find out how to define Duration and Period values in the application properties of a Spring Boot application.

With Spring Boot, we usually store the environment variables in an application.properties or application.yml file. Spring has the capability to read each variable and inject it to a Java variable or field with the corresponding type. In this tutorial, we are going to focus on the Duration and Period data type which are supported out of the box.

While you can always define and inject the values as numeric, it's not a good practice in many cases. That's because you need additional code for converting the values while actually Spring already provides certain formats to use. Therefore, I am going to show you the allowed value formats for defining property values that can be automatically converted to Duration or Period variables.

Inject Duration Value

Duration is a data type in Java that stores time-based values. There are two ways to define a Duration value. You can use a simple format, define the unit using @DurationUnit annotation, or use the ISO-8601 format.

Simple Format

For a simple value, you can define the number followed by the time unit. For Duration, the supported time units are:

  • ns: nanoseconds
  • us: microseconds
  • ms: milliseconds
  • s: seconds
  • m: minutes
  • h: hours
  • d: days

Examples:

  myprop.one=5ns
  myprop.two=10s

Then, you can read the values inside a Spring bean.

  @Value("${myprop.one}")
private Duration one; // PT0.000000005S (5 nanoseconds) @Value("${myprop.two}")
private Duration two; // PT10S (10 seconds)

@DurationUnit Annotation

It's also possible to define a value without a time unit. Then, you can add @DurationUnit annotation to the injected variable. To use the annotation, you need to specify the ChronoUnit. The supported ChronoUnits for Duration data type are NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, and DAYS.

  myprop.one=10
  myprop.two=15

The properties above do not have a time unit. Look at the fields below. The first field doesn't have @DurationUnit annotation, so the unit will be set to milliseconds. The time unit of the second field will be set to hours because of the @DurationUnit annotation.

  @Value("${myprop.one}")
  private Duration one; // PT0.01S (10 milliseconds)

  @Value("${myprop.two}")
  @DurationUnit(ChronoUnit.HOURS)
  private Duration two; // PT15H (15 hours)

ISO-8601 Duration Format

Spring also supports ISO-8601 format for Duration values. The ISO-8601 format is expressed in the P(n)Y(n)M(n)DT(n)H(n)M(n)S or P(n)W format, with each (n) is a numeric value that belongs to the time unit after it.

  • P: A prefix which is referred to as "period". A value with duration format must have this prefix.
  • Y: year.
  • first M: month.
  • W: week.
  • D: day.
  • T: A character that precedes the time components.
  • H: hour.
  • second M: minute.
  • S: second.

However, Spring only allows day as the largest time unit if you use Duration type. If you try to use a larger time unit such as Y (year), you'll get IllegalArgumentException.

Examples:

  myprop.one=PT15S
  myprop.two=PT1H30M
myprop.three=P10DT5H

If you define the values correctly, you should be able to load it.

  @Value("${myprop.one}")
private Duration one; // PT15S (15 seconds) @Value("${myprop.two}")
private Duration two; // PT1H30M (1 hours and 30 minutes) @Value("${myprop.three}")
private Duration three; // PT245H (245 hours or 10 days and 5 hours)

Compared to the simple format, the advantage of using ISO-8601 format is you can use multiple time units and Spring will calculate the values.

For older Spring versions that cannot load ISO-8601 format directly, the solution is by using SpEL to call the parse method of java.time.Duration.

  @Value("#{T(java.time.Duration).parse('${myprop.four}')}")
private Duration four;

Inject Period Value

Period is a data type in Java that stores date-based values. Like Duration, you can define the values using simple format or ISO-8601 format, or use @PeriodUnit annotation to define the time unit.

Simple Format

The simple format also consists of a number followed by the time unit. For Period, the supported time units are:

  • y: years
  • m: months
  • w: weeks
  • d: days

Examples:

  myprop.one=2y
  myprop.two=3w

After defining the properties, you can try to inject the values to Period variables.

  @Value("${myprop.one}")
  private Period one; // P2Y (2 years)

  @Value("${myprop.two}")
  private Period two; // P21D (21 days or 3 weeks)

@PeriodUnit Annotation

For Period data type, the alternative is using the @PeriodUnit annotation. The annotation requires you to specify a ChronoUnit to be used as the time unit. The property value should only contain the numeric part. If the property value already has a time unit, the annotation will be ignored. The supported ChronoUnits for Period data type are DAYS, WEEKS, MONTHS, and YEARS.

  myprop.one=2
  myprop.two=3

The values in the properties above do not have a time unit. Below is the fields declaration. The first field doesn't have @PeriodUnit annotation, so the time unit will be set to days. The second field has @PeriodUnit annotation. As a result, Spring will use the unit from the annotation for the second field.

  @Value("${myprop.one}")
  private Period one; // P2D (2 days)

  @Value("${myprop.two}")
  @PeriodUnit(ChronoUnit.WEEKS)
  private Period two; // P21D (21 days or 3 weeks)

ISO-8601 Period Format

You can also use ISO-8601 format for Period. Basically, the format is similar to the ISO-8601 for Duration. The difference is the allowed time units are Y (year), W (week), M (month), and D (day). That means you cannot include the T character and the components afterwards.

Examples:

  myprop.one=P1Y2M3D
  myprop.two=P1Y2M

Then, you can check whether the values are successfully injected.

  @Value("${myprop.one}")
  private Period one; // P1Y2M3D (1 year, 2 months and 3 days)

  @Value("${myprop.two}")
  private Period two; // P1Y2M (1 year and 2 months)

Add Default Value

To handle if the value is missing on the properties file, it's possible to add a default value for both Duration and Period. Just like other data types, the default value is defined after the property name, separated by :. You can use either the simple format or the ISO-8601 format for the default value.

  @Value("${myprop.one:50s}")
private Duration one; // PT50S (50 seconds) @Value("${myprop.two:PT2M}")
private Duration two; // PT2M (2 minutes) @Value("${myprop.three:2y}")
private Period three; // P2Y (2 years) @Value("${myprop.four:4d}")
private Period four; // P4D (4 days)

Summary

Spring allows us to define Duration and Period property values by using simple format, @DurationUnit or @PeriodUnit annotation, or ISO 8601 format. In addition, you can also define a default value. As long as you follow the allowed formats, you don't need to define a custom converter. The exception is older versions of Spring Boot which require you to use SpEL to parse values with ISO 8601 format.

You can also read about: