Time
Like display and touchscreen, time is
not a Home Assistant entity type - it’s a hardware/utility base that provides the device’s wall-clock time. A time
platform’s only job is to figure out the current time (from an RTC chip, SNTP, Home Assistant, GPS, and so on) and
make it available to the rest of the device - via now(), via on_time/cron-style automations, or simply by other
components calling id(my_time).now().
Like the entity types, time is a platform base: individual components (for example sntp, ds1307 or
homeassistant.time) register a real-time clock and implement the code that actually obtains the time.
Python
Section titled “Python”A time platform lives in a time.py file (or a time/__init__.py package) inside your component’s directory,
allowing the user to configure it under the top-level time: block:
time: - platform: my_component timezone: "Australia/Sydney" on_time: - seconds: 0 minutes: 0 hours: 0 then: - logger.log: "It's midnight!"The typical imports and class declaration look like this:
import esphome.codegen as cgimport esphome.config_validation as cvfrom esphome.components import time as time_
my_time_ns = cg.esphome_ns.namespace("my_time")MyTime = my_time_ns.class_("MyTime", time_.RealTimeClock)time_.RealTimeClock already inherits cg.PollingComponent, so no separate component base is needed. The component
is imported as time_ to avoid shadowing Python’s own time module.
Configuration schema
Section titled “Configuration schema”Extend the shared time_.TIME_SCHEMA, which provides the timezone option and the on_time / on_time_sync
automations, together with update_interval (defaulting to 15 minutes) via cv.polling_component_schema:
CONFIG_SCHEMA = time_.TIME_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(MyTime), })As with the display and touchscreen schema helpers, TIME_SCHEMA doesn’t take your class as an argument, so you
supply your own cv.GenerateID().
Code generation
Section titled “Code generation”In to_code, register the platform with time_.register_time(). Unlike sensor.new_sensor() or
display.register_display(), this does not call cg.register_component() for you - you must do that yourself -
but it does generate the code for timezone and the on_time/on_time_sync automations:
async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await time_.register_time(var, config)The C++ class inherits from time::RealTimeClock:
#include "esphome/components/time/real_time_clock.h"
namespace esphome::my_time {
class MyTime : public time::RealTimeClock { public: void update() override;};
} // namespace esphome::my_timeReporting the time
Section titled “Reporting the time”Whenever you obtain a fresh reading of the current time - from an RTC register read, an SNTP callback, an API
message from Home Assistant, etc. - call the protected synchronize_epoch_(epoch) with the Unix epoch (UTC
seconds since 1970-01-01):
void MyTime::update() { uint32_t epoch = this->read_epoch_from_hardware_(); this->synchronize_epoch_(epoch);}synchronize_epoch_() sets the device’s system clock (skipping the write if it’s already within a second of the
given value, to avoid clock jitter and log spam) and fires the on_time_sync trigger/add_on_time_sync_callback()
listeners. You never need to apply the user’s timezone: yourself - the base class parses and applies it during
setup, and every other component that reads the time goes through RealTimeClock::now(), which already returns
timezone-local values.
Not every platform needs to override update() at all: some (like sntp, which delegates to the underlying OS’s
own SNTP client) only need setup() to kick off the sync mechanism, and call synchronize_epoch_()/rely on the
system clock being set for them elsewhere.
Useful members
Section titled “Useful members”now(): the current time in the configured timezone, as anESPTime(year/month/day/hour/minute/second plus day-of-week/day-of-year and anis_valid()check).utcnow(): the current time with no timezone/DST correction applied.timestamp_now(): the raw Unix epoch (time_t) - what you’d pass back intosynchronize_epoch_().add_on_time_sync_callback(F &&): register a callback to run whenever this clock successfully synchronizes - useful for components that need to defer work until real time is available.
Other components read the current time by depending on time and calling now() on the configured time_id, most
commonly through the time.has_time condition or an
on_time automation, rather than polling a platform directly.
For everything else, the component implements the usual set of methods as described here.
Copyright © 2026 ESPHome - A project from the Open Home Foundation