Calculate elapsed time without unix timestamp

Hi there!

I would like to calculate the time elapsed since a certain date but without the unix timestamp with RTC approach.

Is this possible?

Background is that I would like to build a "electronic art piece" displaying the time elapsed since the birth of my daughter in seconds, minutes, hours, days, months, years. :wink:

With the unix timestamp approach the 32bit value of the unix timestamp will overflow when she is 18 (2038). Allthough this would also be symbolic, I would love to know that this piece would run for at least 2120 (she would be 100 years then :wink: )

Any ideas to solve this?

Thanks so much! :slight_smile:

I doubt the common RTCs have anything to do with the UNIX timestamp. :roll_eyes: They seem to have registers which simply count seconds, minutes, hours, days and such with rollover algorithms for the date. They are not microprocessors as such which use software to do the counting.

A 32 bit counter will count 136 years-worth of seconds.

I can't see a way to do it. I assume that you reject the standard RTC because it is battery backed and you don't expect the battery to have a long enough life.

I'll assume too, that you're ok with it failing during a power outage as long as it comes back up afterwards.

There are two problems: hardware and knowing the time. Hardware kills the project dead: you might get an Arduino without electrolytic caps to last eighteen years, but I seriously doubt a century. Worse, whatever display you use is unlikely to last even as long as the Arduino.

Since the Arduino can't know the time, it must use something external, so you're pushing a part of the problem upstream, which means you need your own hardware to do it or an external source. You might consider a web service that provides the time but where will you find one that's guaranteed to last unchanged for so many years.

Then too, if you're using the internet, how will you connect? WiFi is no good, next time you replace your router, the default key will change. Possibly the encryption method too and then your connectivity is gone. Ethernet's better, but again, how long will what's in your house (or your next one) be compatible with the Arduino's comms.

Maybe you can use the radio clock signal that transmits the time. NIST provides it in the states. Oh wait, what's this - the current administration is cutting their budget and there are suggestions that GPS makes it redundant. Also, it's not worldwide - other countries have their own, I assume on a different frequency.

Ok, GPS then. Well sure. But don't forget it won't function indoors and then you have another piece of hardware that'll fail eventually, to say nothing of the expected longevity of GPS as it exists today.

Maybe you can resolve the hardware issue partially by making a box full of these things and just start another when you have a failure. Maybe you have to tell it the date and time when it starts via keypad. Then perhaps you give it an LDR and a view out of a window and when it gets light, adjust the time to compensate for drift. Not foolproof at all, but the only thing I can think of that will last long enough will be astronomical means.

TheMemberFormerlyKnownAsAWOL:
A 32 bit counter will count 136 years-worth of seconds.

But many systems have been mis-programmed to consider it a signed 32 bit number and represent dates prior to the 1970 epoch. Seemed like a good idea at the time. :cold_sweat:

Noted the concerns with longevity of the display. :astonished:

Paul__B:
But many systems have been mis-programmed to consider it a signed 32 bit number and represent dates prior to the 1970 epoch. Seemed like a good idea at the time. :cold_sweat:

Oh yeah, the 2038 rollover.
Let’s hope it’s another Y2K non-event.

With the unix timestamp approach the 32bit value of the unix timestamp will overflow when she is 18

Sure, but it doesn't matter. Put the 32 bit timestamp value representing when she was born into a uint32_t and subtract from the current value to get the number of seconds since she was born. It will still work after rollover for the same reason millis works after it rolls over.

Thanks so much guys for all your suggestions, thoughts and ideas! :slight_smile:

I should have mentioned how I would like to design the device a bit more: I should be as simple as possible.
It should be a either an ATMega32 or an ATTiny (I suspect this little guy has not the capability to do the heavy lifting of the externals but I would do a bit research if it is possible maybe. Here its justs about the tracking of the time elapsed).

  • ATMega32 (arduino-mini-pro or standalone?) or ATTiny85 (probably not, see above)
  • The display is a simple 0.96’ OLED.
  • RTC would be a DS3231 on a HW-84 board.
  • Small wooden case
  • Buttons for setting the current date needs to be there to
  • Lipo!? Best maybe with solar recharging capabilities? Who knows what connectors will be available in 50 years?! Also: replacing the RTC backup battery should be as simple as possible (also rechargeable?)

