QuantLib: Periods, Time Units and Frequencies

In order to handle date intervals, QuantLib defines the Period class. This class stores an arbitrary time period corresponding to a full number of days. Periods can be constructed using an integer number and a time unit, or using a frequency. Available time units are Days, Weeks, Months, and Years. Thus, a time period can be constructed by

Period p(5, Weeks);

Alternatively one can multiply the time unit with the integer to obtain the period;

Period p = 5*Weeks;

Available frequencies are Daily, Weekly, Biweekly, EveryFourthWeek, Monthly, Bimonthly, Quarterly, EveryFourthMonth, Semiannual, Annual, Once, and NoFrequency. All these identifiers can be used to construct the according periods, but note that the constructor has been made explicit so that no implicit type conversion from Frequency to Period is attempted.

Period p2(Bimonthly);

There are a few accessor methods that allow reading out values stored in the period.

Integer length() const { return length_; }
TimeUnit units() const { return units_; }
Frequency frequency() const;

The method length() returns an integer representing the number of time units of the period, while units() returns the TimeUnits. The frequency method returns the frequency of the time period. Even if the period was not constructed with a frequency, the method will attempt to identify if any of the predefined frequencies matches the time period.

Period p = 4*Months;
std::cout << "Period is " << p.length() << " " 
          << p.units() << "." << std::endl;
std::cout << "Frequency is " << p.frequency() << "." << std::endl;

The output of this code is

Period is 4 Months.
Frequency is Every-Fourth-Month.

Finally Period defines the operation normalize() which attempts to increase the time units if possible. This conversion of the time units works only in two cases. If the units are Days and the length is a multiple of 7, then the units are converted to Weeks. If, on the other hand, the units are Months and the length is a multiple of 12, then the units are converted to Years.

Period p=21*Days;
std::cout << "21 days is " << p << "." << std::endl;

produces the output

21 days is 3W.

In addition to the above member functions, there are a few global functions which provide additional information of a time period. The four functions

Real days(const &Period p);
Real weeks(const &Period p);
Real months(const &Period p);
Real years(const &Period p);

all return a floating point number which represents the period in the units denoted by the function name. These functions work, even if the period is not a multiple of that time unit, but conversion into that unit has to be possible. This means that you can query the number of weeks, when the period is given in units of days, and you can query the number of years, when the period is given in units of months. However conversion between days and months is not possible because there is no defined conversion factor.

Periods Calculations

Periods can be added and subtracted to each other and to dates. When a Period is added to another Period the result will also be a Period object. If the two periods have different time units then conversion to the smaller unit is attempted. Note that this is not always well defined. Periods based on year units and month units can be added freely. Also periods based on week units and day units can be added. But it is not possible to add a month based period to a week based period because it is not well defined how many weeks are in a month. If this is attempted, the code will fail. The following table shows which conversions are possible and which are not.

Year Month Week Day
Year OK OK
Month OK OK
Week OK OK

Consider the example

Period p5w  =5*Weeks;
Period p10d =10*Days;
Period p3m =3*Months;
Period p2y =2*Years;

try {
std::cout << "5 weeks and 10 days is " << p5w+p10d << std::endl;
std::cout << "2 years and 3 months is " << p2y+p3y << std::endl;
std::cout << "3 months and 10 days is " << p3m+p10d << std::endl;
catch(Error e) {

std::cout << std::endl << "Error:  " << e.what() << std::endl;

This code results in the following output.

5 weeks and 10 days is 6W3D
2 years and 3 months is 2Y3M

period.cpp:167: In function `QuantLib::Period&
QuantLib::Period::operator+=(const QuantLib::Period&)':
impossible addition between 3M and 1W3D

Time periods can also be multiplied and divided by integer factors. Multiplication is straightforward. In order for the division to work, the period has to be divisible withot remainder. For this the period can be converted to a smaller unit. Like before with the addition and subtraction, conversion is only possible from Years to Months and from Weeks to Days.

Finally, time periods can be compared using the less than or more than operators. As with calculations, a comparison will convert the time units and attempt a comparison. Conversion between Years and Months and between Weeks and Days is straightforward. But comparisons can go a bit further. For example, a period of 20 days is certainly less than one month, even if one doesn’t know exactly how many days the month would have. If such an ordering is possible, the comparison will produce the expected result. Otherwise it will fail.

Period p1 = 20*Days;
Period p2 = 1*Months;
if (p1 < p2)
  std::cout << p1 << " is shorter than " << p2 << "." << std::endl;
  std::cout << p1 << " is longer than " << p2 << "." << std::endl;

This will output

2W6D is shorter than 1M.

Dates and Periods

Time periods naturally interact with dates in the sense that they can be added to and subtracted from each other.

Date d(3, April, 1976);
Period p3m(3,Months);
std::cout << "Three months after " << d << " is "
          << d + p3m << "." << std::endl;
std::cout << "Two weeks before " << d << " is "
          << d - 2*Weeks << "." << std::endl;

The output of this is

Three months after April 3rd, 1976 is July 3rd, 1976.
Two weeks before April 3rd, 1976 is March 20th, 1976.

But a WARNING is in order. Adding a single month to a date multiple times can result in a different date than directly adding a period of multiple months. This is because, when a month is added, the date will readjust itself to lie within the month. To make this more clear, look at the following example

Date d1(31,Jan,2012);
Date d2 = d1 + 2*Months;
Date d3 = d1 + 1*Months;
Date d4 = d3 + 1*Months;
std::cout << "Two months after " << d1 << " is "
          << d2 << "." << std::endl;
std::cout << "One month after " << d1 << " is "
          << d3 << "." << std::endl;
std::cout << "One month after " << d3 << " is "
          << d4 << "." << std::endl;

The output of this follows.

Two months after January 31st, 2012 is March 31st, 2012.
One month after January 31st, 2012 is February 29th, 2012.
One month after February 29th, 2012 is March 29th, 2012.

By adding a month onto 31 Jan 2012, the day is adjusted to the 29th, to fit within the month of February. Adding another month then results in 29 Mar 2012 which is not two months after the original date.