How best to declare this array?

How should I best declare the array for DIGIT_FONT?
Even though the elements are chars, is it all right to input them as I would input integers, like you see here?
Am I using const char correctly?
Is there something I could do to so that the array will not chew up RAM? I know it is only forty bytes, but still, I would like to try to conserve RAM.

#include "Wire.h"
#include <LiquidCrystal.h>

// Make sure your pin numbers match these!
//                RS  EN  D4  D5  D6  D7
LiquidCrystal lcd( 7,  6,  5,  4,  3,  2);
const byte DST_SWITCH_PIN = 8;
const byte SPEAKER_PIN = 9;
const byte PLUS_BUTTON_PIN = 11;
const byte SET_BUTTON_PIN = 12;

// some useful constants (names of modes)
const byte SET_YEAR   = 6;
const byte SET_MONTH  = 5;
const byte SET_DATE   = 4;
const byte SET_HOUR   = 3;
const byte SET_MINUTE = 2;
const byte SET_SECOND = 1;
const byte KEEP_TIME  = 0;

// another useful constant
const byte MINIMUM_YEAR = 22; // because I am writing this in 2022

// a font for digits
const char DIGIT_FONT[10][4] = {
  {  4,  2,  6,  2 },
  { 32,  2, 32,  2 },
  {  5,  2,  6,  1 },
  {  5,  2,  3,  2 },
  {  6,  2, 32,  2 },
  {  7,  1,  3,  2 },
  {  7,  1,  6,  2 },
  {  4,  2, 32,  2 },
  {  7,  2,  6,  2 },
  {  7,  2,  3,  2 }
};

// variables for the current date and time
byte yy=0, mo=1, dd=0, wd=6;
byte hh=0, mi=0, ss=0;
byte hhTwelve = 12;
byte wn=52;

// other helpful variables
bool gotTheTime = false;
bool timeIsGarbage = false;
byte old_ss = 99, halfSec = 198, old_halfSec = 198;
unsigned long microsNow = 0UL;
unsigned long microsAtLastSecond = 0UL;
bool dstOn = false;
bool plusPressed = false, old_plusPressed = false;
bool setPressed = false, old_setPressed = false;
byte clockMode = KEEP_TIME;
bool useBigDigits = false;

// a buffer for text to be displayed
char buf[20] = "";

void setup() {
  pinMode(DST_SWITCH_PIN, INPUT_PULLUP);
  pinMode(SPEAKER_PIN, OUTPUT);
  pinMode(PLUS_BUTTON_PIN, INPUT_PULLUP);
  pinMode(SET_BUTTON_PIN, INPUT_PULLUP);
  
  Wire.begin();
  lcd.begin(16, 2);
  
  // BEGINNING of code for setting the date and time
  
  // If you wish to set the date and time,
  // uncomment the following:
  
  /*
  // code to precisely set the external real-time clock
  Wire.beginTransmission(0x68); // address DS3231
  Wire.write(0x00); // select register
  // NOTE: before you run this code, you *must*
  // change the following numbers to the correct time!
  // (plus a few seconds to allow for compilation, etc.)
  Wire.write(numberToBcd( 0)); // seconds
  Wire.write(numberToBcd(21)); // minutes
  Wire.write(numberToBcd( 1)); // hours (use 24-hour format)
  Wire.write(numberToBcd( 6)); // day of week (I use Mon=1 .. Sun=7)
  Wire.write(numberToBcd( 9)); // day of month
  Wire.write(numberToBcd( 4)); // month
  Wire.write(numberToBcd(22)); // year (use only two digits)
  Wire.endTransmission();
  */
  
  // END of code for setting the date and time

  /*
  // define special characters for single cell numerals 10 through 12
  byte singleCellTen[]    = { 18, 21, 21, 21, 21, 21, 18,  0 };
  byte singleCellEleven[] = {  9, 27,  9,  9,  9,  9,  9,  0 };
  byte singleCellTwelve[] = { 22, 21, 17, 18, 20, 20, 23,  0 };
  lcd.createChar(10, singleCellTen);
  lcd.createChar(11, singleCellEleven);
  lcd.createChar(12, singleCellTwelve);
  */
  
  // define special "box" characters for drawing large digits
  byte boxDot[]          = {  0,  0,  0,  0,  0,  0, 24, 24 };
  byte boxLeftOnly[]     = { 24, 24, 24, 24, 24, 24, 24, 24 };
  byte boxBottomOnly[]   = {  0,  0,  0,  0,  0,  0, 31, 31 };
  byte boxTopAndLeft[]   = { 31, 31, 24, 24, 24, 24, 24, 24 };
  byte boxTopAndBottom[] = { 31, 31,  0,  0,  0,  0, 31, 31 };
  byte boxLShape[]       = { 24, 24, 24, 24, 24, 24, 31, 31 };
  byte boxCShape[]       = { 31, 31, 24, 24, 24, 24, 31, 31 };
  lcd.createChar(1, boxDot);
  lcd.createChar(2, boxLeftOnly);
  lcd.createChar(3, boxBottomOnly);
  lcd.createChar(4, boxTopAndLeft);
  lcd.createChar(5, boxTopAndBottom);
  lcd.createChar(6, boxLShape);
  lcd.createChar(7, boxCShape);
  
  // play a short tone (for testing the speaker)
  tone(SPEAKER_PIN, 1000, 500);
  
  // display a demo pattern (for testing the display)
  lcd.setCursor(0, 0); // go to beginning of top line
  lcd.print(F("  Display test  "));
  lcd.setCursor(0, 1); // go to beginning of bottom line
  lcd.print(F("0123456789 (^_^)"));

  for (int i = 5; i >= 1; i--) { // countdown from 5 to 1
    lcd.setCursor(0, 0); // go to beginning of top line
    lcd.print((char)('0' + i)); // print the digit
    lcd.setCursor(15, 0); // go to end of top line
    lcd.print((char)('0' + i)); // print the digit again
    delay(998);
  }
}

