A period in Joda-Time represents a period of time defined in terms of fields, for example, 3 years 5 months 2 days and 7 hours. This differs from a duration in that it is inexact in terms of milliseconds. A period can only be resolved to an exact number of milliseconds by specifying the instant (including chronology and time zone) it is relative to.
instant + period = instant
For example, consider a period of 1 month. If you add this period to the 1st February (ISO) then you will get the 1st March. If you add the same period to the 1st March you will get the 1st April. But the duration added (in milliseconds) in these two cases is very different.
As a second example, consider adding 1 day at the daylight savings boundary. If you use a period to do the addition then either 23 or 25 hours will be added as appropriate. If you had created a duration equal to 24 hours, then you would end up with the wrong result.
The Joda-Time library defines two types of implementation of the period concept. The first type can only store a single-field, such as days or hours, but not both. The second type can store any-field, expressing a value such as 5 months 8 days and 7 hours.
Single field Periods
The first type, single-field periods, is new to version 1.4 of Joda-Time. These classes - Years, Months, Weeks, Days, Hours, Minutes, Seconds - all follow a very similar design, and only store the single field as implied by their name. Thus a Days object can only store a number of days.
These classes can be useful when you want to write an API that must specifically take in a period measured in a particular unit. For example, a travel website where you are given the option of travelling ±1 days or ±3 days could store this information in a Days object.
These classes provide static factory methods rather than constructors. For Days these include daysBetween(startDate, endDate) to obtain the number of whole days between two dates or datetimes, and daysIn(interval) to obtain the number of whole days in an interval. In addition, there are a range of constants, such as Days.ZERO and Days.ONE. The factory method days(int) either returns a constant, or creates a new instance as appropriate.
The single-field classes include basic mathemaical operator support. For Days this includes plus(int), plus(Days), multipliedBy(int), dividedBy(int) and negated(). All operations return a new instance, as Days is immutable. The single-field classes are also comparable.
Converting between different types of period is a difficult problem. One day is not always equal to 24 hours. (It might be 23 or 25 at daylight savings time change.) However, many applications have business rules that assume a 24 hour day and so on. To support this, Days has methods named toStandardHours() and so on which convert the number of days to a number of hours assuming a 24 hour day. The word 'standard' is being specifically used in the method name to remind users of the assumption.
Any field Periods
The second type of period is the any-field period. These are implemented by the Period and MutablePeriod classes. Internally, they store a set of int fields, one for each field. The standard set of fields in a period are years, months, weeks, days, hours, minutes, seconds and millis. The PeriodType class allows this set of fields to be restricted, for example to elimate weeks. This is significant when converting a duration or interval to a period, as the calculation needs to know which period fields it should populate.
The Period class is useful when you want to write an API that can take a period that probably contains more than one field. However, to be even more general, your API could define itself to accept a ReadablePeriod, and then the calling code could pass in either a Period or a single field period like Days.
Methods exist on Period to obtain each field value. There are also methods to change a field value, such as withDays(int). They are named 'with' as they return a new Period object, due to immutability.
You cannot compare any-field periods for order, as there is no sensible comparison strategy. Instead, you need to convert the period to a Duration based on a specific date and then compare the duration.
Using Periods in Joda-Time
Within Joda-Time a period is represented by the ReadablePeriod interface. There are nine implementations of the interface provided:
- Period - An immutable implementation
- MutablePeriod - A mutable implementation
- Years - An immutable years-only implementation
- Months - An immutable months-only implementation
- Weeks - An immutable weeks-only implementation
- Days - An immutable days-only implementation
- Hours - An immutable hours-only implementation
- Minutes - An immutable minutes-only implementation
- Seconds - An immutable seconds-only implementation
The code can be used in various ways:
DateTime start = new DateTime(2004, 12, 25, 0, 0, 0, 0); DateTime end = new DateTime(2006, 1, 1, 0, 0, 0, 0); // period of 1 year and 7 days Period period = new Period(start, end); // calc will equal end DateTime calc = start.plus(period); // able to calculate whole days between two dates easily Days days = Days.daysBetween(start, end); // able to calculate whole months between two dates easily Months months = Months.monthsBetween(start, end);
Note that the interface ReadablePeriod should not be used like the collections API. The interface only contains the core subset of the operations. Instead, you should usually refer directly to the implementation classes in your APIs.
Joda-Time defines a null period as a zero length period. Thus, when a method is defined as taking a ReadablePeriod, passing null in will be the same as passing in a zero length period.