From cd4cb8b3ec465c2c9961084d8ff52e119a4e06ee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 20 Jan 2026 17:50:01 -1000 Subject: [PATCH] [datetime] Add const char * overloads for string parsing to avoid heap allocation (#13363) --- esphome/components/datetime/date_entity.cpp | 4 +-- esphome/components/datetime/date_entity.h | 4 ++- .../components/datetime/datetime_entity.cpp | 4 +-- esphome/components/datetime/datetime_entity.h | 6 +++- esphome/components/datetime/time_entity.cpp | 4 +-- esphome/components/datetime/time_entity.h | 4 ++- esphome/core/time.cpp | 33 ++++++++++--------- esphome/core/time.h | 16 +++++++-- 8 files changed, 47 insertions(+), 28 deletions(-) diff --git a/esphome/components/datetime/date_entity.cpp b/esphome/components/datetime/date_entity.cpp index c5ea051914..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 std::string &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 069116d162..955fd92c45 100644 --- a/esphome/components/datetime/date_entity.h +++ b/esphome/components/datetime/date_entity.h @@ -67,7 +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 std::string &date); + 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 fd3901fcfc..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 std::string &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 018346b34b..b5b8cd677e 100644 --- a/esphome/components/datetime/datetime_entity.h +++ b/esphome/components/datetime/datetime_entity.h @@ -71,7 +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 std::string &datetime); + 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 d0b8875ed1..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 std::string &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 d3be3130b1..e4bb113eb5 100644 --- a/esphome/components/datetime/time_entity.h +++ b/esphome/components/datetime/time_entity.h @@ -69,7 +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 std::string &time); + 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; 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..87ebb5c221 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -80,11 +81,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);