void loop() {
  if (clockMode == KEEP_TIME) {
    // normal timekeeping mode
    
    // first, we (try to) read the time from the RTC
    // send request to receive data starting at register 0
    Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
    Wire.write((byte)0); // start at register 0
    Wire.endTransmission();
    Wire.requestFrom(0x68, 7); // request seven bytes
   
    gotTheTime = false;
    while(Wire.available())
    { 
      ss = bcdToNumber(Wire.read()); // get seconds
      mi = bcdToNumber(Wire.read()); // get minutes
      hh = bcdToNumber(Wire.read()); // get hours
      Wire.read(); // discard the day of the week (we will calculate it ourself)
      dd = bcdToNumber(Wire.read()); // get day of month
      mo = bcdToNumber(Wire.read()); // get month
      yy = bcdToNumber(Wire.read()); // get year
      gotTheTime = true;
    }

    microsNow = micros();
    
    // detect garbage dates and times
    if ((yy < MINIMUM_YEAR) || (yy > 99)) timeIsGarbage = true;
    if ((mo < 1) || (mo > 12)) timeIsGarbage = true;
    if ((dd < 1) || (dd > daysInMonth(yy,mo))) timeIsGarbage = true;
    if (hh > 23) timeIsGarbage = true;
    if (mi > 59) timeIsGarbage = true;
    if (ss > 59) timeIsGarbage = true;
    
    // read the Daylight Saving Time on/off switch
    // NOTE: because we are using INPUT_PULLUP, LOW means on, and HIGH means off
    dstOn = (digitalRead(DST_SWITCH_PIN) == LOW);
    
    // adjust for Daylight Saving Time if applicable
    if (dstOn) {
      hh++;
      if (hh >= 24) {
        hh -= 24;
        dd++;
        if (dd > daysInMonth(yy, mo)) {
          dd = 1;
          mo++;
          if (mo > 12) {
            mo = 1;
            yy++;
          }
        }
      }
    }
    
    // try to figure out which half-second we are in
    // (this is important to making the striking work properly)
    if (ss != old_ss) microsAtLastSecond = microsNow;
    halfSec = ss * 2;
    if ((microsNow - microsAtLastSecond) >= 500000UL) halfSec++;
    
    // calculate day of the week
    wd = ymdToWeekday(yy, mo, dd);

    // calculate week number  
    wn = ymdToWeekNumber(yy, mo, dd);
    
    // convert hour to 12-hour format
    hhTwelve = hh;
    if (hhTwelve > 12) {
      hhTwelve -= 12;
    }
    if (hhTwelve == 0) {
      hhTwelve = 12;
    }
    
    if (gotTheTime && (!timeIsGarbage)) {
      // only if we have successfully read the time
      // (and it is not a garbage time)
      // do we then attempt to indicate the time
      
      if (halfSec != old_halfSec) { // do this only once every half-second
        // see if it is time for the clock to strike
        if (mi == 0) {  // strike on the hour, i.e. when minutes are 0
          if (halfSec < 26) {
            // play the Westminster Chimes
            switch (halfSec) {
              case 0:  tone(SPEAKER_PIN, 330, 420); break;
              case 1:  tone(SPEAKER_PIN, 415, 420); break;
              case 2:  tone(SPEAKER_PIN, 370, 420); break;
              case 3:  tone(SPEAKER_PIN, 247, 735); break;
              case 6:  tone(SPEAKER_PIN, 330, 420); break;
              case 7:  tone(SPEAKER_PIN, 370, 420); break;
              case 8:  tone(SPEAKER_PIN, 415, 420); break;
              case 9:  tone(SPEAKER_PIN, 330, 735); break;
              case 12: tone(SPEAKER_PIN, 415, 420); break;
              case 13: tone(SPEAKER_PIN, 330, 420); break;
              case 14: tone(SPEAKER_PIN, 370, 420); break;
              case 15: tone(SPEAKER_PIN, 247, 735); break;
              case 18: tone(SPEAKER_PIN, 247, 420); break;
              case 19: tone(SPEAKER_PIN, 370, 420); break;
              case 20: tone(SPEAKER_PIN, 415, 420); break;
              case 21: tone(SPEAKER_PIN, 330, 735); break;
              default: break;
            }
          }
          else if ((halfSec < (26 + 3 * hhTwelve)) && ((halfSec % 3) == 2)) {
            // bong the hours
            tone(SPEAKER_PIN, 415, 750);
          }
        }
      }
      
      if (useBigDigits) {
        // update the display to show the current time with big digits
        
        // build a string of characters for the top row of the display
        buf[0]  = ' ';
        buf[1]  = DIGIT_FONT[hh/10][0];
        buf[2]  = DIGIT_FONT[hh/10][1];
        buf[3]  = DIGIT_FONT[hh%10][0];
        buf[4]  = DIGIT_FONT[hh%10][1];
        buf[5]  = 1; // dot for colon
        buf[6]  = DIGIT_FONT[mi/10][0];
        buf[7]  = DIGIT_FONT[mi/10][1];
        buf[8]  = DIGIT_FONT[mi%10][0];
        buf[9]  = DIGIT_FONT[mi%10][1];
        buf[10] = 1; // dot for colon
        buf[11] = DIGIT_FONT[ss/10][0];
        buf[12] = DIGIT_FONT[ss/10][1];
        buf[13] = DIGIT_FONT[ss%10][0];
        buf[14] = DIGIT_FONT[ss%10][1];
        buf[15] = ' ';
        lcd.setCursor(0, 0); // move to beginning of top row 
        lcd.print(buf); // print the characters to the display
        
        // build a string of characters for the bottom row of the display
        buf[0]  = ' ';
        buf[1]  = DIGIT_FONT[hh/10][2];
        buf[2]  = DIGIT_FONT[hh/10][3];
        buf[3]  = DIGIT_FONT[hh%10][2];
        buf[4]  = DIGIT_FONT[hh%10][3];
        buf[5]  = 1; // dot for colon
        buf[6]  = DIGIT_FONT[mi/10][2];
        buf[7]  = DIGIT_FONT[mi/10][3];
        buf[8]  = DIGIT_FONT[mi%10][2];
        buf[9]  = DIGIT_FONT[mi%10][3];
        buf[10] = 1; // dot for colon
        buf[11] = DIGIT_FONT[ss/10][2];
        buf[12] = DIGIT_FONT[ss/10][3];
        buf[13] = DIGIT_FONT[ss%10][2];
        buf[14] = DIGIT_FONT[ss%10][3];
        buf[15] = ' ';
        lcd.setCursor(0, 1); // move to beginning of bottom row
        lcd.print(buf); // print the characters to the display
      }
      
      else {
        // update the display to show the current date and time
        
        // build a string of text containing the weekday and the full date
        // (Hint: this code makes more sense if you read it vertically)
        buf[0]  = "BMTWTFSS"[wd];
        buf[1]  = "aouehrau"[wd];
        buf[2]  = "dneduitn"[wd];
        buf[3]  = ' ';
        buf[4]  = ' ';
        buf[5]  = ' ';
        buf[6]  = '0' + (mo/10);
        buf[7]  = '0' + (mo%10);
        buf[8]  = '/';
        buf[9]  = '0' + (dd/10);
        buf[10] = '0' + (dd%10);
        buf[11] = '/';
        buf[12] = '2';
        buf[13] = '0';
        buf[14] = '0' + (yy/10);
        buf[15] = '0' + (yy%10);
        buf[16] = 0;
        // suppress leading zero for month (character at position 6)
        if (buf[6] == '0') buf[6] = ' ';
        // display the weekday and full date on the top line
        lcd.setCursor(0, 0); // move to beginning of top line 
        lcd.print(buf); // print the text to the display
        
        // build a string of text containing the week number and the time
        buf[0]  = 'W';
        buf[1]  = 'k';
        buf[2]  = '0' + (wn/10);
        buf[3]  = '0' + (wn%10);
        buf[4]  = ' ';
        buf[5]  = ' ';
        buf[6]  = '0' + (hhTwelve/10);
        buf[7]  = '0' + (hhTwelve%10);
        buf[8]  = ':';
        buf[9]  = '0' + (mi/10);
        buf[10] = '0' + (mi%10);
        buf[11] = ':';
        buf[12] = '0' + (ss/10);
        buf[13] = '0' + (ss%10);
        buf[14] = ((hh<12) ? 'a' : 'p');
        buf[15] = 'm';
        buf[16] = 0;
        // suppress leading zero for hour (character at position 6)
        if (buf[6] == '0') buf[6] = ' ';
        // display the week number and the time on the bottom line
        lcd.setCursor(0, 1); // move to beginning of bottom line
        lcd.print(buf); // print the text to the display
      }  
    }
    
    else if (gotTheTime) {
      // if we have read a garbage time from the RTC,
      // then we will end up in here
      
      // we request that the time be set
      lcd.setCursor(0, 0); // go to beginning of top line
      lcd.print(F(" Please set the "));
      lcd.setCursor(0, 1); // go to beginning of bottom line
      lcd.print(F(" date and time. "));     
    }
    
    else {
      // if we have *completely* failed to read *anything* from the RTC,
      // then we will end up inside this "else"
      
      // indicate failure to read the time
      lcd.setCursor(0, 0); // go to beginning of top line
      lcd.print(F("Error:          "));
      lcd.setCursor(0, 1); // go to beginning of bottom line
      lcd.print(F("Can\'t read time "));
      
      while(1) {
        // do nothing, forever
      }
    }
    
    while (micros() - microsNow < 10000UL) {
      // do nothing for about 1/100 of a second
    }
    
    // remember these for the next time through loop()
    old_ss = ss;
    old_halfSec = halfSec;
  }
  
  else {
    // time setting mode
    
    microsNow = micros();
    
    // show the screen for setting the time
    // first, assemble the string to be displayed
    switch (clockMode) {
      case SET_YEAR:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set year: 20XX "));
        buf[13] = '0' + (yy/10);
        buf[14] = '0' + (yy%10);
        break;
      case SET_MONTH:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set month:  XX "));
        buf[13] = '0' + (mo/10);
        buf[14] = '0' + (mo%10);
        break;
      case SET_DATE:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set date:   XX "));
        buf[13] = '0' + (dd/10);
        buf[14] = '0' + (dd%10);
        break;
      case SET_HOUR:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set hour: XXXm "));
        // Should I take care of this conversion here or elsewhere?
        // I'll take care of it here, just to be safe.
        hhTwelve = hh % 12;
        if (hhTwelve == 0) hhTwelve = 12;
        buf[11] = '0' + (hhTwelve/10);
        buf[12] = '0' + (hhTwelve%10);
        buf[13] = ((hh < 12) ? 'a' : 'p');
        break;
      case SET_MINUTE:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set minute: XX "));
        buf[13] = '0' + (mi/10);
        buf[14] = '0' + (mi%10);
        break;
      case SET_SECOND:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set second: XX "));
        buf[13] = '0' + (ss/10);
        buf[14] = '0' + (ss%10);
        break;
      default:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Mode error!    "));
    }
    lcd.setCursor(0, 0); // move to beginning of top line 
    lcd.print(buf); // print the text to the display
    lcd.setCursor(0, 1); // go to beginning of bottom line
    lcd.print(F("                ")); // print a full row of blanks
    
    while (micros() - microsNow < 20000UL) {
      // do nothing for about 1/50 of a second
    }
  }
  
  // check the buttons
  // NOTE: because we are using INPUT_PULLUP, LOW means the button is pressed
  plusPressed = (digitalRead(PLUS_BUTTON_PIN) == LOW);
  setPressed = (digitalRead(SET_BUTTON_PIN) == LOW);

  if (plusPressed && !(old_plusPressed)) {
    // the "plus" button was just pressed
    switch (clockMode) {
      case SET_YEAR:
        yy = (yy + 1) % 100;
        if (yy < MINIMUM_YEAR) yy = MINIMUM_YEAR;
        break;
      case SET_MONTH:
        mo = (mo % 12) + 1;
        break;
      case SET_DATE:
        dd = (dd % daysInMonth(yy, mo)) + 1;
        break;
      case SET_HOUR:
        hh = (hh + 1) % 24;
        break;
      case SET_MINUTE:
        mi = (mi + 1) % 60;
        break;
      case SET_SECOND:
        ss = (ss + 5) % 60;
        ss -= (ss % 5);
        break;
      default:
        useBigDigits = !useBigDigits;
    }
  }
  
  if (setPressed && !(old_setPressed)) {
    // the "set" button was just pressed
    
    // change to the new mode
    if (clockMode == KEEP_TIME) {
      clockMode = SET_YEAR;
    }
    else {
      clockMode--;
    }
    
    // act according to the new mode
    switch (clockMode) {
      case SET_YEAR:
        if (yy < MINIMUM_YEAR) yy = MINIMUM_YEAR;
        if (yy > 99) yy = 99;
        break;
      case SET_MONTH:
        if (mo < 1) mo = 1;
        if (mo > 12) mo = 12;
        break;
      case SET_DATE:
        if (dd < 1) dd = 1;
        if (dd > daysInMonth(yy, mo)) dd = daysInMonth(yy, mo);
        break;
      case SET_HOUR:
        if (hh > 23) hh = 23;
        break;
      case SET_MINUTE:
        if (mi > 59) mi = 59;
        break;
      case SET_SECOND:
        if (ss > 59) ss = 59;
        break;
        
      case KEEP_TIME:
        // prepare to enter normal timekeeping mode
      
        // read the Daylight Saving Time on/off switch
        // NOTE: because we are using INPUT_PULLUP, LOW means on, and HIGH means off
        dstOn = (digitalRead(DST_SWITCH_PIN) == LOW);
        
        // because the RTC keeps "standard" (i.e. non-Daylight Saving) time,
        // then, if we are in Daylight Saving Time,
        // we will need to subtract 1 hour before we write to the RTC
        if (dstOn) {
          if (hh == 0) {
            hh = 23;
            dd--;
            if (dd == 0) {
              mo--;
              if (mo == 0) {
                mo = 12;
                yy--;
              }
              dd = daysInMonth(yy, mo);
            }
          }
          else {
            hh--;
          }
        }
        
        // calculate the day of the week (not that we really care, anyway)
        wd = ymdToWeekday(yy, mo, dd);
        
        // indicate that this is a valid time, not a garbage time
        timeIsGarbage = false;
      
        // write the date and time to the RTC
        Wire.beginTransmission(0x68); // address DS3231
        Wire.write(0x00); // select register
        Wire.write(numberToBcd(ss)); // seconds
        Wire.write(numberToBcd(mi)); // minutes
        Wire.write(numberToBcd(hh)); // hours (use 24-hour format)
        Wire.write(numberToBcd(wd)); // day of week (I use Mon=1 .. Sun=7)
        Wire.write(numberToBcd(dd)); // day of month
        Wire.write(numberToBcd(mo)); // month
        Wire.write(numberToBcd(yy)); // year (use only two digits)
        Wire.endTransmission();
        
        // update these variables
        microsNow = micros();
        microsAtLastSecond = microsNow;
        halfSec = ss * 2;
        
        // change these variables to nonsense values
        // this will force a display update next time through loop()
        old_ss = 99;
        old_halfSec = 198;
        
        // maybe I don't need this delay, but I'm putting it in anyway
        delay(50);
        break;
        
      default:
        // should never happen
        lcd.setCursor(0, 0); // move to beginning of top line      
        lcd.print(F("Error: bad mode!")); // display error message
        while (1) {
          // do nothing, forever
        }
    }
  }
  
  old_plusPressed = plusPressed;
  old_setPressed = setPressed;
}

