From d41980d0d2aa9618b41ec5f9fb22f146e81683b5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 18 Jan 2026 23:06:17 -1000 Subject: [PATCH 1/3] [datetime] Add const char * overloads for string parsing to avoid heap allocation --- esphome/components/datetime/date_entity.cpp | 2 +- esphome/components/datetime/date_entity.h | 3 +- .../components/datetime/datetime_entity.cpp | 2 +- esphome/components/datetime/datetime_entity.h | 3 +- esphome/components/datetime/time_entity.cpp | 2 +- esphome/components/datetime/time_entity.h | 3 +- esphome/core/time.cpp | 33 ++++++++++--------- esphome/core/time.h | 15 +++++++-- 8 files changed, 38 insertions(+), 25 deletions(-) diff --git a/esphome/components/datetime/date_entity.cpp b/esphome/components/datetime/date_entity.cpp index c5ea051914..c0851a9f9f 100644 --- a/esphome/components/datetime/date_entity.cpp +++ b/esphome/components/datetime/date_entity.cpp @@ -106,7 +106,7 @@ DateCall &DateCall::set_date(uint16_t year, uint8_t month, uint8_t day) { DateCall &DateCall::set_date(ESPTime time) { return this->set_date(time.year, time.month, time.day_of_month); }; -DateCall &DateCall::set_date(const std::string &date) { +DateCall &DateCall::set_date(const char *date) { ESPTime val{}; if (!ESPTime::strptime(date, val)) { ESP_LOGE(TAG, "Could not convert the date string to an ESPTime object"); diff --git a/esphome/components/datetime/date_entity.h b/esphome/components/datetime/date_entity.h index 069116d162..60aca059a9 100644 --- a/esphome/components/datetime/date_entity.h +++ b/esphome/components/datetime/date_entity.h @@ -67,7 +67,8 @@ class DateCall { void perform(); DateCall &set_date(uint16_t year, uint8_t month, uint8_t day); DateCall &set_date(ESPTime time); - DateCall &set_date(const std::string &date); + DateCall &set_date(const char *date); + DateCall &set_date(const std::string &date) { return this->set_date(date.c_str()); } DateCall &set_year(uint16_t year) { this->year_ = year; diff --git a/esphome/components/datetime/datetime_entity.cpp b/esphome/components/datetime/datetime_entity.cpp index fd3901fcfc..67eaf3f78e 100644 --- a/esphome/components/datetime/datetime_entity.cpp +++ b/esphome/components/datetime/datetime_entity.cpp @@ -163,7 +163,7 @@ DateTimeCall &DateTimeCall::set_datetime(ESPTime datetime) { datetime.second); }; -DateTimeCall &DateTimeCall::set_datetime(const std::string &datetime) { +DateTimeCall &DateTimeCall::set_datetime(const char *datetime) { ESPTime val{}; if (!ESPTime::strptime(datetime, val)) { ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object"); diff --git a/esphome/components/datetime/datetime_entity.h b/esphome/components/datetime/datetime_entity.h index 018346b34b..3d8f05217c 100644 --- a/esphome/components/datetime/datetime_entity.h +++ b/esphome/components/datetime/datetime_entity.h @@ -71,7 +71,8 @@ class DateTimeCall { void perform(); DateTimeCall &set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); DateTimeCall &set_datetime(ESPTime datetime); - DateTimeCall &set_datetime(const std::string &datetime); + DateTimeCall &set_datetime(const char *datetime); + DateTimeCall &set_datetime(const std::string &datetime) { return this->set_datetime(datetime.c_str()); } DateTimeCall &set_datetime(time_t epoch_seconds); DateTimeCall &set_year(uint16_t year) { diff --git a/esphome/components/datetime/time_entity.cpp b/esphome/components/datetime/time_entity.cpp index d0b8875ed1..f2daa9c0b5 100644 --- a/esphome/components/datetime/time_entity.cpp +++ b/esphome/components/datetime/time_entity.cpp @@ -74,7 +74,7 @@ TimeCall &TimeCall::set_time(uint8_t hour, uint8_t minute, uint8_t second) { TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); }; -TimeCall &TimeCall::set_time(const std::string &time) { +TimeCall &TimeCall::set_time(const char *time) { ESPTime val{}; if (!ESPTime::strptime(time, val)) { ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object"); diff --git a/esphome/components/datetime/time_entity.h b/esphome/components/datetime/time_entity.h index d3be3130b1..8122c95173 100644 --- a/esphome/components/datetime/time_entity.h +++ b/esphome/components/datetime/time_entity.h @@ -69,7 +69,8 @@ class TimeCall { void perform(); TimeCall &set_time(uint8_t hour, uint8_t minute, uint8_t second); TimeCall &set_time(ESPTime time); - TimeCall &set_time(const std::string &time); + TimeCall &set_time(const char *time); + TimeCall &set_time(const std::string &time) { return this->set_time(time.c_str()); } TimeCall &set_hour(uint8_t hour) { this->hour_ = hour; diff --git a/esphome/core/time.cpp b/esphome/core/time.cpp index 4047033f84..554431c631 100644 --- a/esphome/core/time.cpp +++ b/esphome/core/time.cpp @@ -67,7 +67,7 @@ std::string ESPTime::strftime(const char *format) { std::string ESPTime::strftime(const std::string &format) { return this->strftime(format.c_str()); } -bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) { +bool ESPTime::strptime(const char *time_to_parse, size_t len, ESPTime &esp_time) { uint16_t year; uint8_t month; uint8_t day; @@ -75,40 +75,41 @@ bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) { uint8_t minute; uint8_t second; int num; + const int ilen = static_cast(len); - if (sscanf(time_to_parse.c_str(), "%04hu-%02hhu-%02hhu %02hhu:%02hhu:%02hhu %n", &year, &month, &day, // NOLINT - &hour, // NOLINT - &minute, // NOLINT - &second, &num) == 6 && // NOLINT - num == static_cast(time_to_parse.size())) { + if (sscanf(time_to_parse, "%04hu-%02hhu-%02hhu %02hhu:%02hhu:%02hhu %n", &year, &month, &day, // NOLINT + &hour, // NOLINT + &minute, // NOLINT + &second, &num) == 6 && // NOLINT + num == ilen) { esp_time.year = year; esp_time.month = month; esp_time.day_of_month = day; esp_time.hour = hour; esp_time.minute = minute; esp_time.second = second; - } else if (sscanf(time_to_parse.c_str(), "%04hu-%02hhu-%02hhu %02hhu:%02hhu %n", &year, &month, &day, // NOLINT - &hour, // NOLINT - &minute, &num) == 5 && // NOLINT - num == static_cast(time_to_parse.size())) { + } else if (sscanf(time_to_parse, "%04hu-%02hhu-%02hhu %02hhu:%02hhu %n", &year, &month, &day, // NOLINT + &hour, // NOLINT + &minute, &num) == 5 && // NOLINT + num == ilen) { esp_time.year = year; esp_time.month = month; esp_time.day_of_month = day; esp_time.hour = hour; esp_time.minute = minute; esp_time.second = 0; - } else if (sscanf(time_to_parse.c_str(), "%02hhu:%02hhu:%02hhu %n", &hour, &minute, &second, &num) == 3 && // NOLINT - num == static_cast(time_to_parse.size())) { + } else if (sscanf(time_to_parse, "%02hhu:%02hhu:%02hhu %n", &hour, &minute, &second, &num) == 3 && // NOLINT + num == ilen) { esp_time.hour = hour; esp_time.minute = minute; esp_time.second = second; - } else if (sscanf(time_to_parse.c_str(), "%02hhu:%02hhu %n", &hour, &minute, &num) == 2 && // NOLINT - num == static_cast(time_to_parse.size())) { + } else if (sscanf(time_to_parse, "%02hhu:%02hhu %n", &hour, &minute, &num) == 2 && // NOLINT + num == ilen) { esp_time.hour = hour; esp_time.minute = minute; esp_time.second = 0; - } else if (sscanf(time_to_parse.c_str(), "%04hu-%02hhu-%02hhu %n", &year, &month, &day, &num) == 3 && // NOLINT - num == static_cast(time_to_parse.size())) { + } else if (sscanf(time_to_parse, "%04hu-%02hhu-%02hhu %n", &year, &month, &day, &num) == 3 && // NOLINT + num == ilen) { esp_time.year = year; esp_time.month = month; esp_time.day_of_month = day; diff --git a/esphome/core/time.h b/esphome/core/time.h index f6f1d57dbb..1a56e771bd 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -80,11 +80,20 @@ struct ESPTime { } /** Convert a string to ESPTime struct as specified by the format argument. - * @param time_to_parse null-terminated c string formatet like this: 2020-08-25 05:30:00. + * @param time_to_parse c string formatted like this: 2020-08-25 05:30:00. + * @param len length of the string (not including null terminator if present) * @param esp_time an instance of a ESPTime struct - * @return the success sate of the parsing + * @return the success state of the parsing */ - static bool strptime(const std::string &time_to_parse, ESPTime &esp_time); + static bool strptime(const char *time_to_parse, size_t len, ESPTime &esp_time); + /// @copydoc strptime(const char *, size_t, ESPTime &) + static bool strptime(const char *time_to_parse, ESPTime &esp_time) { + return strptime(time_to_parse, strlen(time_to_parse), esp_time); + } + /// @copydoc strptime(const char *, size_t, ESPTime &) + static bool strptime(const std::string &time_to_parse, ESPTime &esp_time) { + return strptime(time_to_parse.c_str(), time_to_parse.size(), esp_time); + } /// Convert a C tm struct instance with a C unix epoch timestamp to an ESPTime instance. static ESPTime from_c_tm(struct tm *c_tm, time_t c_time); From 54a4d60f5d26fc6a04ae18a82bf2b52e84d65f9e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 18 Jan 2026 23:09:24 -1000 Subject: [PATCH 2/3] [datetime] Add const char * overloads for string parsing to avoid heap allocation --- esphome/components/datetime/date_entity.cpp | 4 ++-- esphome/components/datetime/date_entity.h | 5 +++-- esphome/components/datetime/datetime_entity.cpp | 4 ++-- esphome/components/datetime/datetime_entity.h | 7 +++++-- esphome/components/datetime/time_entity.cpp | 4 ++-- esphome/components/datetime/time_entity.h | 5 +++-- 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/esphome/components/datetime/date_entity.cpp b/esphome/components/datetime/date_entity.cpp index c0851a9f9f..3ba488c0aa 100644 --- a/esphome/components/datetime/date_entity.cpp +++ b/esphome/components/datetime/date_entity.cpp @@ -106,9 +106,9 @@ DateCall &DateCall::set_date(uint16_t year, uint8_t month, uint8_t day) { DateCall &DateCall::set_date(ESPTime time) { return this->set_date(time.year, time.month, time.day_of_month); }; -DateCall &DateCall::set_date(const char *date) { +DateCall &DateCall::set_date(const char *date, size_t len) { ESPTime val{}; - if (!ESPTime::strptime(date, val)) { + if (!ESPTime::strptime(date, len, val)) { ESP_LOGE(TAG, "Could not convert the date string to an ESPTime object"); return *this; } diff --git a/esphome/components/datetime/date_entity.h b/esphome/components/datetime/date_entity.h index 60aca059a9..955fd92c45 100644 --- a/esphome/components/datetime/date_entity.h +++ b/esphome/components/datetime/date_entity.h @@ -67,8 +67,9 @@ class DateCall { void perform(); DateCall &set_date(uint16_t year, uint8_t month, uint8_t day); DateCall &set_date(ESPTime time); - DateCall &set_date(const char *date); - DateCall &set_date(const std::string &date) { return this->set_date(date.c_str()); } + DateCall &set_date(const char *date, size_t len); + DateCall &set_date(const char *date) { return this->set_date(date, strlen(date)); } + DateCall &set_date(const std::string &date) { return this->set_date(date.c_str(), date.size()); } DateCall &set_year(uint16_t year) { this->year_ = year; diff --git a/esphome/components/datetime/datetime_entity.cpp b/esphome/components/datetime/datetime_entity.cpp index 67eaf3f78e..730abb3ca8 100644 --- a/esphome/components/datetime/datetime_entity.cpp +++ b/esphome/components/datetime/datetime_entity.cpp @@ -163,9 +163,9 @@ DateTimeCall &DateTimeCall::set_datetime(ESPTime datetime) { datetime.second); }; -DateTimeCall &DateTimeCall::set_datetime(const char *datetime) { +DateTimeCall &DateTimeCall::set_datetime(const char *datetime, size_t len) { ESPTime val{}; - if (!ESPTime::strptime(datetime, val)) { + if (!ESPTime::strptime(datetime, len, val)) { ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object"); return *this; } diff --git a/esphome/components/datetime/datetime_entity.h b/esphome/components/datetime/datetime_entity.h index 3d8f05217c..b5b8cd677e 100644 --- a/esphome/components/datetime/datetime_entity.h +++ b/esphome/components/datetime/datetime_entity.h @@ -71,8 +71,11 @@ class DateTimeCall { void perform(); DateTimeCall &set_datetime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); DateTimeCall &set_datetime(ESPTime datetime); - DateTimeCall &set_datetime(const char *datetime); - DateTimeCall &set_datetime(const std::string &datetime) { return this->set_datetime(datetime.c_str()); } + DateTimeCall &set_datetime(const char *datetime, size_t len); + DateTimeCall &set_datetime(const char *datetime) { return this->set_datetime(datetime, strlen(datetime)); } + DateTimeCall &set_datetime(const std::string &datetime) { + return this->set_datetime(datetime.c_str(), datetime.size()); + } DateTimeCall &set_datetime(time_t epoch_seconds); DateTimeCall &set_year(uint16_t year) { diff --git a/esphome/components/datetime/time_entity.cpp b/esphome/components/datetime/time_entity.cpp index f2daa9c0b5..74e43fbbe7 100644 --- a/esphome/components/datetime/time_entity.cpp +++ b/esphome/components/datetime/time_entity.cpp @@ -74,9 +74,9 @@ TimeCall &TimeCall::set_time(uint8_t hour, uint8_t minute, uint8_t second) { TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); }; -TimeCall &TimeCall::set_time(const char *time) { +TimeCall &TimeCall::set_time(const char *time, size_t len) { ESPTime val{}; - if (!ESPTime::strptime(time, val)) { + if (!ESPTime::strptime(time, len, val)) { ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object"); return *this; } diff --git a/esphome/components/datetime/time_entity.h b/esphome/components/datetime/time_entity.h index 8122c95173..e4bb113eb5 100644 --- a/esphome/components/datetime/time_entity.h +++ b/esphome/components/datetime/time_entity.h @@ -69,8 +69,9 @@ class TimeCall { void perform(); TimeCall &set_time(uint8_t hour, uint8_t minute, uint8_t second); TimeCall &set_time(ESPTime time); - TimeCall &set_time(const char *time); - TimeCall &set_time(const std::string &time) { return this->set_time(time.c_str()); } + TimeCall &set_time(const char *time, size_t len); + TimeCall &set_time(const char *time) { return this->set_time(time, strlen(time)); } + TimeCall &set_time(const std::string &time) { return this->set_time(time.c_str(), time.size()); } TimeCall &set_hour(uint8_t hour) { this->hour_ = hour; From 48e7e7aeb382f1698a3fb2468a6bcdccac9f43d6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 18 Jan 2026 23:18:38 -1000 Subject: [PATCH 3/3] hdr --- esphome/core/time.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/core/time.h b/esphome/core/time.h index 1a56e771bd..87ebb5c221 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include