Word Clock with minute resolution of time in words and linear display of seconds

6v6gt:
What I don't see here is how to get the time from another source (say an RTC) for cases where the internet is not available.

I suppose you could have the RTC keep UTC, and then do the math on that.

Supposing that you are using an RTC which keeps track of time in "human" form (seconds, minutes, hours, day of week, day of month, month, last two figures of year), converting between Unix time and "human" time is not as much trouble as one might think.

Seconds, minutes, and hours are left as an exercise for the reader.
As for days, well, there are 86400 seconds in 1 day, and so you just divide the "raw" count of seconds by 86400 to get a count of days. That count of days uses 1970-01-01 as its "zero" date. From that count, subtract 10957. That is because the functions I am about to give you use 2000-01-01 as their "zero" date, and from 1970-01-01 to 2000-01-01 it is 10957 days. Please read the comments within the functions, so as to be aware of input and output syntax.

uint16_t ymdToDays(uint8_t y, uint8_t m, uint8_t d) {
  // Converts date (year, month, day) to day number.
  // Input the year as a number from 0 to 99. (0 means AD 2000)
 
  // some basic sanity checks
  if (y>99) return 0xFFFF;
  if ((m<1)||(m>12)) return 0xFFFF;
  if ((d<1)||(d>31)) return 0xFFFF;
 
  y+=4; // so that the next line can't make it go negative
  if (m<=2) {y--; m+=12;} // treat Jan. and Feb. as part of preceding year
 
  uint16_t n=365*y; // whole years (not counting leap days)
  n+=(y>>2); // add in the leap days
  n+=(31*(uint16_t)m); // take care of months (assuming 31 days per month)
 
  // the next few lines compensate for short months
  if     (m>11) n-=4;
  else if (m>9) n-=3;
  else if (m>6) n-=2;
  else if (m>4) n-=1;
 
  n+=d; // take care of the day of the month
 
  return (n-1495); // make 2000-01-01(Sat) be day 0
}


uint32_t daysToYmd(uint16_t n) {
  // Converts day number to date.
  // Valid day numbers are 0 through 36524.
  //     0 means 2000-01-01(Sat)
  // 36524 means 2099-12-31(Thu)
  if (n>36524) return 0xFFFFFFFF;
 
  // Here we go!
  n+=1401; // move starting point to 1996-03-01
  uint8_t y=(n/1461)*4; // take care of 4-year intervals
  n%=1461; // max 3 years 365 days (not 4 years!) remaining
  if (n>=730) {n-=730; y+=2;} // 2 years
  if (n>=365) {n-=365; y++;}  // 1 year
  uint8_t m=3; // get started on the month
  // Note repeating pattern of month lengths:
  // 31 30 31 30 31  31 30 31 30 31  31 <30
  // Mr Ap My Je Jl  Au Se Oc No De  Ja Fe
  if (n>=306) {n-=306; y++; m=1;} // for Jan. and Feb.
  else if (n>=153) {n-=153; m=8;} // for Aug. thru Dec.
  if (n>=122) {n-=122; m+=4;} // here, 122 days mean 4 months
  else if (n>=61) {n-=61; m+=2;} // 61 days mean 2 months
  if (n>=31) {n-=31; m++;} // 31 days means exactly 1 month
  uint8_t d=n+1; // get the day of the month
  y-=4; // make y be 0 for AD 2000
 
  // Now we have the correct year, month, and day of the month.
  // (Note: y is 0 for AD 2000, ... 99 for AD 2099)
 
  // The following output is a "placeholder" for testing purposes.
  // It should probably be replaced with something more useful.
  return (((2000+y)*10000L)+(100L*m)+d);
}

Now you can convert between "human" UTC and Unix time without a problem. At least, for UTC dates from 2000-01-01 to 2099-12-31, there will be no problem.