This is from Chapter 14 of Beginning C for Arduino by Dr. Jack Purdum.


It adds to the implementation of an Arduino library shown in Chapter 12 that calculates both Leap Years and the date Easter occurs in any year, to add Julian date capability.

There are 3 source files here:
  1. The Arduino Sketch to test the "Dates" library
  2. The Dates.h header file
  3. The Dates.cpp Library source file

These files are shown below. To run and test the Dates library you must:
  • Create a folder within the Arduino IDE 'libraries' folder named "Dates" NOTE: You may want to save the existing "Dates" library folder if you want to be able to get back to the Chapter 12 version.
  • Copy and paste the .h and .cpp listings below into text files, naming them with their .h and .cpp extensions
  • Copy those two files inside the 'Dates' folder
  • Close and restart the Arduino IDE
  • Copy and paste the test sketch below into a new Arduino IDE window.
  • Verify and upload the sketch. The expected output is shown at the end of this page.

DatesTestSketch:

/* Listing 14-5. Using the Dates Library */
#include "Dates.h"

Dates myDates;          // This calls the default constructor 

void setup() {
  int i;
  int total;

  Serial.begin(9600);
  //Dates myDates;        // Default constructor, no initializer 
  //Dates myDates(2015);  // Constructor with initializer

  for (i = 2000; i < 2017; i++) {
    Serial.print(i);
    Serial.print(" is ");
    if (myDates.IsLeapYear(i) == 0) {
      Serial.print("not ");
    }
    Serial.print("a leap year, Easter is on: ");
    myDates.myEaster.year = i;
    myDates.GetEaster(&myDates);
    total = myDates.julian(3, 5, i);
    Serial.print(myDates.myEaster.easterStr);
    Serial.print("  jullian days to May 3: ");
    Serial.println(total);
  }
}
void loop() {}




Dates.h:

/* Listing 12-2. The Dates.h header file (Modified in Chapter 14 to add Julian )

  Dates.h - Library for finding is a year is a leap year
       and the date for Easter for a given year.
  Created and modified by: Dr. Jack Purdum, Dec. 25, 2014
  Released into the public domain.
*/
#ifndef Dates_h          // If we haven't read this file before...
  #define Dates_h        // ...read it now. This prevents double-including

  #include "Arduino.h"   // Not needed for our code, but often included
  
  class Dates
  {
    public:
      #define ASCIIZERO 48     // character for 0 in ASCII

    struct easter {
      int month;
      int day;
      int year;
      int leap;
      char easterStr[11];
    };
    struct easter myEaster;
                                // Function prototypes:
    int IsLeapYear(int year);
    void GetEaster(Dates *myEaster);
    char * DayOfTheWeek(int day, int month, int year);
    char *GetDayOfWeek();
    int julian(int day, int month, int year);  // New method
        
  };
#endif     // Don't forget this!



Dates.cpp:

/* Listing 14-3. The Dates.cpp source code Modified to add Julian */
#include "Arduino.h"
#include "Dates.h"

/*****
  Purpose: Determine if a given year is a leap year. Algorithm 
           taken from C Programmer's Toolkit, Jack Purdum, Que 
           Corp., 1993, p.258.

  Parameters:
    int year             The year to test 

  Return value:
    int          1 if the year is a leap year, 0 otherwise
*****/
int Dates::IsLeapYear(int year)
{
  if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
    return 1;  // It is a leap year
  } else {
    return 0;  // not a leap year
  }
}

/*****
  Purpose: Determine the date for Easter for a given year. 
           Algorithm taken from Beginning Object Oriented            
           Programming with C#, Jack Purdum, Wrox, 2012. 

  Parameters:
    struct easter *myData    Pointer to an easter structure

  Return value:
    void                 
    
  CAUTION: This function assumes that the year member of the structure holds the
           year being tested upon entry.       
*****/

