Computing day of year in C++

I have been recently asked on my post on the date library if the library has a function for computing the day of the year. It actually does not, although it is fairly simple to compute it.

UPDATE: Howard Hinnant has shown in a comment below how to write a day_of_year() function using the date libray.

Let’s look at the days of the year.

Day Day of year
January 1 1
January 2 2
January 31 31
February 1 32
February 28 59

Here is where things complicate a bit, because during leap years February has 29 days. So we actually need to have two counts of days.

Day Day of non-leap year Day of leap year
January 1 1 1
January 2 2 2
January 31 31 31
February 1 32 32
February 28 59 59
February 29 N/A 60
March 1 60 61
December 31 365 366

It is fairly simple to compute the day of the year based on the day of the month if we knew the day of the year of each first day of the month. That can be also put in a table.

Day of month Day of non-leap year Day of leap year
January 1 1 1
February 1 32 32
March 1 60 61
April 1 91 92
May 1 121 122
June 1 152 153
July 1 182 183
August 1 213 214
September 1 244 245
October 1 274 275
November 1 305 306
December 1 335 336

So we can compute the day of the year as:

We can simplify that a bit by subtracking 1 from the day of the year values in the table above, such that January 1st is the day 0, February 1st is day 31, etc.

The following code sample shows how this can be written in C++:

And how it can be used:

This day_of_year() function can be used with the date library too. I’ll just add one more utility function that takes a date::year_month_day value and returns the day of the year.

And we want to know what day of the year today is then we can do that too:

The day_of_year() function is very simple and does not do any argument checks. That makes it possible to compute dates such as 2017.08.55 or 2017.55.100. Obviously, these not only that do not make sense, but indexing the days_to_month array beyond its bounds is undefined behavior. That means that in practice you should write a function that validates the arguments and throws an exception upon error. However, in this case, the day_of_year() can not be constexpr anymore.

This would throw an exception on dates like 2017.13.1 or 2017.1.50, but would not do so for 2017.2.30 or 2017.11.31 that are also invalid dates. That can be further corrected by verifying that the day of the month does not exceed the number of days that month can have in the given year.

, , , Hits for this post: 3819 .

C++11 added a date and time utility library called chrono, available in namespace std::chrono and header <chrono>. The problem with it is that the library is a general purpose one and therefore lacks many useful features, such as working with dates, weeks, calendars, timezones and other related features. Fortunately, a rich date and time library based on chrono has been created by Howard Hinnant and is available on github. The library is called date and is actually a collection of several small libraries:

  • date: the main library, available in header date.h, defines new date and time classes and operations with them. All the other libraries are based on this one.
  • timezones: a library for timezones, available in files tz.h/tz.cpp, based on the IANA timezone database
  • chrono_io: a library for streaming durations, available in header chrono_io.h
  • iso_week: a library that implements the ISO week calendar, available in header iso_week.h
  • julian and islamic: libraries that implement the Julian and Islamic calendars, available in headers julian.h and islamic.h

You can find all the necessary documentation on github. Here are several links:

In this article we will look at some examples for working with dates and ISO weeks. This library introduces many new types to handle various date and time representations. Among these we will look at:

  • sys_days: A count of days since std::system_clock‘s epoch. This is a time_point with a resolution of a day, and is implicitly convertible to std::system_clock::time_point, that has a much smaller resolution (millisecond or nanosecond), but not the other way around. To go the other way you must use floor().
  • year_month_day: A type that holds a day with fields for year, month (1 to 12) and day (1 to 31).
  • year_month_weekday: A type that holds a day with fields for year, month (1 to 12), a day of the week (0 to 6), and an index in the range [1, 5] that indicates the number of the week in the month.
  • year_weeknum_weekday: A type that hold a year, a weeknum (1 to 53) and a weekday (0 to 6). This can convert implicitly to and from a sys_days.

For using the library we need the following:

  • include header date.h and namespaces date and date::literals
  • for iso weeks we also need header iso_week.h and namespaces iso_week and iso_week::literals
  • NOTICE: The namespaces date::literals and iso_week::literals define types and literal operators with the same name and therefore can lead to name collisions; therefore you should only include them in the scope where you need them.

We will use the following lambda expression to print various dates to the console:

NOTICE: All the ‘today’ and related dates below are based on 2016-10-31.

Let us look at some examples:

  • create sys_days objects (including literals):

  • create year_month_day objects (including literals):

  • creating year_month_weekday literals and converting to year_month_day

  • create year_month_day values for today, yesterday and tomorrow

  • create year_month_day values for first and last day of the month

    Update: The following, as indicated by Howard Hinnant in the comments, can also be used:

  • create iso_week literals

  • get the iso week number for today

We will look at more utilities and examples in another post.

, , , , , , Hits for this post: 11572 .