How To: Convert Day Number to Month and Day

I needed to convert the day number, that is, what day it is between 1 and 365, into a month and day without using Calendar / DateTime classes and such today, so I knocked up a few simple functions that work assuming it’s not a leap year (i.e. that February has 28 days).

Here they are if they’re of any use to you:

int dateToDayNumber(int month, int day)
{
	// Catch invalid input and return early
	if (month < 1 || month > 12 || day < 1 || day > 31) return 0;
 
	if (month == 1 ) return       day;
	if (month == 2 ) return 31  + day;
	if (month == 3 ) return 59  + day;
	if (month == 4 ) return 90  + day;
	if (month == 5 ) return 120 + day;
	if (month == 6 ) return 151 + day;
	if (month == 7 ) return 181 + day;
	if (month == 8 ) return 212 + day;
	if (month == 9 ) return 243 + day;
	if (month == 10) return 273 + day;
	if (month == 11) return 304 + day;
	return 334 + day;
}
 
int dayNumberToMonth(int dayNumber)
{
	// Catch invalid input and return early
	if (dayNumber < 1 || dayNumber > 365) return 0;
 
	if (dayNumber <= 31 ) return 1;  // Jan
	if (dayNumber <= 59 ) return 2;  // Feb
	if (dayNumber <= 90 ) return 3;  // Mar
	if (dayNumber <= 120) return 4;  // Apr
	if (dayNumber <= 151) return 5;  // May
	if (dayNumber <= 181) return 6;  // Jun
	if (dayNumber <= 212) return 7;  // Jul
	if (dayNumber <= 243) return 8;  // Aug
	if (dayNumber <= 273) return 9;  // Sep
	if (dayNumber <= 304) return 10; // Oct
	if (dayNumber <= 334) return 11; // Nov
	return 12;                       // Dec
}
 
int dayNumberToDayOfMonth(int dayNumber)
{
	// Catch invalid input and return early
	if (dayNumber < 1 || dayNumber > 365) return 0;
 
	if (dayNumber <= 31 ) return dayNumber;       // Jan
	if (dayNumber <= 59 ) return dayNumber - 31;  // Feb
	if (dayNumber <= 90 ) return dayNumber - 59;  // Mar
	if (dayNumber <= 120) return dayNumber - 90;  // Apr
	if (dayNumber <= 151) return dayNumber - 120; // May
	if (dayNumber <= 181) return dayNumber - 151; // Jun
	if (dayNumber <= 212) return dayNumber - 181; // Jul
	if (dayNumber <= 243) return dayNumber - 212; // Aug
	if (dayNumber <= 273) return dayNumber - 243; // Sep
	if (dayNumber <= 304) return dayNumber - 273; // Oct
	if (dayNumber <= 334) return dayNumber - 304; // Nov
	return dayNumber - 334;                       // Dec
}

A touch cheap, but it gets the job done.