The device is allways “off” by design. There is only one button (no switch) to activate (power on) the box. The display will be on for maybe 30 seconds (or whatever). After that the controller will cut its own power supply with a transistor or something like that (need to evaluate the design exactly on a protoboard first of course).
The powersupply for the device and the backup for the RTC gives me another headaches. Not sure what would be the best design to be future proof.

Sure, but it doesn’t matter. Put the 32 bit timestamp value representing when she was born into a uint32_t and subtract from the current value to get the number of seconds since she was born. It will still work after rollover for the same reason millis works after it rolls over.

Really? But what will happen when I subtract the current timestamp from the birthday timestamp when the 32bit value overflows? Because It could be something like that exactly at rollover: 0 - 1597473720; because then I have -1597473720 which is wrong of course. A second before It would have been: 2147483647 - 1597473720 = 550009927

Do I have to implement a mecanism to track that it overflowed (one time) maybe and add that value to the current timestamp (all in an uint32_t)?

(0 + (2147483647 * 1)) - 1597473720

Maybe just:

uint32_t birthdayTimestamp = 1597473720
uint32_t secondsSinceBirthday = currentTimestamp - birthdayTimestamp 

if(secondsSinceBirthday < 0)
{
 secondsSinceBirthday = currentTimestamp + 2147483647 - birthdayTimestamp
}

This will work only one time but maybe this is enough :wink: But somehow this bothers me, that its not working for an “infinit” number of overflows… :smiley:

Your example uses signed arithmetic, that is why it fails. Unsigned arithmetic will wrap cleanly if it's coded with subtraction.

Anyway, the Arduino time library uses unsigned time_t values, and so does not expire in 2036 (it just gave up the ability to deal with dates prior to 1970-ish). It's sometime after 2100, I forget the exact date.

Your example uses signed arithmetic, that is why it fails. Unsigned arithmetic will wrap cleanly if it's coded with subtraction.

Could you supply an example? :slight_smile:

Anyway, the Arduino time library uses unsigned time_t values, and so does not expire in 2036 (it just gave up the ability to deal with dates prior to 1970-ish). It's sometime after 2100, I forget the exact date.

Which library do you mean? And does it work with the RTC mentioned?

Really?

Really.
As aarg suggested. My homework for you is to search this site for the explanation of why it works for millis, which wraps cleanly every 49 days. It's exactly the same except millis is 1000 times faster.

asuryan:
Could you supply an example? :slight_smile:

Which library do you mean? And does it work with the RTC mentioned?

unsigned long secondsAlive = now - birthDate;

