How do you calculate the year, month and dom from a UTC time stamp?

It can't be that hard!

Time library for Arduino AVR does it through the day(uint32_t), month(uint32_t) and year(uint32_t)

But unfortunately these functions are not implemented in the time library for ESP32.

Does anyone know where or how the day(uint32_t), month(uint32_t) and year(uint32_t) are coded in the Arduino architecture.

Because I can't figure out how the calculation is done by looking at the source code for the AVR version of the Time library.

I am stuck and I can't move on until I figure this out.

Never mind I found what I needed here: https://www.jotform.com/help/443-Mastering-Date-and-Time-Calculation after trawling through a heap of useless links.

The architecture of the processor has nothing at all to do with the nature of calendar calculations.
The usual approach is to count seconds from some defined epoch, like Jan 1st 1970.

This function might be helpful for dealing with dates:

int32_t daycount(int16_t y, int8_t m, int8_t d) {
  // Input is year, month, and day, in that order.
  // For contemporary years, use the four-digit year number.
  // The return value is a day number using an "endless" count of days. 
 
  // sanity checking of input
  if ((y<-30000)||(y>30000)) return -999999999;
  if ((m<1)||(m>12))         return -999999999;
  if ((d<1)||(d>31))         return -999999999;
 
  // pretend Jan. and Feb. belong to previous year
  if (m<=2) {
    y--;
    m+=12;
  }
 
  // day count is unimaginatively named "n"
  int32_t n = 365 * (int32_t)y;
 
  // next we calculate leap days
  int16_t x = y >> 2; // to get floor(y/4) and not trunc(y/4)
  n += x; // 4-year rule
  x = ((x+8000)/25)-320; // workaround for braindead C/C++ division
  // now x equals floor(y/100)
  n -= x; // 100-year rule
  x >>= 2; // make it floor(y/400)
  n += x; // 400-year rule
 
  // next we take care of the months
  n += (31 * (int16_t)m);
  // adjust for the 30-day months
  if     (m>11) n-=4;
  else if (m>9) n-=3;
  else if (m>6) n-=2;
  else if (m>4) n-=1;
 
  // next we take care of the day of the month
  n += d;
 
  // return n; // this line is for testing purposes only
 
  // finally, we add or subtract a constant
  return (n + 1721026); // day 0 == -4713-11-24(Mon) (Julian Day at noon)
  return (n -      34); // day 0 ==  0000-01-01(Sat)
  return (n -     400); // day 0 ==  0001-01-01(Mon) (think REXX)
  return (n -  693993); // day 0 ==  1899-12-30(Sat) (think MS Excel)
  return (n -  719562); // day 0 ==  1970-01-01(Thu) (think Unix time)
  return (n -  730519); // day 0 ==  2000-01-01(Sat)
}

It converts a year, month, and day of the month to a straight count of days.
See the comments within the function. Especially notice that you can change the return value to suit your needs.

It really aint hard as I predicted:

1 minute 60 seconds
1 hour 3600 seconds
1 day 86400 seconds
1 month (30.44 days) 2629743 seconds
1 year (365.24 days) 31556926 seconds

It is just a matter of finding the relevant information is the sea of garbage on this subject - mainly using pre-exisiting javascript, PHP and C++ functions to do the conversion.

All no good for my purposes.

All I did was calculate the year from 31556926 seconds.

Then I can get the number of seconds into this year and therefore the number of days into this year.

After that calculating the month and day of month is easy:

uint8_t CNTPTime::getMonth(uint32_t nEpoch)
{
  uint16_t nYear = getYear(nEpoch), nDayCount = 0;
  uint8_t nMonth = 1;
  const uint32_t nSecondsInThisYear = nEpoch % nSECONDS_PER_YEAR;
  const uint16_t nDOY = nSecondsInThisYear / nSECONDS_PER_DAY;

  while (true)
  {
    nDayCount += getMaxDOM(nMonth, nYear);

    if (nDayCount > nDOY)
    {
      break;
    }
    else
      nMonth++;
  }
  return nMonth;
}

But from time.cpp:

int hour(time_t t) { // the hour for the given time
  refreshCache(t);
  return tm.Hour;  
}

And using these functions like so:

hour(nEpoch);

It does work nicely but I have no clue how it is working from the source code in Time.cpp

boylesg:
All I did was calculate the year from 31556926 seconds.

Then I can get the number of seconds into this year and therefore the number of days into this year.

That will give you an answer, but it will be the wrong answer.

A calendar year is either 365 days or 366 days. In seconds, those figures would be 31536000 seconds and 31622400 seconds, respectively.

Your figure of 31556926 seconds equals 365 days, 5 hours, 48 minutes, and 46 seconds. I do not suppose you want the next second after 05:48:45 to be 00:00:00.

Also, I made a mistake. I gave you a function that converts in the direction opposite from what you want.