byte bcdToNumber(byte b) {
  // convert BCD (binary-coded decimal) to an ordinary number
  byte tens = (b >> 4) & 0xF;
  byte ones = b & 0xF;
  return (byte)((tens * 10) + ones);
}

byte numberToBcd(byte n) {
  // convert a number to binary-coded decimal
  byte tens = (n/10);
  byte ones = (n%10);
  return (byte)((tens << 4) + ones);
}

byte daysInMonth(byte y, byte m) {
  // get the number of days in the given month
  // y is for the year (0 to 99 for years 2000 through 2099)
  // m is for the month (1 to 12)
  
  // reject out-of-range input
  if (y > 99) return 0;
  if ((m < 1) || (m > 12)) return 0;
  
  // Fourth, eleventh, ninth, and sixth,
  // thirty days to each we fix. 
  if ((m==4)||(m==11)||(m==9)||(m==6)) return 30; 
  // Every other, thirty-one,
  // except the second month alone,
  if (m!=2) return 31;
  // which hath twenty-eight, in fine,
  // till leap-year give it twenty-nine.
  if ((y%4)==0) return 29; // leap year
  return 28; // not a leap year 
}

byte ymdToWeekNumber (byte y, byte m, byte d) {
  // get the week number for a given year, month, and day  
  // NOTE: This function uses two-digit years
  // y is a number from 0 (for year 2000) to 99 (for year 2099)
  // This function will not work for years outside of this range!
  
  // reject out-of-range dates
  if (y > 99) return 0;
  if ((m < 1)||(m > 12)) return 0;
  if ((d < 1)||(d > 31)) return 0;
  // special case first two days of January 2000
  if ((y == 0) && (m == 1) && (d <= 2)) return 52;
  // (It is useful to know that Jan. 1, 2000 was a Saturday)
  // compute adjustment for dates within the year
  //     If Jan. 1 falls on: Mo Tu We Th Fr Sa Su
  // then the adjustment is:  6  7  8  9  3  4  5
  byte adj = ((y + 1 + ((y+3)/4)) % 7) + 3;
  // compute day of the year (in range 1-366)
  int doy = d;
  if (m > 1) doy += 31;
  if (m > 2) {
    if ((y%4)==0) doy += 29;
    else doy += 28;
  }
  if (m > 3) doy += 31;
  if (m > 4) doy += 30;
  if (m > 5) doy += 31;
  if (m > 6) doy += 30;
  if (m > 7) doy += 31;
  if (m > 8) doy += 31;
  if (m > 9) doy += 30;
  if (m > 10) doy += 31;
  if (m > 11) doy += 30;
  // compute week number
  byte wknum = (adj + doy) / 7;
  // check for boundary conditions
  if (wknum < 1) {
    // last week of the previous year
    // go to previous year and re-compute adjustment
    y--;
    adj = ((y + 1 + ((y+3)/4)) % 7) + 3;
    // check to see whether that year had 52 or 53 weeks
    // all years beginning on Thursday have 53 weeks
    if (adj==9) return 53;
    // leap years beginning on Wednesday have 53 weeks
    if ((adj==8) && ((y%4)==0)) return 53;
    // other years have 52 weeks
    return 52;
  }
  if (wknum > 52) {
    // check to see whether week 53 exists in this year
    // all years beginning on Thursday have 53 weeks
    if (adj==9) return 53;
    // leap years beginning on Wednesday have 53 weeks
    if ((adj==8) && ((y%4)==0)) return 53;
    // other years have 52 weeks
    return 1;
  }
  return wknum;
}