Yes, the official DateTime library (it's listed) does work with an RTC if you want.

asuryan:
With the unix timestamp approach the 32bit value of the unix timestamp will overflow when she is 18 (2038). Allthough this would also be symbolic, I would love to know that this piece would run for at least 2120 (she would be 100 years then :wink: )

Any ideas to solve this?

To track time that far into the future (2120, one hundred +years from now) definitely has some challenges.
For trying to get something that will work that far out, you have to keep things really simple.
I’d be a bit concerned about an OLED display.
I’ve had devices that started to have some pixel failures after a few years, like 6-8.
But at least is isn’t having to deal with the issues facing Jeff Bezo’s 10,000 year clock.

The 32 value issue you are referring to is known as the year 2038 problem or the Y2k38 problem.

I believe that this will be a MUCH bigger issue than y2k.
Y2k was just a date storage issue. While it could create some issues, it wasn’t as bad as an actual time tracking issue like Y2k38. Particularly since many of the issues will occur in customer owned embedded devices.
A few years back it already started to create issues in the financial space,
as they were bumping into it for things like 30 year mortgages.
I predict there will be quite a few IoT “Smart” device issues in 2038.
Thermostats, TVs, sprinkler systems, etc…
But in some cases, the device may start to fail at Jan 1, 2100 before the Y2k38 2106 date as it may have an internal RTC chip that doesn’t support dates beyond Dec 31, 2100.

For all you guys thinking it can be handled similar to the will millis() differentials is handled,…
Well it isn’t that simple.
The reason is millis() only uses a simple unsigned counter.
When using an RTC, it isn’t that simple since the RTC does not use a simple counter.
The RTC is tracking a broken down time which has to be converted to an integer value by the time library s/w.
Those conversions and calculations in the RTC and/or time library library s/w can break down that far into the future.

There are some potential issues that far into the future for both the RTC and the time library s/w.

#1, is that the DSD3231 datasheet says it has “leap-year compensation up to 2100”
Then what? It may not work correctly.
If so, then there may be a leap year issue which will throw off the date which will throw off
the time_t conversion by the time library s/w even if the time library does not have a y2k38 issue.
You may want to try testing the RTC to see what happens on dates that far into the future.
It may have some internal y2k issues as the chip only tracks 2 digits for year.
There is a “Century” bit when year rolls over from 99 to 00 but then what does it do? Does it really work?
So the RTC may not be able to properly handle dates beyond dec 31, 2099.
It would be interesting to see if it still gets the date and days of week correct after a rollover when the century bit is set.
But if it did, why would they say leap year compensation up to 2100, instead of 2199 ?
note:
I just looked at two DS3231 Arduino libraries. 2 of the 3 did not handle/support the century bit.

#2 the time library functions can have limitations in them that won’t properly convert dates that far into the future.
It could be leap year issues like the RTC chip, or its lookup tables, or in some cases I’ve seen code that cheats to make it simpler because it “knows” the device won’t exist that far into the future.
There are also issues if the time library code uses signed 32 bit values for its calculations.
Things get wonky and start to miscalculate the time_t value once going beyond jan 2106.

I have no idea how to work around an RTC leap year issue,
Ignoring that, there are some solutions for you for the time library s/w,
particularly if you can accept only getting to 2106 vs 2120.

If you use the Arduino TimeLib/Time library you can get to 2106 since it uses an unsigned 32 bit integer
You can’t track time before 1970 but you don’t need that.

If you want to get to 2120 and beyond, there are some other alternatives.

The gnu time functions that come with the current AVR libC library have been modified in two ways to deal with Y2K38.
Their modifications have pluses an minuses.
IMO, they did one thing right and one good or bad depending on your point of view. (bad IMO).
They have switched to a 32 bit unsigned time_t value, which is good since that gets you to 2106
The other modification was to move the time_t epoch from 1970 to 2000.
This bumps out the rollover date from 2106 to 2136.
From the AVR libc manual:
https://www.nongnu.org/avr-libc/user-manual/group__avr__time.html

time_t represents seconds elapsed from Midnight, Jan 1 2000 UTC (the Y2K ‘epoch’). Its range allows this implementation to represent time up to Tue Feb 7 06:28:15 2136 UTC.

This makes the time_t values in the AVR libC time functions not directly compatible with unix time_t values.
IMO, this bad and wrong since they are providing a unix time library API but not using unix time_t values/epoch which means you know have to know whether you have a AVR libC time_t value or a unix time_t value when using their time functions. This is a big issue if moving / comparing timestamps across systems.
For your specific case, it might be good as it gets you out to your desired 2120 date and beyond.

You could also switch to another Arduino platform that uses 64 bit time_t values.
Currently the only ones I know of that uses 64 bit time_t values are the esp32 and the STM32 platforms.
Of those, I much prefer the esp32 as there are fewer platform issues especially for things like i2c.


Simplest case for you, if you are using AVR and plan to stick with that environment, would be to simply use the AVR libC time functions/API. It provides the unix time functions you will need like mktime(), brktime(), difftime() etc.
Then just abuse the library API by tracking local time rather than setting the clock to proper epoch based at UTC / GMT.
This simplifies things quite a bit.
i.e. you put local time in the RTC rather than UCT and you simply treat the time as if you were in london.
That way you don’t have to deal with any timezone related conversions / issues.
If gives you the wrong time_t value since it is offset based on your time zone but it won’t matter if the local timezone is the same timezone as the birth timezone.
note:
Many people don’t understand how time_t values are supposed to work.
The time_t value does not represent a time but rather is an offset value from a specified epoch which is a specified time at UTC. It is not adjusted for locality.
i.e the time_t value at any point in time, is the same at every point on the planet.
Example:
Even though it may be noon in NYC and 6pm in London, the time_t value in both locations is exactly the same.

Now, if you want to support the current local timezone being different than the birth timezone, things will start to complicated very quickly. As then you will have to handle all the timezone conversions and deal with DST offsets.
The AVR libC time functions don’t handle time DST offsets for you.

i.e. the daughter was born in PST at noon, but you want the correct offset now that she is living in NYC or even Paris, FR.
To support that requires dealing with timezones and potentially DST offsets depending on how accurate you want to get.

The esp8266 & esp32 code supports timezones and DST offsets for local times. They allow you create a TZ environment variable which is is a POSIZ timezone string that specifies a timezone offset and when DST changes are.
But keep in mind that DST change dates are set by politicians and change from time to time, so would need a way to configure/change it.

To really handle timezones and DST changes correctly can get complicated pretty quickly.

— bill

Thanks so much for your input and the very detailed information/thoughts!

Maybe I will switch to the esp32. Maybe this will give me a bit more options to solve the requirements.
But I think 2106 would be enough for me. :slight_smile:

I will build a small test device to get a better feeling of the important aspects of the project! :slight_smile:

Thanks so far!

Maybe something like this would work. It won’t give correct values near leap days but it should be close enough. I would add some internal buttons for setting date and time in case the RTC or its battery ever fails.

const int BirthYear = 2020;
const int BirthMonth = 8;
const int BirthDay = 24;
const int BirthHour = 12;
const int BirthMinute = 10;
const int BirthSecond = 40;


int DaysPerMonth[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};


// DS3231_Serial_Hard
// Copyright (C)2015 Rinky-Dink Electronics, Henning Karlsen. All right reserved
// web: http://www.RinkyDinkElectronics.com/
//
// A quick demo of how to use my DS3231-library to
// retrieve time- and date-data for you to manipulate.
//
// To use the hardware I2C (TWI) interface of the Arduino you must connect
// the pins as follows:
//
// Arduino Uno/2009:
// ----------------------
// DS3231:  SDA pin   -> Arduino Analog 4 or the dedicated SDA pin
//          SCL pin   -> Arduino Analog 5 or the dedicated SCL pin
//
// Arduino Leonardo:
// ----------------------
// DS3231:  SDA pin   -> Arduino Digital 2 or the dedicated SDA pin
//          SCL pin   -> Arduino Digital 3 or the dedicated SCL pin
//
// Arduino Mega:
// ----------------------
// DS3231:  SDA pin   -> Arduino Digital 20 (SDA) or the dedicated SDA pin
//          SCL pin   -> Arduino Digital 21 (SCL) or the dedicated SCL pin
//
// Arduino Due:
// ----------------------
// DS3231:  SDA pin   -> Arduino Digital 20 (SDA) or the dedicated SDA1 (Digital 70) pin
//          SCL pin   -> Arduino Digital 21 (SCL) or the dedicated SCL1 (Digital 71) pin
//
// The internal pull-up resistors will be activated when using the
// hardware I2C interfaces.
//
// You can connect the DS3231 to any available pin but if you use any
// other than what is described above the library will fall back to
// a software-based, TWI-like protocol which will require exclusive access
// to the pins used, and you will also have to use appropriate, external
// pull-up resistors on the data and clock signals.
//


#include <DS3231.h>


// Init the DS3231 using the hardware interface
DS3231  rtc(SDA, SCL);


// Init a Time-data structure
Time  t;


void setup()
{
  // Setup Serial connection
  Serial.begin(115200);
  // Uncomment the next line if you are using an Arduino Leonardo
  //while (!Serial) {}


  // Initialize the rtc object
  rtc.begin();


  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(WEDNESDAY);     // Set Day-of-Week to SUNDAY
  //rtc.setTime(12, 0, 0);     // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(1, 1, 2014);   // Set the date to January 1st, 2014
}


void loop()
{
  // Get data from the DS3231
  t = rtc.getTime();


  int ElapsedYears = t.year - BirthYear;
  int ElapsedMonths = t.mon - BirthMonth;
  int ElapsedDays = t.date - BirthDay;
  int ElapsedHours = t.hour - BirthHour;
  int ElapsedMinutes = t.min - BirthMinute;
  int ElapsedSeconds = t.sec - BirthSecond;


  if (ElapsedSeconds < 0)
  {
    ElapsedSeconds += 60;
    ElapsedMinutes--;
  }


  if (ElapsedMinutes < 0)
  {
    ElapsedMinutes += 60;
    ElapsedHours--;
  }


  if (ElapsedHours < 0)
  {
    ElapsedHours += 24;
    ElapsedDays--;
  }


  if (ElapsedDays < 0)
  {
    ElapsedDays += DaysPerMonth[t.mon];
    ElapsedMonths--;
  }


  if (ElapsedMonths < 0)
  {
    ElapsedMonths += 12;
    ElapsedYears--;
  }


  // Send date over serial connection
  Serial.print("Elapsed time: ");
  Serial.print(ElapsedYears);
  Serial.print('-');
  Serial.print(ElapsedMonths);
    Serial.print('-');
  Serial.print(ElapsedDays);
    Serial.print(' ');
  Serial.print(ElapsedHours);
    Serial.print(':');
  Serial.print(ElapsedMinutes);
    Serial.print(':');
  Serial.println(ElapsedSeconds);


  // Wait one second before repeating :)
  delay (1000);
}

asuryan:
Thanks so much for your input and the very detailed information/thoughts!

Maybe I will switch to the esp32. Maybe this will give me a bit more options to solve the requirements.
But I think 2106 would be enough for me. :slight_smile:

I just went back and run my test sketch to test the size of time_t and how it handles beyond bit 31 just to make sure about the size of the time_t.
(I had recently been testing many platforms as I have an existing clock project that I want to keep running beyond 2038)
I am so sorry I incorrectly remembered the esp32 time library.
The esp32 time library is the same as the the esp8266.
They both use signed 32 bit time_t values so they break in 2038.
For STM32, the official STM32 platform uses 64 bit time_t at least on the Nucleo 64 board, which is the only board I can test with core since it doesn't support serial downloading on the blue pill board on linux and even then you have to have some sort of magic device made by STM to do serial downloading.
The Roger Clark STM32 platform uses 32 bit time_t values on the blue pill board, but I can't test it on the Nucleo 64 as that platform does not support the Nucleo 64.

The pic32 platform uses 32 bit time_t values.

So the only platform that I have tested so far that uses 64 bit time_t is the official STM32 platform.

The libC time library that comes with avr-gcc gets you to 2136.
And the Arduino TimeLib/Time library will get you to 2106.

But it sounds like the limiting factor may be the RTC chip.

If there is a 2100 issue for the RTC chip, you could use the RTC chip for nothing more than a interrupt clock base.
i.e. don't use the RTC date/time registers and instead use the DS3231 to generate a 1 sec interrupt pulse on the /INT/SQW output pin.
And use the interrupt to drive the time keeping.
Old school way like the original machines used for unix.

--- bill

Assuming we are still using electricity
Assuming the Arduino is still alive
Assuming motors are still running
Assuming there is a global time
Assuming society does not collapse

We know the longest day of the year
We know progression and retrogression of the Sun.

Assuming you can follow the sunrise and set you can definitely know the day of the yeay.
You can count days with an int
Count years with an int

Assuming a global A.I. is soon to be uleashed maybe the programming can be on the web. And locally, the unit just follows the commands ?

Someone’s got this problem covered.