void Dates::GetEaster(Dates *myData){ // This is line 44
  int offset;
  int leap;
  int day;
  int temp1, temp2, total;

  myData->myEaster.easterStr[0] = '0';    // Always a '0'
  myData->myEaster.easterStr[2] = '/';    // Always a '/'
  myData->myEaster.easterStr[3] = '0';    // Assume day is less than 10
  myData->myEaster.easterStr[10] = '\0';  // null char for End of string
  
  offset = myData->myEaster.year % 19;
  leap = myData->myEaster.year % 4;
  day = myData->myEaster.year % 7;
  temp1 = (19 * offset + 24) % 30;
  temp2 = (2 * leap + 4 * day + 6 * temp1 + 5) % 7;
  total = (22 + temp1 + temp2);
  if (total > 31) {
    myData->myEaster.easterStr[1] = '4';    // Must be in April
    myData->myEaster.month = 4;             // Save the month
    day = total - 31;
  } else {
    myData->myEaster.easterStr[1] = '3';    // Must be in March
    myData->myEaster.month = 3;             // Save the month
    day = total;
  }
  myData->myEaster.day = day;              // Save the day
  
  if (day < 10) {                 // One day char or two?
    myData->myEaster.easterStr[4] = (char) (day + ASCIIZERO);
  } else {
    itoa(day, myData->myEaster.easterStr + 3, 10);  // Convert day to ASCII and...
  }
  myData->myEaster.easterStr[5] = '/';     // Always a '/' and overwrites null from itoa()
  itoa(myData->myEaster.year, myData->myEaster.easterStr + 6, 10);    // Convert year to ASCII...
}

/***** Listing 14-3. The julian() Method. (Added in Chapter 14)

  Purpose: Determine the numbers of days between the given date
           and Jan 1 of the same year. Algorithm taken from C 
           Programmer's Toolkit, Jack Purdum, Que Corp., 1993, 
           p.257.

  Parameters:
    int day         The day to test
    int month       The month to test
    int year        The year to test (e.g., 2015)

  Return value:
    int             The number of days, including the one given          
*****/
int Dates::julian(int day, int month, int year) 
{
  static int runsum[] = {0, 31, 59, 90, 120, 151, 181,
                         212, 243, 273, 304, 334, 365};
  int total;
  
  total = runsum[month - 1] + day;
  if (month > 2) {
    total += IsLeapYear(year);   // Adjust for leap year
  }
  return total;
}



Expected output of the Dates Library Test:


2000 is a leap year, Easter is on: 04/23/2000  jullian days to May 3: 124
2001 is not a leap year, Easter is on: 04/15/2001  jullian days to May 3: 123
2002 is not a leap year, Easter is on: 03/31/2002  jullian days to May 3: 123
2003 is not a leap year, Easter is on: 04/20/2003  jullian days to May 3: 123
2004 is a leap year, Easter is on: 04/11/2004  jullian days to May 3: 124
2005 is not a leap year, Easter is on: 03/27/2005  jullian days to May 3: 123
2006 is not a leap year, Easter is on: 04/16/2006  jullian days to May 3: 123
2007 is not a leap year, Easter is on: 04/08/2007  jullian days to May 3: 123
2008 is a leap year, Easter is on: 03/23/2008  jullian days to May 3: 124
2009 is not a leap year, Easter is on: 04/12/2009  jullian days to May 3: 123
2010 is not a leap year, Easter is on: 04/04/2010  jullian days to May 3: 123
2011 is not a leap year, Easter is on: 04/24/2011  jullian days to May 3: 123
2012 is a leap year, Easter is on: 04/08/2012  jullian days to May 3: 124
2013 is not a leap year, Easter is on: 03/31/2013  jullian days to May 3: 123
2014 is not a leap year, Easter is on: 04/20/2014  jullian days to May 3: 123
2015 is not a leap year, Easter is on: 04/05/2015  jullian days to May 3: 123
2016 is a leap year, Easter is on: 03/27/2016  jullian days to May 3: 124