/*  Daten mit Grenzwerten zum testen der Funktionen:
 *  29.12.2014 = ist ein Montag und faellt bereits in die 1. Woche des naechsten Jahres
 *  31.12.2015 = in dem Jahr gab es eine 53. Woche
 *  31.12.2016 = Tag des Jahres wird mit 366 korrekt berechnet (Schaltjahr)
 *  01.01.2017 = ist ein Sonntag und faellt noch in die 52. Woche des Vorjahres
*/
uint16_t year = 2014;
uint8_t month = 12;
uint8_t day = 29;
const char WeekDays[][3] = {"Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"}; // Abkuerzungen der Wochentage
void setup() {
  Serial.begin(115200);
  Serial.println(F("Berechnung nach ISO 8601"));
  
  Serial.print(F("Datum: "));
  Serial.print(day);
  Serial.print(".");
  Serial.print(month);
  Serial.print(".");
  Serial.println(year);
  uint8_t wd = GetWeekday(year, month, day);        // 1 = Mo, 2 = Di, 3 = Mi, 4 = Do, 5 = Fr, 6 = Sa, 7 = So
  Serial.print(F("Wochentag: "));
  Serial.println(WeekDays[wd - 1]);
  
  uint16_t doy = GetDayOfYear(year, month, day);    // den Tag des Jahres berechnen
  Serial.print(F("Tag des Jahres: "));
  Serial.println(doy);
  
  uint8_t WeekNr = GetWeekNumber(year, month, day); // die Wochennummer berechnen
  Serial.print(F("Wochennummer: "));
  Serial.println(WeekNr);
  bool LeapYear = IsLeapYear(year);                 // berechnen, ob das Jahr ein Schaltjahr ist
  Serial.print(F("Schaltjahr: "));
  Serial.println(LeapYear ? F("Ja") : F("Nein"));
}
void loop() {
}
/***** Den Wochentag nach ISO 8601 (1 = Mo, 2 = Di, 3 = Mi, 4 = Do, 5 = Fr, 6 = Sa, 7 = So) berechnen *****/
uint8_t GetWeekday(uint16_t y, uint8_t m, uint8_t d) {
  static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
  y -= m < 3;
  uint8_t wd = (y + y / 4 - y / 100 + y / 400 + t[m - 1] + d) % 7;
  return (wd == 0 ? 7 : wd);
}
/***** Die Wochennummer nach ISO 8601 berechnen *****/
uint8_t GetWeekNumber(uint16_t y, uint8_t m, uint8_t d) {
  bool LeapYear;
  uint16_t doy = GetDayOfYear(y, m, d);  // Anzahl der Tage im Jahr ermitteln
  uint8_t wd = GetWeekday(y, m, d);      // Wochentag ermitteln
  uint8_t wnr = (doy - wd + 7) / 7;      // die Wochennummer berechnen
  switch (wnr) {
    case 0:                              // wenn die Wochennummer Null ergibt, dann liegt der Tag am Anfang des Jahres (1. Sonderfall)
      wd = GetWeekday(y - 1, 12, 31);    // den letzten Wochentag aus dem Vorjahr ermitteln
      LeapYear = IsLeapYear(y - 1);      // ermitteln, ob es sich beim Vorjahr um ein Schaltjahr handelt
      break;                             // und nach dem Switch weitermachen...
    case 52:                             // wenn die Wochennummer 52 ergibt, dann liegt der Tag am Ende des Jahres (2. Sonderfall)
      wd = GetWeekday(y, 12, 31);        // den letzten Wochentag aus diesem Jahr ermitteln
      LeapYear = IsLeapYear(y);          // ermitteln, ob es sich bei diesem Jahr um ein Schaltjahr handelt
      break;                             // und nach dem Switch weitermachen...
    default:                             // in den anderen Faellen kann die Funktion
      return wnr;                        // hier verlassen und die Wochennummer zurueckgegeben werden
  }
  if (wd < 4) {                          // wenn der 31.12. vor dem Donnerstag liegt, dann...
    wnr = 1;                             // ist das die erste Woche des Jahres
  } else {                               // anderenfalls muss ermittelt werden, ob es eine 53. Woche gibt (3. Sonderfall)
    /* wenn der letzte Wochentag auf einen Donnerstag oder,          */
    /* in einem Schaltjahr, auf einen Donnerstag oder Freitag fällt, */
    /* dann ist das die 53. Woche, ansonsten die 52. Woche.          */
    wnr = ((wd == 4) || (LeapYear && wd == 5)) ? 53 : 52;
  }
  return wnr;
}
/***** die Anzahl der Tage (Tag des Jahres) berechnen *****/
uint16_t GetDayOfYear(uint16_t y, uint8_t m, uint8_t d) {
  static const uint16_t mdays[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  return d + mdays[m - 1] + (m >= 2 && IsLeapYear(y));
}
/***** Testen, ob das Jahr ein Schaltjahr ist *****/
bool IsLeapYear(uint16_t y) {
  return  !(y % 4) && ((y % 100) || !(y % 400)); // Schaltjahrberechnung (true = Schaltjahr, false = kein Schaltjahr)
}