Datetime ISO String via MQTT in textsensor and on OLED

Hi there :).

Since yesterday, I am playing around with ESPHOME and an ESP8266 which is working great so far. But here is something which I would like to get managed, but nothing is working ;).

Problem:
I receive an iso-formated datetime string (e.g. “2025-03-02T12:08:12.379Z”) via MQTT (which is already working).

> text_sensor:
>   - platform: mqtt_subscribe
>     id: RASP_DATETIME
>     topic: just/a/topic/datetime

And now I would like to format this str into
a) Time (converted from UTC to Europe/Berlin)
b) Date
in order to display it on an OLED:

> display:
>   - platform: ssd1306_i2c
>     model: "SSD1306 128x64"
>     address: 0x3C
>     id: rasp_display
>     pages:
>       - id: page1   
>         lambda: |-
>           it.print(0,0,id(font1), id(RASP_DATETIME).state.c_str()); // is working
>           it.strftime(0, 0, id(font1), "%H:%M", id(RASP_DATETIME).state) // something like this?

Can you help me?

All the best and a nice Sunday!!

You’ll need to use strptime first, to convert from a string to what strftime can work with.

It doesn’t seem to understand ISO time strings as-is, though, so you might need to do some change to the string, first.

https://esphome.io/api/structesphome_1_1_e_s_p_time#a976b42c8d2be9d6373f8632a97a79326

Unfortunately strptime expects local time - and you cannot easily change it to UTC, for conversion back to your local time.

There is a POSIX function you can use called timegm but it’s not implemented on either Arduino or IDF platforms.

I do have a function that does it though - so it can be done. Below yaml will take an ISO DateTime string and convert it to two text sensors - a date and a time, in your local time zone.

First you need to add the function to ESPHome. Create a .h file in the ESPHome directory, in this example it’s called functions.h. Paste the function code into it and save, then add an include to your yaml.

time_t _mkgmtime(const struct tm *tm) 
{
    // Month-to-day offset for non-leap-years.
    static const int month_day[12] =
    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};

    // Most of the calculation is easy; leap years are the main difficulty.
    int month = tm->tm_mon % 12;
    int year = tm->tm_year + tm->tm_mon / 12;
    if (month < 0) {   // Negative values % 12 are still negative.
        month += 12;
        --year;
    }

    // This is the number of Februaries since 1900.
    const int year_for_leap = (month > 1) ? year + 1 : year;

    time_t rt = tm->tm_sec                             // Seconds
        + 60 * (tm->tm_min                          // Minute = 60 seconds
        + 60 * (tm->tm_hour                         // Hour = 60 minutes
        + 24 * (month_day[month] + tm->tm_mday - 1  // Day = 24 hours
        + 365 * (year - 70)                         // Year = 365 days
        + (year_for_leap - 69) / 4                  // Every 4 years is     leap...
        - (year_for_leap - 1) / 100                 // Except centuries...
        + (year_for_leap + 299) / 400)));           // Except 400s.
    return rt < 0 ? -1 : rt;
}
esphome:
  name: webinterface
  includes: 
    - functions.h

logger:

esp32:
  board: esp32dev
  framework:
    type: esp-idf

time:
  - platform: sntp
    id: sntp_time
     
ota:
  platform: esphome
  password: "c1c4c455c995776545f45707eaecc11d"
  
wifi:
 ssid: SARWLAN
 password: !secret wifi_password
 output_power: 8.5db
 
captive_portal:

api:
  reboot_timeout: 0s

globals:
  id: time1
  type: std::string
  initial_value: '"2025-03-02T19:08:12.379Z"'
     
text_sensor:

  - platform: template
    name: "Date from string"
    id: dfs
    lambda: |-
      time_t t;
      struct tm tm;
      strptime(id(time1).c_str(), "%FT%TZ%z", &tm);
      t = _mkgmtime(&tm);
      struct tm *tml = localtime(&t);
      char tout_c[20];
      strftime(tout_c, 20, "%Y-%m-%d", tml);
      std::string tout = tout_c;
      return tout;

  - platform: template
    name: "Time from string"
    id: tfs
    lambda: |-
      time_t t;
      struct tm tm;
      strptime(id(time1).c_str(), "%FT%T%z", &tm);
      t = _mkgmtime(&tm);
      struct tm *tml = localtime(&t);
      char tout_c[20];
      strftime(tout_c, 20, "%H:%M:%S", tml);
      std::string tout = tout_c;
      return tout;

@Spockie - note I changed your example time string to a few hours later so I could more easily test in my time zone.

If your goal is to show local time, you could use a different method, for instance using the SNTP component or using the Home Assistant Time Source component.