Teensy 4.0 Audio Shield SD incorrect file timestamp

Hope someone can help me figure this out.

Teensy 4.0 + Audio Shield
Arduino IDE 2.3.2
Teensy Loader 1.59
Windows 10 IoT Enterprise (also tried on Windows 11 pro with same results)

TimeLib successfully syncs with RTC and displays correct time but timestamp on files and folders when writing to the SD card is one day behind.

I tested it with and without FsDateTime::setCallback(dateTime) callback with same results.

I know dateTime callback function is called because it prints the correct day().

I thought this used to work when I started my project back in December, but I could be wrong.

Here's my test code. It prints the correct date and time every 5 seconds. Manually creating a folder gets incorrect timestamp.

C++:
#include <SD.h>
#include <TimeLib.h>
#include <MTP_Teensy.h>

#define SDCARD_CS_PIN 10

uint32_t delayStart = millis();

void setup() {
  Serial.begin(9600);
  while (!Serial && millis() < 5000) {
    // wait for serial port to connect.
  }

  // Initialize SD card
  if (!SD.begin(SDCARD_CS_PIN))
    Serial.println("SD card: Failed");
  else
    Serial.println("SD card: OK");

  // Begin MTP session
  MTP.begin();

  // Add SD Card
  MTP.addFilesystem(SD, "Media");

  // Synchronise the Time object used in the program code with the RTC time provider.
  // See https://github.com/PaulStoffregen/Time
  setSyncProvider(getTeensy3Time);

  if (timeStatus() != timeSet)
    Serial.println(F("Unable to sync with RTC"));
  else
    Serial.println(F("RTC has set system time"));

  // Define a callback that will assign the correct datetime for any file system operations
  FsDateTime::setCallback(dateTime);
}

void loop() {
  MTP.loop();
 
  if ((millis() - delayStart) > 5000) {
    printDateTime();
    
    delayStart = millis();
  }
}

// Retrieve the current time from Teensy built-in RTC
time_t getTeensy3Time() {
  return Teensy3Clock.get();
}

// Callback to assign timestamps for file system operations
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {

  Serial.print("Day: ");
  printDigits(day());
  Serial.println();

  // Return date using FS_DATE macro to format fields.
  *date = FS_DATE(year(), month(), day());

  // Return time using FS_TIME macro to format fields.
  *time = FS_TIME(hour(), minute(), second());

  // Return low time bits in units of 10 ms.
  *ms10 = second() & 1 ? 100 : 0;
}

void printDateTime() {
  printDigits(month());
  Serial.print("/");
  printDigits(day());
  Serial.print("/");
  Serial.print(year());
  Serial.print(" ");
  printDigits(hour());
  Serial.print(":");
  printDigits(minute());
  Serial.print(":");
  printDigits(second());
  Serial.println();
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}
 
Have you actually taken the card out and connected it directly to a PC to check the dates, rather than relying on MTP?

You shouldn't need TimeLib or set the FsDateTime callback manually, the SD library does that automatically.
 
Have you actually taken the card out and connected it directly to a PC to check the dates, rather than relying on MTP?

You shouldn't need TimeLib or set the FsDateTime callback manually, the SD library does that automatically.
Thanks for the suggestion. I took the card out and lo and behold - timestamp is correct all along.

Is this a known bug?

In my application, the card is inside a device and is not accessible to the user, which is why I'm using MTP.
 
No, because setSyncProvider is a utility function for TimeLib - it tells it where to pull the current time from. If you get rid of TimeLib you won't need to tell it what clock source to use.

The SD library already includes a dateTime callback that it registers when you call SD.begin().
That's only true if you are using the built in RTC not a more accurate and less power hungry external one.
 
Which is literally the case here.
Yes but your original statement would lead readers to believe that it was RIGHT for ALL cases.
It could cause readers to produce code which was not working correctly for them and be unclear why.
You shouldn't need TimeLib or set the FsDateTime callback manually, the SD library does that automatically.
No statement that this is ONLY true when using the built in RTC.
 