Here is a function I just dug up that converts a count of days since January 1, 2000 to a year, month, and day of the month. The main caveat is that it cannot handle dates before AD 2000.

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);
}

From January 1, 1970 (the base date of the Unix epoch) to January 1, 2000 (the base date of my function) is 10957 days. That is 946684800 seconds. So, you can subtract 946684800 seconds from the Unix time you wish to convert, then use division and modulus to get days, hours, minutes, and seconds (the divisors being, in order, 86400, 3600, and 60), then feed the “days” number into my function to get the sought-after year, month, and day of the month.

Don't forget the occasional leap second that is added as the world's rotational speed slows down...

CrossRoads:
Don't forget the occasional leap second that is added as the world's rotational speed slows down...
Leap second - Wikipedia

Unix time attempts to sweep leap seconds under the carpet.

CrossRoads:
Don’t forget the occasional leap second that is added as the world’s rotational speed slows down…
Leap second - Wikipedia

I appreciate that, but note ‘1 year (365.24 days) 31556926 seconds’

The leap seconds are already accounted for in the average length of a year in seconds?

And I can obtain the current year independently of day of current year:

uint16_t CNTPTime::getYear(uint32_t nEpoch)
{  
  return 1970 + (nEpoch / nSECONDS_PER_YEAR);
}

From the year value I know whether or not it is a leap year and therefore the number of days to add to the count for February when calculating the month and the day of month.

Why reinvent the wheel? Just use the "gmtime" standard library function!

struct tm tm = *gmtime(&utc_timestamp);

// tm.tm_year + 1900 gives you the year (e.g., 2018)
// tm.tm_mon + 1 gives you the month (e.g., 3)
// tm.tm_mday gives you the day of the month (e.g., 23)

You may have to adjust utc_timestamp to make it match the epoch that gmtime() expects. avr-libc uses 2000-01-01T00:00:00Z as its epoch. I have no idea what epoch the ESP32 uses for its UTC timestamps. avr-libc provides the macros UNIX_OFFSET and NTP_OFFSET to convert timestamps between avr-libc epoch and the Unix or NTP epochs, which might be useful.

christop:
Why reinvent the wheel? Just use the "gmtime" standard library function!

struct tm tm = *gmtime(&utc_timestamp);

// tm.tm_year + 1900 gives you the year (e.g., 2018)
// tm.tm_mon + 1 gives you the month (e.g., 3)
// tm.tm_mday gives you the day of the month (e.g., 23)




You may have to adjust utc_timestamp to make it match the epoch that gmtime() expects. avr-libc uses 2000-01-01T00:00:00Z as its epoch. I have no idea what epoch the ESP32 uses for its UTC timestamps. avr-libc provides the macros UNIX_OFFSET and NTP_OFFSET to convert timestamps between avr-libc epoch and the Unix or NTP epochs, which might be useful.

I am getting the time stamp directly from

const char* CNTPTime::m_arrayTimeServers[] = {"0.uk.pool.ntp.org", "1.uk.pool.ntp.org", 
                                                   "0.us.pool.ntp.org", "1.us.pool.ntp.org", 
                                                   "2.us.pool.ntp.org", "3.us.pool.ntp.org", 
                                                   "0.au.pool.ntp.org", "1.au.pool.ntp.org", 
                                                   "2.au.pool.ntp.org", "3.au.pool.ntp.org"};

Does that make a difference?

@boylesg

Using your method, what year, month, day of the month, hour, minute, and second do you get for these timestamps?

1514732446
1514732447
1514732448
1514732449
1514732450

boylesg:
I am getting the time stamp directly from

const char* CNTPTime::m_arrayTimeServers[] = {"0.uk.pool.ntp.org", "1.uk.pool.ntp.org", 

"0.us.pool.ntp.org", "1.us.pool.ntp.org",
                                                  "2.us.pool.ntp.org", "3.us.pool.ntp.org",
                                                  "0.au.pool.ntp.org", "1.au.pool.ntp.org",
                                                  "2.au.pool.ntp.org", "3.au.pool.ntp.org"};




Does that make a difference?

Yes. Assuming it's an NTP timestamp (the epoch is 1900-01-01T00:00:00Z), convert it to avr-libc time by subtracting NTP_OFFSET. Then you can use the avr-libc time functions like gmtime() to get a breakdown of the time into year, month, day, etc.

ESP32 core does not seem to have the functions day(uint32_t TimeStamp) etc

So the ESP32 version of Time.h is useless to me. I don't know how else I can get the day, month and year from a UTC time stamp using ESP32 Time.h.

So I am not using it at all, just writing my own code to extract these from the time stamp.

So I assume that since the ESP32 is just a hollow pipe to the internet timer server, then what ever specific epoch ESP32 uses is irrelevant in my case?