byte ymdToWeekday(byte y, byte m, byte d) {
  // get the day of the week for a given year, month, and day  
  // NOTE: This function uses two-digit years
  // y is a number from 0 (for year 2000) to 99 (for year 2099)
  // This function will not work for years outside of this range!
  if (y > 99) return 0;
  if (d < 1) return 0;
  byte l = (((y%4)==0) ? 1 : 0);
  byte n = y + (y/4);
  switch (m) {
    case 1:  if (d > 31) return 0;  n+=(1-l); break;
    case 2: if (d>(28+l)) return 0; n+=(4-l); break;
    case 3:  if (d > 31) return 0;  n+= 4;    break;
    case 4:  if (d > 30) return 0;  break;
    case 5:  if (d > 31) return 0;  n+= 2; break;
    case 6:  if (d > 30) return 0;  n+= 5; break;
    case 7:  if (d > 31) return 0;  break;
    case 8:  if (d > 31) return 0;  n+= 3; break;
    case 9:  if (d > 30) return 0;  n+= 6; break;
    case 10: if (d > 31) return 0;  n+= 1; break;
    case 11: if (d > 30) return 0;  n+= 4; break;
    case 12: if (d > 31) return 0;  n+= 6; break;
    default: return 0;
  }
  n += d;
  n = ((n + 4) % 7) + 1;
  return n;  // 1 for Mon, 2 for Tue, ..., 7 for Sun
}

