This is from Chapter 12 of Beginning C with Arduino by Dr. Jack Purdum.


It shows the implementation of an Arduino library that calculates both Leap Years and the date Easter occurs in any year.

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"
  • 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 12-4. A program to test the Dates library routine */
#include <Dates.h>

Dates myDates;

void setup() {
  int i;
  Serial.begin(9600);
  
  for (i = 2000; i < 2016; i++) {
    Serial.print(i);
    Serial.print(" is ");
    if (myDates.IsLeapYear(i) == 0)
      Serial.print("not ");
    Serial.print("a leap year and Easter is on ");
    myDates.myEaster.year = i;
    myDates.GetEaster(&myDates);
    Serial.print(myDates.myEaster.easterStr);
    Serial.print("  ");
    Serial.print(myDates.myEaster.month);
    Serial.print("  ");
    Serial.print(myDates.myEaster.day);
    Serial.print(" ");
    Serial.println(myDates.myEaster.year);
  }
}
void loop() {}




Dates.h:

/* Listing 12-2. The Dates.h header file

  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);
  };
#endif     // Don't forget this!



Dates.cpp:

/* Listing 12-3. The Dates.cpp source code */
#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...
}



Expected output of the Dates Library Test:


2000 is a leap year and Easter is on 04/23/2000  4  23 2000
2001 is not a leap year and Easter is on 04/15/2001  4  15 2001
2002 is not a leap year and Easter is on 03/31/2002  3  31 2002
2003 is not a leap year and Easter is on 04/20/2003  4  20 2003
2004 is a leap year and Easter is on 04/11/2004  4  11 2004
2005 is not a leap year and Easter is on 03/27/2005  3  27 2005
2006 is not a leap year and Easter is on 04/16/2006  4  16 2006
2007 is not a leap year and Easter is on 04/08/2007  4  8 2007
2008 is a leap year and Easter is on 03/23/2008  3  23 2008
2009 is not a leap year and Easter is on 04/12/2009  4  12 2009
2010 is not a leap year and Easter is on 04/04/2010  4  4 2010
2011 is not a leap year and Easter is on 04/24/2011  4  24 2011
2012 is a leap year and Easter is on 04/08/2012  4  8 2012
2013 is not a leap year and Easter is on 03/31/2013  3  31 2013
2014 is not a leap year and Easter is on 04/20/2014  4  20 2014
2015 is not a leap year and Easter is on 04/05/2015  4  5 2015