To reiterate my first post, I also tested without using the FsDateTime callback and it also produced incorrect file timestamp.

I'm using the FsDateTime callback to verify the day() (which is correct) just before any SD file operation.

I need TimeLib to verify and display correct date for debugging.
 
I guess I'm not crazy for thinking that this used to work last December.
Last year was an odd numbered year so it was safe. I believe the bug offsets all leap years by two, so any dates after the end of February in even numbered years will be incorrect.
 
Using FsDateTime callback as workaround.

My understanding is that this is a leap year issue, so not sure if checking for even numbered years is necessary.

C++:
// Callback to assign timestamps for file system operations
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {

  //file timestamp bug workaround
  //https://forum.pjrc.com/index.php?threads/teensy-4-0-audio-shield-sd-incorrect-file-timestamp.75002/
  //https://github.com/PaulStoffregen/cores/pull/742
  time_t tStamp = now();
  if ((year() % 4 == 0 && year() % 100 != 0 || year() % 400 == 0) && year() % 2 == 0 && month() > 2) {
    struct tm* tm = localtime(&tStamp);
    tm->tm_mday += 1;
    tStamp = mktime(tm);
  }

  // Return date using FS_DATE macro to format fields.
  *date = FS_DATE(year(tStamp), month(tStamp), day(tStamp));

  // Return time using FS_TIME macro to format fields.
  *time = FS_TIME(hour(), minute(), second());

  // Return low time bits in units of 10 ms.
  *ms10 = second() & 1 ? 100 : 0;
}
 
For even numbered years that aren't leap years, the bug goes the other way (because the code thinks it is a leap year):

Example:
Code:
void printDateTime() {
  uint32_t now;
  DateTimeFields dt;
  now = 1778422435; // 2026-05-10 14:13:55
  breakTime(now, dt);
  Serial.printf("%04u-%02u-%02u %02u:%02u:%02u", dt.year+1900, dt.mon+1, dt.mday, dt.hour, dt.min, dt.sec);
  now = makeTime(dt); // <- THIS IS BROKEN
  Serial.printf(" -> %u -> ", now);
  breakTime(now, dt);
  Serial.printf("%04u-%02u-%02u %02u:%02u:%02u", dt.year+1900, dt.mon+1, dt.mday, dt.hour, dt.min, dt.sec);
  Serial.println();
}
void setup() {
  Serial.begin(0);
  while (!Serial);
  printDateTime();
 }
void loop() {}
 
 // output: "2026-05-10 14:13:55 -> 1778508835 -> 2026-05-11 14:13:55"
 // makeTime gains one day in non-leap even years for dates after the leap day
 
For even numbered years that aren't leap years, the bug goes the other way (because the code thinks it is a leap year):

Got it.

Updated code, if anyone is looking for a workaround.

I will be delivering this project next week so I can't wait for the Teensyduino patch.

C++:
// Callback to assign timestamps for file system operations
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {

  //file timestamp bug workaround
  //https://forum.pjrc.com/index.php?threads/teensy-4-0-audio-shield-sd-incorrect-file-timestamp.75002/
  //https://github.com/PaulStoffregen/cores/pull/742
  time_t tStamp = now();
  struct tm* tm = localtime(&tStamp);

  if ((year() % 4 == 0 && (year() % 100 != 0 || year() % 400 == 0)) && month() > 2) {
    tm->tm_mday += 1;
    tStamp = mktime(tm);
  }
  else if (year() % 2 == 0 && month() > 2) {
    tm->tm_mday -= 1;
    tStamp = mktime(tm);
  }

  // Return date using FS_DATE macro to format fields.
  *date = FS_DATE(year(tStamp), month(tStamp), day(tStamp));

  // Return time using FS_TIME macro to format fields.
  *time = FS_TIME(hour(), minute(), second());

  // Return low time bits in units of 10 ms.
  *ms10 = second() & 1 ? 100 : 0;
}
 
Back
Top