i see no problem`

1 Like

Are you using an AVR processor (Ardiuno Mini, Micro, Nano, UNO, Leonardo, MEGA)? When it becomes necessary you can use the PROGMEM keyword to keep your array from being moved to RAM. It will stay in FLASH/PROGMEM and you have to use special functions like 'pgm_read_byte()' to fetch the data. For example:
char x = pgm_read_byte(&DIGIT_FONT[6][2]);

1 Like
#include "Wire.h"
#include <LiquidCrystal.h>

// Make sure your pin numbers match these!
//                RS  EN  D4  D5  D6  D7
LiquidCrystal lcd( 7,  6,  5,  4,  3,  2);
const byte DST_SWITCH_PIN = 8;
const byte SPEAKER_PIN = 9;
const byte PLUS_BUTTON_PIN = 11;
const byte SET_BUTTON_PIN = 12;

// some useful constants (names of modes)
const byte SET_YEAR   = 6;
const byte SET_MONTH  = 5;
const byte SET_DATE   = 4;
const byte SET_HOUR   = 3;
const byte SET_MINUTE = 2;
const byte SET_SECOND = 1;
const byte KEEP_TIME  = 0;

// another useful constant
const byte MINIMUM_YEAR = 22; // because I am writing this in 2022

// a font for digits
const char DIGIT_FONT[10][4] PROGMEM = {
  {  4,  2,  6,  2 },
  { 32,  2, 32,  2 },
  {  5,  2,  6,  1 },
  {  5,  2,  3,  2 },
  {  6,  2, 32,  2 },
  {  7,  1,  3,  2 },
  {  7,  1,  6,  2 },
  {  4,  2, 32,  2 },
  {  7,  2,  6,  2 },
  {  7,  2,  3,  2 }
};

// variables for the current date and time
byte yy=0, mo=1, dd=0, wd=6;
byte hh=0, mi=0, ss=0;
byte hhTwelve = 12;
byte wn=52;

// other helpful variables
bool gotTheTime = false;
bool timeIsGarbage = false;
byte old_ss = 99, halfSec = 198, old_halfSec = 198;
unsigned long microsNow = 0UL;
unsigned long microsAtLastSecond = 0UL;
bool dstOn = false;
bool plusPressed = false, old_plusPressed = false;
bool setPressed = false, old_setPressed = false;
byte clockMode = KEEP_TIME;
bool useBigDigits = false;

// a buffer for text to be displayed
char buf[20] = "";

void setup() {
  pinMode(DST_SWITCH_PIN, INPUT_PULLUP);
  pinMode(SPEAKER_PIN, OUTPUT);
  pinMode(PLUS_BUTTON_PIN, INPUT_PULLUP);
  pinMode(SET_BUTTON_PIN, INPUT_PULLUP);
  
  Wire.begin();
  lcd.begin(16, 2);
  
  // BEGINNING of code for setting the date and time
  
  // If you wish to set the date and time,
  // uncomment the following:
  
  /*
  // code to precisely set the external real-time clock
  Wire.beginTransmission(0x68); // address DS3231
  Wire.write(0x00); // select register
  // NOTE: before you run this code, you *must*
  // change the following numbers to the correct time!
  // (plus a few seconds to allow for compilation, etc.)
  Wire.write(numberToBcd( 0)); // seconds
  Wire.write(numberToBcd(21)); // minutes
  Wire.write(numberToBcd( 1)); // hours (use 24-hour format)
  Wire.write(numberToBcd( 6)); // day of week (I use Mon=1 .. Sun=7)
  Wire.write(numberToBcd( 9)); // day of month
  Wire.write(numberToBcd( 4)); // month
  Wire.write(numberToBcd(22)); // year (use only two digits)
  Wire.endTransmission();
  */
  
  // END of code for setting the date and time

  /*
  // define special characters for single cell numerals 10 through 12
  byte singleCellTen[]    = { 18, 21, 21, 21, 21, 21, 18,  0 };
  byte singleCellEleven[] = {  9, 27,  9,  9,  9,  9,  9,  0 };
  byte singleCellTwelve[] = { 22, 21, 17, 18, 20, 20, 23,  0 };
  lcd.createChar(10, singleCellTen);
  lcd.createChar(11, singleCellEleven);
  lcd.createChar(12, singleCellTwelve);
  */
  
  // define special "box" characters for drawing large digits
  byte boxDot[]          = {  0,  0,  0,  0,  0,  0, 24, 24 };
  byte boxLeftOnly[]     = { 24, 24, 24, 24, 24, 24, 24, 24 };
  byte boxBottomOnly[]   = {  0,  0,  0,  0,  0,  0, 31, 31 };
  byte boxTopAndLeft[]   = { 31, 31, 24, 24, 24, 24, 24, 24 };
  byte boxTopAndBottom[] = { 31, 31,  0,  0,  0,  0, 31, 31 };
  byte boxLShape[]       = { 24, 24, 24, 24, 24, 24, 31, 31 };
  byte boxCShape[]       = { 31, 31, 24, 24, 24, 24, 31, 31 };
  lcd.createChar(1, boxDot);
  lcd.createChar(2, boxLeftOnly);
  lcd.createChar(3, boxBottomOnly);
  lcd.createChar(4, boxTopAndLeft);
  lcd.createChar(5, boxTopAndBottom);
  lcd.createChar(6, boxLShape);
  lcd.createChar(7, boxCShape);
  
  // play a short tone (for testing the speaker)
  tone(SPEAKER_PIN, 1000, 500);
  
  // display a demo pattern (for testing the display)
  lcd.setCursor(0, 0); // go to beginning of top line
  lcd.print(F("  Display test  "));
  lcd.setCursor(0, 1); // go to beginning of bottom line
  lcd.print(F("0123456789 (^_^)"));

  for (int i = 5; i >= 1; i--) { // countdown from 5 to 1
    lcd.setCursor(0, 0); // go to beginning of top line
    lcd.print((char)('0' + i)); // print the digit
    lcd.setCursor(15, 0); // go to end of top line
    lcd.print((char)('0' + i)); // print the digit again
    delay(998);
  }
}

void loop() {
  if (clockMode == KEEP_TIME) {
    // normal timekeeping mode
    
    // first, we (try to) read the time from the RTC
    // send request to receive data starting at register 0
    Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
    Wire.write((byte)0); // start at register 0
    Wire.endTransmission();
    Wire.requestFrom(0x68, 7); // request seven bytes
   
    gotTheTime = false;
    while(Wire.available())
    { 
      ss = bcdToNumber(Wire.read()); // get seconds
      mi = bcdToNumber(Wire.read()); // get minutes
      hh = bcdToNumber(Wire.read()); // get hours
      Wire.read(); // discard the day of the week (we will calculate it ourself)
      dd = bcdToNumber(Wire.read()); // get day of month
      mo = bcdToNumber(Wire.read()); // get month
      yy = bcdToNumber(Wire.read()); // get year
      gotTheTime = true;
    }

    microsNow = micros();
    
    // detect garbage dates and times
    if ((yy < MINIMUM_YEAR) || (yy > 99)) timeIsGarbage = true;
    if ((mo < 1) || (mo > 12)) timeIsGarbage = true;
    if ((dd < 1) || (dd > daysInMonth(yy,mo))) timeIsGarbage = true;
    if (hh > 23) timeIsGarbage = true;
    if (mi > 59) timeIsGarbage = true;
    if (ss > 59) timeIsGarbage = true;
    
    // read the Daylight Saving Time on/off switch
    // NOTE: because we are using INPUT_PULLUP, LOW means on, and HIGH means off
    dstOn = (digitalRead(DST_SWITCH_PIN) == LOW);
    
    // adjust for Daylight Saving Time if applicable
    if (dstOn) {
      hh++;
      if (hh >= 24) {
        hh -= 24;
        dd++;
        if (dd > daysInMonth(yy, mo)) {
          dd = 1;
          mo++;
          if (mo > 12) {
            mo = 1;
            yy++;
          }
        }
      }
    }
    
    // try to figure out which half-second we are in
    // (this is important to making the striking work properly)
    if (ss != old_ss) microsAtLastSecond = microsNow;
    halfSec = ss * 2;
    if ((microsNow - microsAtLastSecond) >= 500000UL) halfSec++;
    
    // calculate day of the week
    wd = ymdToWeekday(yy, mo, dd);

    // calculate week number  
    wn = ymdToWeekNumber(yy, mo, dd);
    
    // convert hour to 12-hour format
    hhTwelve = hh;
    if (hhTwelve > 12) {
      hhTwelve -= 12;
    }
    if (hhTwelve == 0) {
      hhTwelve = 12;
    }
    
    if (gotTheTime && (!timeIsGarbage)) {
      // only if we have successfully read the time
      // (and it is not a garbage time)
      // do we then attempt to indicate the time
      
      if (halfSec != old_halfSec) { // do this only once every half-second
        // see if it is time for the clock to strike
        if (mi == 0) {  // strike on the hour, i.e. when minutes are 0
          if (halfSec < 26) {
            // play the Westminster Chimes
            switch (halfSec) {
              case 0:  tone(SPEAKER_PIN, 330, 420); break;
              case 1:  tone(SPEAKER_PIN, 415, 420); break;
              case 2:  tone(SPEAKER_PIN, 370, 420); break;
              case 3:  tone(SPEAKER_PIN, 247, 735); break;
              case 6:  tone(SPEAKER_PIN, 330, 420); break;
              case 7:  tone(SPEAKER_PIN, 370, 420); break;
              case 8:  tone(SPEAKER_PIN, 415, 420); break;
              case 9:  tone(SPEAKER_PIN, 330, 735); break;
              case 12: tone(SPEAKER_PIN, 415, 420); break;
              case 13: tone(SPEAKER_PIN, 330, 420); break;
              case 14: tone(SPEAKER_PIN, 370, 420); break;
              case 15: tone(SPEAKER_PIN, 247, 735); break;
              case 18: tone(SPEAKER_PIN, 247, 420); break;
              case 19: tone(SPEAKER_PIN, 370, 420); break;
              case 20: tone(SPEAKER_PIN, 415, 420); break;
              case 21: tone(SPEAKER_PIN, 330, 735); break;
              default: break;
            }
          }
          else if ((halfSec < (26 + 3 * hhTwelve)) && ((halfSec % 3) == 2)) {
            // bong the hours
            tone(SPEAKER_PIN, 415, 750);
          }
        }
      }
      
      if (useBigDigits) {
        // update the display to show the current time with big digits
        
        // build a string of characters for the top row of the display
        buf[0]  = ' ';
        buf[1]  = pgm_read_byte(&DIGIT_FONT[hh/10][0]);
        buf[2]  = pgm_read_byte(&DIGIT_FONT[hh/10][1]);
        buf[3]  = pgm_read_byte(&DIGIT_FONT[hh%10][0]);
        buf[4]  = pgm_read_byte(&DIGIT_FONT[hh%10][1]);
        buf[5]  = 1; // dot for colon
        buf[6]  = pgm_read_byte(&DIGIT_FONT[mi/10][0]);
        buf[7]  = pgm_read_byte(&DIGIT_FONT[mi/10][1]);
        buf[8]  = pgm_read_byte(&DIGIT_FONT[mi%10][0]);
        buf[9]  = pgm_read_byte(&DIGIT_FONT[mi%10][1]);
        buf[10] = 1; // dot for colon
        buf[11] = pgm_read_byte(&DIGIT_FONT[ss/10][0]);
        buf[12] = pgm_read_byte(&DIGIT_FONT[ss/10][1]);
        buf[13] = pgm_read_byte(&DIGIT_FONT[ss%10][0]);
        buf[14] = pgm_read_byte(&DIGIT_FONT[ss%10][1]);
        buf[15] = ' ';
        lcd.setCursor(0, 0); // move to beginning of top row 
        lcd.print(buf); // print the characters to the display
        
        // build a string of characters for the bottom row of the display
        buf[0]  = ' ';
        buf[1]  = pgm_read_byte(&DIGIT_FONT[hh/10][2]);
        buf[2]  = pgm_read_byte(&DIGIT_FONT[hh/10][3]);
        buf[3]  = pgm_read_byte(&DIGIT_FONT[hh%10][2]);
        buf[4]  = pgm_read_byte(&DIGIT_FONT[hh%10][3]);
        buf[5]  = 1; // dot for colon
        buf[6]  = pgm_read_byte(&DIGIT_FONT[mi/10][2]);
        buf[7]  = pgm_read_byte(&DIGIT_FONT[mi/10][3]);
        buf[8]  = pgm_read_byte(&DIGIT_FONT[mi%10][2]);
        buf[9]  = pgm_read_byte(&DIGIT_FONT[mi%10][3]);
        buf[10] = 1; // dot for colon
        buf[11] = pgm_read_byte(&DIGIT_FONT[ss/10][2]);
        buf[12] = pgm_read_byte(&DIGIT_FONT[ss/10][3]);
        buf[13] = pgm_read_byte(&DIGIT_FONT[ss%10][2]);
        buf[14] = pgm_read_byte(&DIGIT_FONT[ss%10][3]);
        buf[15] = ' ';
        lcd.setCursor(0, 1); // move to beginning of bottom row
        lcd.print(buf); // print the characters to the display
      }
      
      else {
        // update the display to show the current date and time
        
        // build a string of text containing the weekday and the full date
        // (Hint: this code makes more sense if you read it vertically)
        buf[0]  = "BMTWTFSS"[wd];
        buf[1]  = "aouehrau"[wd];
        buf[2]  = "dneduitn"[wd];
        buf[3]  = ' ';
        buf[4]  = ' ';
        buf[5]  = ' ';
        buf[6]  = '0' + (mo/10);
        buf[7]  = '0' + (mo%10);
        buf[8]  = '/';
        buf[9]  = '0' + (dd/10);
        buf[10] = '0' + (dd%10);
        buf[11] = '/';
        buf[12] = '2';
        buf[13] = '0';
        buf[14] = '0' + (yy/10);
        buf[15] = '0' + (yy%10);
        buf[16] = 0;
        // suppress leading zero for month (character at position 6)
        if (buf[6] == '0') buf[6] = ' ';
        // display the weekday and full date on the top line
        lcd.setCursor(0, 0); // move to beginning of top line 
        lcd.print(buf); // print the text to the display
        
        // build a string of text containing the week number and the time
        buf[0]  = 'W';
        buf[1]  = 'k';
        buf[2]  = '0' + (wn/10);
        buf[3]  = '0' + (wn%10);
        buf[4]  = ' ';
        buf[5]  = ' ';
        buf[6]  = '0' + (hhTwelve/10);
        buf[7]  = '0' + (hhTwelve%10);
        buf[8]  = ':';
        buf[9]  = '0' + (mi/10);
        buf[10] = '0' + (mi%10);
        buf[11] = ':';
        buf[12] = '0' + (ss/10);
        buf[13] = '0' + (ss%10);
        buf[14] = ((hh<12) ? 'a' : 'p');
        buf[15] = 'm';
        buf[16] = 0;
        // suppress leading zero for hour (character at position 6)
        if (buf[6] == '0') buf[6] = ' ';
        // display the week number and the time on the bottom line
        lcd.setCursor(0, 1); // move to beginning of bottom line
        lcd.print(buf); // print the text to the display
      }  
    }
    
    else if (gotTheTime) {
      // if we have read a garbage time from the RTC,
      // then we will end up in here
      
      // we request that the time be set
      lcd.setCursor(0, 0); // go to beginning of top line
      lcd.print(F(" Please set the "));
      lcd.setCursor(0, 1); // go to beginning of bottom line
      lcd.print(F(" date and time. "));     
    }
    
    else {
      // if we have *completely* failed to read *anything* from the RTC,
      // then we will end up inside this "else"
      
      // indicate failure to read the time
      lcd.setCursor(0, 0); // go to beginning of top line
      lcd.print(F("Error:          "));
      lcd.setCursor(0, 1); // go to beginning of bottom line
      lcd.print(F("Can\'t read time "));
      
      while(1) {
        // do nothing, forever
      }
    }
    
    while (micros() - microsNow < 10000UL) {
      // do nothing for about 1/100 of a second
    }
    
    // remember these for the next time through loop()
    old_ss = ss;
    old_halfSec = halfSec;
  }
  
  else {
    // time setting mode
    
    microsNow = micros();
    
    // show the screen for setting the time
    // first, assemble the string to be displayed
    switch (clockMode) {
      case SET_YEAR:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set year: 20XX "));
        buf[13] = '0' + (yy/10);
        buf[14] = '0' + (yy%10);
        break;
      case SET_MONTH:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set month:  XX "));
        buf[13] = '0' + (mo/10);
        buf[14] = '0' + (mo%10);
        break;
      case SET_DATE:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set date:   XX "));
        buf[13] = '0' + (dd/10);
        buf[14] = '0' + (dd%10);
        break;
      case SET_HOUR:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set hour: XXXm "));
        // Should I take care of this conversion here or elsewhere?
        // I'll take care of it here, just to be safe.
        hhTwelve = hh % 12;
        if (hhTwelve == 0) hhTwelve = 12;
        buf[11] = '0' + (hhTwelve/10);
        buf[12] = '0' + (hhTwelve%10);
        buf[13] = ((hh < 12) ? 'a' : 'p');
        break;
      case SET_MINUTE:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set minute: XX "));
        buf[13] = '0' + (mi/10);
        buf[14] = '0' + (mi%10);
        break;
      case SET_SECOND:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Set second: XX "));
        buf[13] = '0' + (ss/10);
        buf[14] = '0' + (ss%10);
        break;
      default:
        //                  01234567890123456
        strcpy_P(buf, PSTR(" Mode error!    "));
    }
    lcd.setCursor(0, 0); // move to beginning of top line 
    lcd.print(buf); // print the text to the display
    lcd.setCursor(0, 1); // go to beginning of bottom line
    lcd.print(F("                ")); // print a full row of blanks
    
    while (micros() - microsNow < 20000UL) {
      // do nothing for about 1/50 of a second
    }
  }
  
  // check the buttons
  // NOTE: because we are using INPUT_PULLUP, LOW means the button is pressed
  plusPressed = (digitalRead(PLUS_BUTTON_PIN) == LOW);
  setPressed = (digitalRead(SET_BUTTON_PIN) == LOW);

  if (plusPressed && !(old_plusPressed)) {
    // the "plus" button was just pressed
    switch (clockMode) {
      case SET_YEAR:
        yy = (yy + 1) % 100;
        if (yy < MINIMUM_YEAR) yy = MINIMUM_YEAR;
        break;
      case SET_MONTH:
        mo = (mo % 12) + 1;
        break;
      case SET_DATE:
        dd = (dd % daysInMonth(yy, mo)) + 1;
        break;
      case SET_HOUR:
        hh = (hh + 1) % 24;
        break;
      case SET_MINUTE:
        mi = (mi + 1) % 60;
        break;
      case SET_SECOND:
        ss = (ss + 5) % 60;
        ss -= (ss % 5);
        break;
      default:
        useBigDigits = !useBigDigits;
    }
  }
  
  if (setPressed && !(old_setPressed)) {
    // the "set" button was just pressed
    
    // change to the new mode
    if (clockMode == KEEP_TIME) {
      clockMode = SET_YEAR;
    }
    else {
      clockMode--;
    }
    
    // act according to the new mode
    switch (clockMode) {
      case SET_YEAR:
        if (yy < MINIMUM_YEAR) yy = MINIMUM_YEAR;
        if (yy > 99) yy = 99;
        break;
      case SET_MONTH:
        if (mo < 1) mo = 1;
        if (mo > 12) mo = 12;
        break;
      case SET_DATE:
        if (dd < 1) dd = 1;
        if (dd > daysInMonth(yy, mo)) dd = daysInMonth(yy, mo);
        break;
      case SET_HOUR:
        if (hh > 23) hh = 23;
        break;
      case SET_MINUTE:
        if (mi > 59) mi = 59;
        break;
      case SET_SECOND:
        if (ss > 59) ss = 59;
        break;
        
      case KEEP_TIME:
        // prepare to enter normal timekeeping mode
      
        // read the Daylight Saving Time on/off switch
        // NOTE: because we are using INPUT_PULLUP, LOW means on, and HIGH means off
        dstOn = (digitalRead(DST_SWITCH_PIN) == LOW);
        
        // because the RTC keeps "standard" (i.e. non-Daylight Saving) time,
        // then, if we are in Daylight Saving Time,
        // we will need to subtract 1 hour before we write to the RTC
        if (dstOn) {
          if (hh == 0) {
            hh = 23;
            dd--;
            if (dd == 0) {
              mo--;
              if (mo == 0) {
                mo = 12;
                yy--;
              }
              dd = daysInMonth(yy, mo);
            }
          }
          else {
            hh--;
          }
        }
        
        // calculate the day of the week (not that we really care, anyway)
        wd = ymdToWeekday(yy, mo, dd);
        
        // indicate that this is a valid time, not a garbage time
        timeIsGarbage = false;
      
        // write the date and time to the RTC
        Wire.beginTransmission(0x68); // address DS3231
        Wire.write(0x00); // select register
        Wire.write(numberToBcd(ss)); // seconds
        Wire.write(numberToBcd(mi)); // minutes
        Wire.write(numberToBcd(hh)); // hours (use 24-hour format)
        Wire.write(numberToBcd(wd)); // day of week (I use Mon=1 .. Sun=7)
        Wire.write(numberToBcd(dd)); // day of month
        Wire.write(numberToBcd(mo)); // month
        Wire.write(numberToBcd(yy)); // year (use only two digits)
        Wire.endTransmission();
        
        // update these variables
        microsNow = micros();
        microsAtLastSecond = microsNow;
        halfSec = ss * 2;
        
        // change these variables to nonsense values
        // this will force a display update next time through loop()
        old_ss = 99;
        old_halfSec = 198;
        
        // maybe I don't need this delay, but I'm putting it in anyway
        delay(50);
        break;
        
      default:
        // should never happen
        lcd.setCursor(0, 0); // move to beginning of top line      
        lcd.print(F("Error: bad mode!")); // display error message
        while (1) {
          // do nothing, forever
        }
    }
  }
  
  old_plusPressed = plusPressed;
  old_setPressed = setPressed;
}

byte bcdToNumber(byte b) {
  // convert BCD (binary-coded decimal) to an ordinary number
  byte tens = (b >> 4) & 0xF;
  byte ones = b & 0xF;
  return (byte)((tens * 10) + ones);
}

byte numberToBcd(byte n) {
  // convert a number to binary-coded decimal
  byte tens = (n/10);
  byte ones = (n%10);
  return (byte)((tens << 4) + ones);
}

byte daysInMonth(byte y, byte m) {
  // get the number of days in the given month
  // y is for the year (0 to 99 for years 2000 through 2099)
  // m is for the month (1 to 12)
  
  // reject out-of-range input
  if (y > 99) return 0;
  if ((m < 1) || (m > 12)) return 0;
  
  // Fourth, eleventh, ninth, and sixth,
  // thirty days to each we fix. 
  if ((m==4)||(m==11)||(m==9)||(m==6)) return 30; 
  // Every other, thirty-one,
  // except the second month alone,
  if (m!=2) return 31;
  // which hath twenty-eight, in fine,
  // till leap-year give it twenty-nine.
  if ((y%4)==0) return 29; // leap year
  return 28; // not a leap year 
}

byte ymdToWeekNumber (byte y, byte m, byte d) {
  // get the week number for a given year, month, and day  
  // NOTE: This function uses two-digit years
  // y is a number from 0 (for year 2000) to 99 (for year 2099)
  // This function will not work for years outside of this range!
  
  // reject out-of-range dates
  if (y > 99) return 0;
  if ((m < 1)||(m > 12)) return 0;
  if ((d < 1)||(d > 31)) return 0;
  // special case first two days of January 2000
  if ((y == 0) && (m == 1) && (d <= 2)) return 52;
  // (It is useful to know that Jan. 1, 2000 was a Saturday)
  // compute adjustment for dates within the year
  //     If Jan. 1 falls on: Mo Tu We Th Fr Sa Su
  // then the adjustment is:  6  7  8  9  3  4  5
  byte adj = ((y + 1 + ((y+3)/4)) % 7) + 3;
  // compute day of the year (in range 1-366)
  int doy = d;
  if (m > 1) doy += 31;
  if (m > 2) {
    if ((y%4)==0) doy += 29;
    else doy += 28;
  }
  if (m > 3) doy += 31;
  if (m > 4) doy += 30;
  if (m > 5) doy += 31;
  if (m > 6) doy += 30;
  if (m > 7) doy += 31;
  if (m > 8) doy += 31;
  if (m > 9) doy += 30;
  if (m > 10) doy += 31;
  if (m > 11) doy += 30;
  // compute week number
  byte wknum = (adj + doy) / 7;
  // check for boundary conditions
  if (wknum < 1) {
    // last week of the previous year
    // go to previous year and re-compute adjustment
    y--;
    adj = ((y + 1 + ((y+3)/4)) % 7) + 3;
    // check to see whether that year had 52 or 53 weeks
    // all years beginning on Thursday have 53 weeks
    if (adj==9) return 53;
    // leap years beginning on Wednesday have 53 weeks
    if ((adj==8) && ((y%4)==0)) return 53;
    // other years have 52 weeks
    return 52;
  }
  if (wknum > 52) {
    // check to see whether week 53 exists in this year
    // all years beginning on Thursday have 53 weeks
    if (adj==9) return 53;
    // leap years beginning on Wednesday have 53 weeks
    if ((adj==8) && ((y%4)==0)) return 53;
    // other years have 52 weeks
    return 1;
  }
  return wknum;
}

byte ymdToWeekday(byte y, byte m, byte d) {
  // get the day of the week for a given year, month, and day  
  // NOTE: This function uses two-digit years
  // y is a number from 0 (for year 2000) to 99 (for year 2099)
  // This function will not work for years outside of this range!
  if (y > 99) return 0;
  if (d < 1) return 0;
  byte l = (((y%4)==0) ? 1 : 0);
  byte n = y + (y/4);
  switch (m) {
    case 1:  if (d > 31) return 0;  n+=(1-l); break;
    case 2: if (d>(28+l)) return 0; n+=(4-l); break;
    case 3:  if (d > 31) return 0;  n+= 4;    break;
    case 4:  if (d > 30) return 0;  break;
    case 5:  if (d > 31) return 0;  n+= 2; break;
    case 6:  if (d > 30) return 0;  n+= 5; break;
    case 7:  if (d > 31) return 0;  break;
    case 8:  if (d > 31) return 0;  n+= 3; break;
    case 9:  if (d > 30) return 0;  n+= 6; break;
    case 10: if (d > 31) return 0;  n+= 1; break;
    case 11: if (d > 30) return 0;  n+= 4; break;
    case 12: if (d > 31) return 0;  n+= 6; break;
    default: return 0;
  }
  n += d;
  n = ((n + 4) % 7) + 1;
  return n;  // 1 for Mon, 2 for Tue, ..., 7 for Sun
}

It's an Uno, which I'm using for testing and debugging. When I decide I'm finished with the software, I plan to move it to a Nano.

Also, what should I do instead of lines like these?

        buf[0]  = "BMTWTFSS"[wd];
        buf[1]  = "aouehrau"[wd];
        buf[2]  = "dneduitn"[wd];

These lines are to print the day of the week (read them vertically). I will also have more, similar code, for printing the abbreviation for the month.

Or, am I worried over nothing? Is there a way to measure how much RAM I am really using?

buf[] ={
  "BMTWTFSS"[wd],
  "aouehrau"[wd],
  "dneduitn"[wd] 
}

This is helpful: Gammon Forum : Electronics : Microprocessors : Putting constant data into program memory (PROGMEM) scroll up and there's progmem stuff too

if (y > 99 || d < 1 || m < 1) return 0;

What I would use:

const char DOW_Names[8][3] = 
{"Bad","Mon","Tue","Wed","Thu","Fri","Sat","Sun"};

        buf[0]  = DOW_Names[wd][0];
        buf[1]  = DOW_Names[wd][1];
        buf[2]  = DOW_Names[wd][2];

Wouldn't that invoke undefined behavior, or something of that sort?
I would think that that would need to be:

const char DOW_Names[8][4] = 
{"Bad","Mon","Tue","Wed","Thu","Fri","Sat","Sun"};

with room for the null terminators.

How about

const char * DOW_Names[8] = 
{"Bad","Mon","Tue","Wed","Thu","Fri","Sat","Sun"};

Doesn't this avoid the magic number by letting the compiler do the counting for you?

const char * DOW_Names[] = 
{"Bad","Mon","Tue","Wed","Thu","Fri","Sat","Sun"};

Yes it does, but my main point was the use of an array of pointers

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.