5 thoughts on “How To: Convert Day Number to Month and Day”

    1. Haha, that’s a very clever way of doing it. Thanks for pointing this out!

      Also, thanks for the implementation link – very well done.

      Update: In case the pastebin link expires or such, here is Mneumonic Carrier’s provided reference implementation:

      // --------------------------------------------------------------------------------
      // Reply to https://r3dux.org/2017/08/how-to-convert-day-number-to-month-and-day/
      // No error checking, this is just an example implementation of the date algorithms
      // from: https://alcor.concordia.ca/~gpkatch/gdate-method.html.
      // --------------------------------------------------------------------------------
       
      #include <iostream>
      #include <vector>
       
      typedef std::vector<std::string> stringvec;
      typedef unsigned long ul;
      typedef signed long sl;
       
      // --------------------------------------------------------------------------------
      // Given the year, month and day, return the day number.
      // --------------------------------------------------------------------------------
      ul CalcDayNumFromDate(ul y, ul m, ul d)
      {
        m = (m + 9) % 12;
        y -= m / 10;
        ul dn = 365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + (d - 1);
        return dn;
      }
       
       
      // --------------------------------------------------------------------------------
      // Given a day, calculate and return the date (in a string vector).
      // --------------------------------------------------------------------------------
      template<typename OUT>
      void CalcDateFromDayNum(ul day, OUT &retval)
      {
        ul y = (10000*day + 14780) / 3652425;
        sl ddd = day - (365*y + y/4 - y/100 + y/400);
        if (ddd < 0) {
          y--;
          ddd = day - (365*y + y/4 + y/100 + y/400);
        }
       
        ul mi = (100*ddd + 52) / 3060;
        ul mm = (mi + 2) % 12 + 1;
        y += (mi + 2) / 12;
        ul dd = ddd - (mi*306 + 5) / 10 + 1;
       
        retval.push_back(std::to_string(y));
        retval.push_back(std::to_string(mm));
        retval.push_back(std::to_string(dd));
      }
       
      // --------------------------------------------------------------------------------
      // Given the year and a day number, calculate and return the month and day.
      // --------------------------------------------------------------------------------
      stringvec CalcDayAndMonth(ul year, ul day)
      {
        stringvec date;
       
        ul start_day = CalcDayNumFromDate(year, 1, 1);
        CalcDateFromDayNum(start_day + day - 1, date);
       
        return date;
      }
       
      // --------------------------------------------------------------------------------
      // Program entry point.
      // --------------------------------------------------------------------------------
      int main(int argc, char **argv)
      {
        std::string year, day;
       
        std::string month[] = 
            {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", 
             "Aug", "Sep", "Oct", "Nov", "Dec"};
       
        std::cout << "Enter the year (YYYY): "; 
        getline(std::cin, year);
        std::cout << "Enter the day (between 0 and 365): "; 
        getline(std::cin, day);
       
        stringvec date = CalcDayAndMonth(std::atoi(year.c_str()), std::atoi(day.c_str()));
       
        std::cout << "Month: " << month[std::atoi(date[1].c_str()) - 1] 
                  << ", Day: " << date[2] << std::endl;
       
        return 0;
      }
      1. You’re welcome! I thought it would be better posting my code in pastebin rather than the comments section here, as I wasn’t sure if code would be formatted correctly here.

        Whenever working with dates, the first step is to convert that date into a “date number”. This isn’t actually that difficult, but most people don’t realize that a leap year is: “…every four years, except if the year is divisible by 100, but even then it’s still a leap year if it’s divisible by 400…”.

        A clear understanding of the problem domain is essential (and sometimes it requires a little effort (i.e. reading) to understand the problem domain).

        My CalcDateFromDayNum() method could be implemented in many different ways. I’m used a template function because it’s something I’m used to using for returning more complex data structures from a method (in this case, a string vector). However, a neater way to achieve this would be to pass an iterator to the string vector container instead, like so:

        // -----------------------------------------------------------------------------
        // -----------------------------------------------------------------------------
        template
        void CalcDateFromDayNum(ul day, OUT iter)
        {
          ul y = (10000*day + 14780) / 3652425;
          sl ddd = day - (365*y + y/4 - y/100 + y/400);
          if (ddd &lt; 0) {
            y--;
            ddd = day - (365*y + y/4 + y/100 + y/400);
          }
         
          ul mi = (100*ddd + 52) / 3060;
          ul mm = (mi + 2) % 12 + 1;
          y += (mi + 2) / 12;
          ul dd = ddd - (mi*306 + 5) / 10 + 1;
         
          *(iter++) = std::to_string(y);
          *(iter++) = std::to_string(mm);
          *(iter) = std::to_string(dd);
        }

        And then call it like this:

        CalcDateFromDayNum(start_day + day - 1, std::back_inserter(date));

        The advantage of doing it this way is it’s more versatile, as all containers have iterators (i.e. the previous implementation would only work with containers that support the push_back() method).

        Of course, another way would be to simply return a stringvec (string vector), just like CalcDayAndMonth does:

        // -----------------------------------------------------------------------------
        // -----------------------------------------------------------------------------
        stringvec CalcDateFromDayNum(ul day)
        {
          stringvec retval;
         
          ul y = (10000*day + 14780) / 3652425;
          sl ddd = day - (365*y + y/4 - y/100 + y/400);
          if (ddd &lt; 0) {
            y--;
            ddd = day - (365*y + y/4 + y/100 + y/400);
          }
         
          ul mi = (100*ddd + 52) / 3060;
          ul mm = (mi + 2) % 12 + 1;
          y += (mi + 2) / 12;
          ul dd = ddd - (mi*306 + 5) / 10 + 1;
         
          retval.push_back(std::to_string(y));
          retval.push_back(std::to_string(mm));
          retval.push_back(std::to_string(dd));
         
          return retval;
        }

        Of course, you could always use a properly defined struct or class to hold the date type:

        struct TDate
        {
          ui year;
          ui month;
          ui day;
        };

        In which case your CalcDateFromDayNum() becomes:

        // -----------------------------------------------------------------------------
        // -----------------------------------------------------------------------------
        TDate CalcDateFromDayNum(ul day)
        {
          TDate retval;
         
          ul y = (10000*day + 14780) / 3652425;
          sl ddd = day - (365*y + y/4 - y/100 + y/400);
          if (ddd &lt; 0) {
            y--;
            ddd = day - (365*y + y/4 + y/100 + y/400);
          }
         
          ul mi = (100*ddd + 52) / 3060;
          ul mm = (mi + 2) % 12 + 1;
          y += (mi + 2) / 12;
          ul dd = ddd - (mi*306 + 5) / 10 + 1;
         
          retval.year = y;
          retval.month = mm;
          retval.day = dd;
         
          return retval;
        }

        and CalcDayAndMonth() becomes:

        // -----------------------------------------------------------------------------
        // -----------------------------------------------------------------------------
        TDate CalcDayAndMonth(ul year, ul day)
        {
          TDate date;
         
          ul start_day = CalcDayNumFromDate(year, 1, 1);
          date = CalcDateFromDayNum(start_day + day - 1);
         
          return date;
        }

        And inside your main() you do:

        TDate date = CalcDayAndMonth( std::atoi(year.c_str()), std::atoi(day.c_str()) );
         
        std::cout << "Month: " << month[date.month - 1] << " Day: " << date.day << std::endl;

        The latter (with the TDate struct) is problem much easier to read and understand (instead of relying on a stringvec).

        Here’s the complete implementation using the TDate struct:

        https://pastebin.com/iQFaUT9T

        As an added bonus, it’s a trivial matter to find the “Day of Week”:

        std::string Day[] = {"Wed", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue"};
        ul daynum = CalcDayNumFromDate(2017, 8, 30);
        ui dayofweek = daynum % 7;
        std::cout << "Day of Week: "" << Day[dayofweek] << std::endl;

        But now I’m just rambling…

        1. Wow! A very comprehensive coverage of the matter – and orders of magnitude more thought than I’d put into it!

          Outstanding work. I salute you, sir! =D

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.