Issues with time_t in printf()

mborgerson

Well-known member
I was using printf() to show some intermediate results in a data collection program. I found that using printf() to show the time and two other variables didn't work as expected:


var1 = 1; var2 = 2;
Serial.printf("now: %lu var1: %u var2: %u\n", now(),var1,var2);
produced: "now: 1725965454 var1: 0 var2: 1"

Something about calling now() inside printf was messing up the following variable, so I put together the following program which illustrates the issue:

Code:
#include "TimeLib.h"

uint16_t var1, var2;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  setSyncProvider(get_TeensyTime);  // Use the time provided from loader
  delay(2000);
  Serial.println("\n\nTesting now() function in printf");
  var1 = 1;
  var2 = 2;
  Serial.printf("Starting with var1: %u   var2: %u\n", var1, var2);
}

void loop() {
  delay(2000);
  Serial.println("\n");
  Serial.printf("now: %lu\n", now());
  Serial.printf("var1: %u   var2: %u\n", var1, var2);
  Serial.printf("now: %lu  var1: %u   var2: %u\n", now(),var1,var2);
  uint32_t lnow = now();
  Serial.printf("lnow: %lu  var1: %u   var2: %u\n", lnow,var1,var2);
  time_t tnow = now();
  Serial.printf("tnow: %lu  var1: %u   var2: %u\n", tnow,var1,var2);
  Serial.printf("(uint32_t)now(): %lu  var1: %u   var2: %u\n", (uint32_t)now(),var1,var2);
  Serial.printf("(time_t)now():   %lu  var1: %u   var2: %u\n", (time_t)now(),var1,var2);
}

time_t get_TeensyTime(void) {
  return Teensy3Clock.get();
}

Running this code showed anomalous values in three cases, which seems to show that printf() doesn't know how to handle the time_t data type and something gets messed up in its internal parameter stack. I thought that Arduino defined time_t to be an unsigned long. What am I missing?

Code:
Testing now() function in printf
Starting with var1: 1   var2: 2

now: 1725965974
var1: 1   var2: 2
now: 1725965974  var1: 0   var2: 1     <--------------
lnow: 1725965974  var1: 1   var2: 2
tnow: 1725965974  var1: 0   var2: 1    <--------------
(uint32_t)now(): 1725965974  var1: 1   var2: 2
(time_t)now():   1725965974  var1: 0   var2: 1   <--------------

now: 1725965976
var1: 1   var2: 2
now: 1725965976  var1: 0   var2: 1    <--------------
lnow: 1725965976  var1: 1   var2: 2
tnow: 1725965976  var1: 0   var2: 1   <--------------
(uint32_t)now(): 1725965976  var1: 1   var2: 2
(time_t)now():   1725965976  var1: 0   var2: 1   <--------------
 
Technically now() wasn't called inside printf - all the arguments are computed first, then the call is made. I suspect this is because time_t is a wider type than you expected, ie long long. Try %llu instead of %lu ??
Did you turn on compiler warnings and check them?
 
Technically now() wasn't called inside printf - all the arguments are computed first, then the call is made. I suspect this is because time_t is a wider type than you expected, ie long long. Try %llu instead of %lu ??
Did you turn on compiler warnings and check them?
I added a line to my test program to print the sizeof(time_t). It returned 8, so you are correct that the something in the current compiler or libraries is setting time_t to a long-long 64-bit variable type. All the versions of time.h, TimeLib.h, etc. I've looked at to show time_t as an unsigned long. or uint32_t. The only time I saw long-long for time_t was in reference to the Windows OS.

Could this be something that changed with the version 11 compiler?
 
Could this be something that changed with the version 11 compiler?
Yes, though I'm not sure in which version the change occurred. If I understand correctly, it's a configuration choice in the tools, but perhaps the default configuration changed in 11.
 
Last edited:
Either newlib or the newer version of gcc (I forget which is responsible) uses 64-bits for time_t, because we're getting pretty close to the point in time where a signed integer count of the number of seconds passed since the beginning of 1970 will no longer fit in a 32-bit value.
 
Some time in 2038 I think for 2^31 seconds past 1970... With 64 bits you can have microsecond precision and last 250 thousand years or so...
 
The printf() functions should normally warn you when the parameter type doesn’t match, for example “%lu” and time_t, which should be “%llu”. However, the Teensyduino implementation of the Print interface (which contains a printf(), and is the one called by Serial.printf()) doesn’t enable this because the theory is there’s too many warnings. (I disagree with this decision.)

May I suggest using printf() instead of Serial.printf()? The standard library should have these warnings enabled. (Serial.printf() uses the definition from the Print class; the non-qualified printf() is the one from the standard library.)

Note: std::printf() (if you include stdio.h) will also work.
 
Last edited:
Back
Top