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
: nanosecondsus
: microsecondsms
: millisecondss
: secondsm
: minutesh
: hoursd
: 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 ChronoUnit
s 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
: yearsm
: monthsw
: weeksd
: 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 ChronoUnit
s 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: