The EPIC Time Conversion and Manipulation Thread!

After seeing many threads on the forum asking for help with time conversions and calculations using time and realizing I generally had no real understanding of how time worked in HA I decided to dig in and try to figure it out.

After over a week of internet and forum searches I think I’ve now got a lot of it figured out. I’m sure there is more that I don’t have here because sometimes “you don’t know what you don’t know”.

So, since I didn’t know this stuff and because of the number of other posts asking about this I figured I’d create a thread for a centralized location to put all of this in for (hopefully) easy reference and with an explanation where appropriate/necessary.

I’ll divide up this initial post into different sections dealing with different aspects:

  1. time format conversion directives

  2. strings, datetime objects, timestamps and custom timestamps

  3. strptime

  4. strftime

I have designed and written the following posts to be (I hope…except for the noted exceptions) completely template-editor-friendly so you should be able to just copy the entire post into the template editor under the dev-tool section and follow along with the results.
That will allow you to play around with it to see the results of the changes you make in real time. If you run into an issue please point it out and I’ll try to fix it.

The only exceptions to the above are where I used entities from my own set up to help illustrate how to use the concepts in things directly related to HA. You’ll need to substitute those entities with ones from your setup in the dev-tool template editor for them to render correctly.
I’ve annotated them in bold in the posts where necessary.

All of this is as I’ve tried to understand it but I’m pretty new to these concepts and I am likely to have something wrong so feel free to suggest improvements or additions and I will update the appropriate sections as necessary.

Again, the goal is to have a local place to find information on this subject since spending hours or days searching the 'net can not only be frustrating but also a waste of time since I wasn’t able to find some of this information anywhere I looked.

I hope it makes sense and that you find it useful.

Here is what I’ve learned…

84 Likes

This is the list of time conversion directives that I’ll be using in the rest of these posts.

This list can be found in several locations on the internet. I started with strftime.org

Directive	Meaning	Example	Notes

%a		Weekday as locale’s abbreviated name.	
		Sun, Mon, …, Sat (en_US);
		So, Mo, …, Sa (de_DE)(1)
%A		Weekday as locale’s full name.	
		Sunday, Monday, …, Saturday (en_US);
		Sonntag, Montag, …, Samstag (de_DE)
%w		Weekday as a decimal number, where 0 is Sunday and 6 is Saturday.	0, 1, …, 6	 
%d		Day of the month as a zero-padded decimal number.	01, 02, …, 31
%-d		Day of the month as a decimal number.	1, 2, ...	31
%b		Month as locale’s abbreviated name.	
		Jan, Feb, …, Dec (en_US);
		Jan, Feb, …, Dez (de_DE)
%B		Month as locale’s full name.	
		January, February, …, December (en_US);
		Januar, Februar, …, Dezember (de_DE)
%m		Month as a zero-padded decimal number.	01, 02, …, 12
%-m		Month as a decimal number.  1, 2, ... 12	 
%y		Year without century as a zero-padded decimal number.	00, 01, …, 99	 
%Y		Year with century as a decimal number.	0001, 0002, …, 2013, 2014, …, 9998, 9999
%H		Hour (24-hour clock) as a zero-padded decimal number.	00, 01, …, 23
%-H		Hour (24-hour clock) as a decimal number. 0, 1, 2, ... 23	 
%I		Hour (12-hour clock) as a zero-padded decimal number.	01, 02, …, 12
%-I		Hour (12-hour clock) as a decimal number. 1, 2, ... 12	 
%p		Locale’s equivalent of either AM or PM.	
		AM, PM (en_US);
		am, pm (de_DE)
%M		Minute as a zero-padded decimal number.	00, 01, …, 59
%-M		Minute as a decimal number. 0, 1, 2, .. 59	 (can go to 61 to account for leap seconds)
%S		Second as a zero-padded decimal number.	00, 01, …, 59  (can go to 61 to account for leap seconds)
%-S		Second as a decimal number. 0, 1, 2, .. 59
%f		Microsecond as a decimal number, zero-padded on the left.	000000, 000001, …, 999999
%z		UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive).	(empty), +0000, -0400, +1030
%Z		Time zone name (empty string if the object is naive).	(empty), UTC, EST, CST	 
%j		Day of the year as a zero-padded decimal number.	001, 002, …, 366
%-j		Day of the year as a decimal number.   1, 2, ... 366	 
%U		Week number of the year (Sunday as the first day of the week) as a zero padded decimal number. All days in a new year preceding the first Sunday are considered to be in week 0.	00, 01, …, 53
%W		Week number of the year (Monday as the first day of the week) as a decimal number. All days in a new year preceding the first Monday are considered to be in week 0.	00, 01, …, 53
%c		Locale’s appropriate date and time representation.	
		Tue Aug 16 21:30:00 1988 (en_US);
		Di 16 Aug 21:30:00 1988 (de_DE)
%x		Locale’s appropriate date representation.	
		08/16/88 (None);
		08/16/1988 (en_US);
		16.08.1988 (de_DE)
%X		Locale’s appropriate time representation.	
		21:30:00 (en_US);
		21:30:00 (de_DE)
%%		A literal '%' character.	%	

The following work also but they aren't listed in the official python documentation:

%D		date in month/day/year (ex Jan 1st, 2008 = 01/01/08) ?
%s		date & time as a UNIX Epoch timestamp ? 
%C		century ??
%e		same as %d??
%F		date in year-month-day
%g 		year in what format? same as %y?
%G 		same as %Y?
%h 		abbreviated month. same as %b?
%k 		same as %-H or %-I?
%l 		 "  "  "
%P 		Locale’s equivalent of either am or pm like %p above but lowercase ?
%r 		time as hour:minute:second AM/PM
%R 		time as hour:minute
%T 		time as hour:minute:second
%u 		UTC hour
%V 		week number of the year. same as %U or %W ??

For the items marked with a ? without further testing I can't figure out which value it is.

--

The following flag characters are permitted between the '%' character and the conversion specifier character:

_      (underscore) Pad a numeric result string with spaces.

-      (dash) Do not pad a numeric result string.

0      Pad a numeric result string with zeros even if the conversion specifier character uses space-padding by default.

^      Convert alphabetic characters in result string to uppercase.

#      Swap the case of the result string.  (This flag works only with certain conversion specifier characters, and of these, it is only really useful with %Z.)
12 Likes

NOTE: I’ve tried to make all of these posts able to be copied as is into the template editor and work.
Due to some of the examples I’ve included you will get errors when you copy this post until you make corrections and use your correct entities in those examples.
I’ll annotate those sections that need to be modified in bold and with a “@@”.

The Unix Epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1 00:00:00

as_timestamp() - converts a properly formatted date/time string to a Unix Epoch representation.

time (GMT) = 2018-12-14 19:57:27 (UTC)
time (my time zone in UTC representation): 2018-12-14T19:57:27-05:00
time (my time zone local representation) : 2018-12-14 14:57:27

time as Epoch timestamp: 1544817447

A unix date/time string requires a correct format for the conversion algorithms to work correctly.

For example, here are time and date strings that won’t work:

time = 07:30

Set time as a string = time_string = ‘07:30’
{%- set time_string = '07:30' %}

time_string = {{ time_string }}

time_string as a timestamp = {{ as_timestamp(time_string) }} <== Doesn't work :(

date = 2018-12-12

Set date as a string = date_string = ‘2018-12-12’
{%- set date_string = '2018-12-12' %}

date as a string = {{ date_string }}

date_string as a timestamp = {{ as_timestamp(date_string) }} <== Doesn't work :(

A correct date/time string requires at least the following format:

date/time = 2008-02-09 21:00:00 (as local date/time)

Set date/time 1 as a string = good_time_string = ‘2008-02-09 21:00’
{%- set good_time_string = '2008-02-09 21:00' %}

Properly formatted date/time 1 as string = {{ good_time_string }}

Properly formatted date/time 1 as a timestamp = {{ as_timestamp(good_time_string) }} <== Works! :)

Or this will also work:

date/time = 2018-12-11T12:24:32.00+00:00 (as UTC date/time)

(This is the string format used in the attributes of date/time objects in HA. HOWEVER: This is not, what I will call, a true UNIX “datetime object” as used in the later sections. I’m not sure what the correct terminology is tho.)

The “+00:00” (or -00:00) is the UTC offset for your local timezone and is an addition to the hour:minute which will be adjusted when converted to local date/time (NOTE: “+00:00:00” won’t work)

Set date/time 2 as a string = good_time_string_UTC = ‘2018-12-11T12:24:32.00-00:00’
{%- set good_time_string_UTC = '2018-12-11T12:24:32.00-00:00' %}

Properly formatted date/time 2 as string = {{ good_time_string_UTC }} (UTC)

Properly formatted date/time 2 as timestamp = {{ as_timestamp(good_time_string_UTC) }} (UTC)

timestamp_custom() - converts a UNIX Epoch timestamp into a custom date/time string = unix_epoch_timestamp|timestamp_custom(format)

A custom time stamp can be used to represent the date and/or time in any format you want by using the time format directives from post 2.

date/time 1 as a custom timestamp = {{ as_timestamp(good_time_string)|timestamp_custom ('%Y/%m/%d %H%M') }}

When custom timestamp is used you can specify whether it gets converted to local or stays in UTC by inserting “, true” or “, false” to the end of the format section (true is default):

date/time 2 as custom local timestamp = {{ as_timestamp(good_time_string_UTC)|timestamp_custom ('%Y/%m/%d %H%M') }} (local)

date/time 2 as custom UTC timestamp = {{ as_timestamp(good_time_string_UTC)|timestamp_custom ('%Y/%m/%d %H%M', false) }} (UTC)

Or you can specify a default local/UTC format using the following filters:

date/time 2 as local timestamp = {{ as_timestamp(good_time_string_UTC)|timestamp_local }} (local)

date/time 2 as UTC timestamp = {{ as_timestamp(good_time_string_UTC)|timestamp_utc }} (UTC)

Add %z to display UTC offset of the above custom timestamp:

date/time 2 as UTC representation = {{ as_timestamp(good_time_string_UTC)|timestamp_custom ('%Y/%m/%d %H%M %z') }}

Next, since a UNIX Epoch timestamp is just an integer or floating point number then you can use pretty much any true number (not a number represented by a string) and convert it to a time string using timestmp_custom():

1223366 as a custom timestamp = {{ 1223366|timestamp_custom ('%Y/%m/%d %H%M') }}

Since a UNIX Epoch time stamp are just the number of seconds since a specific date then we can use that information to then manipulate date/time mathematically.

For example, to add 1 hour (3600 seconds) to the time above:

date/time 2 + 3600 seconds = {{ (as_timestamp(good_time_string_UTC) + 3600)|timestamp_custom ('%Y/%m/%d %H%M') }}

@@ The examples in this section will work as long as you pick an entity_id that includes an attribute that contains a time value. You can simply just pick any automation or script that has run recently:

Example 2

To find the number of hours since the last time a script was triggered:

The current time is - {{ now() }} (local time)

The script was last triggered - {{ states.script.garage_lights_timer.attributes.last_triggered }} (UTC time)

now() as a timestamp = {{ as_timestamp(now()) }}

One of my scripts "last_triggered" attribute as a timestamp = {{ as_timestamp(states.script.garage_lights_timer.attributes.last_triggered) }}

{{ as_timestamp(now()) }} - {{ as_timestamp(states.script.garage_lights_timer.attributes.last_triggered) }} = {{ ( (as_timestamp(now()) - as_timestamp(states.script.garage_lights_timer.attributes.last_triggered))) | int }} seconds

Therefor, the script was triggered {{ ( (as_timestamp(now()) - as_timestamp(states.script.garage_lights_timer.attributes.last_triggered)) / 3600 ) | round(2) }} hours ago.

/@@

@@ The following section uses current values based on the sensors created by the Time & Date component in HA @@

Here are examples using HA entities in date/time calculations:

date sensor = {{ states.sensor.date.state }}

date sensor as timestamp = {{ as_timestamp(states.sensor.date.state) }} ==> doesn't work

time sensor = {{ states.sensor.time.state }}

date + time sensor (concatenated) = {{ states.sensor.date.state + states.sensor.time.state }}

date + time sensor (concatenated) as timestamp = {{ as_timestamp(states.sensor.date.state + states.sensor.time.state) }} <== Doesn't work :(

Notice that above ends up not working because just adding them together doesn’t result in a properly formatted date/time string.

To do that you need to create it in the following way:

date + time sensor (concatenated with a space) = {{ states.sensor.date.state + ' ' + states.sensor.time.state }}

date + time sensor as timestamp = {{ as_timestamp(states.sensor.date.state + ' ' + states.sensor.time.state) }} <== Works! :)

/@@

This is an example using timestamps to set a sensor for the time to leave for work.

The calculation checks to see if the next leave time is today but after the current time.
If it is then it sets the leave time to today at the set time.
If the next leave time is before the current time it sets the next leave time to tomorrow at the set time.
If the next leave time is tomorrow it then checks if tomorrow is a Saturday or Sunday and if yes then it sets the leave time for the next Monday.

This exercise was taken from this thread (Remaining time until next leave to work) and is what inspired me to start trying to dig in and learn this stuff. :slight_smile:

now = {{ now() }}

Set current date & time to now as a timestamp:

{%- set current_date_time = as_timestamp(now()) %}

current_date_time as timestamp = {{ current_date_time }}

Then use custom_timestamp to extract the day of the week (0-6) as a string value:

current day of week = {{ current_date_time|timestamp_custom('%w') }}

Set desired time to leave for work = leave_time = 06:00

{%- set leave_time = '06:00' %}

Create a variable - next_leave - and set it to the current date at a specific time of the day (“leave_time” above) by concatenating the two strings and converting to a timestamp:

{%- set next_leave = as_timestamp(states.sensor.date.state + ' ' + leave_time) %}

next_leave = (date sensor + leave_time) as a timestamp = {{ next_leave }}

next_leave as a custom timestamp date/time string = {{ next_leave|timestamp_local }}

Convert current_date_time as a timestamp & next_leave as a timestamp to integers then check if current_date_time (as an integer timestamp) is after next_leave (as an integer timestamp) then set next_leave_mod to the next day (add 86400 seconds) at “time to leave”.

If it’s not then set the next leave to today at “time to leave”:

{%- if current_date_time | int >= next_leave | int %}
  {% set next_leave_mod = next_leave | int + 86400 %}
{%- else %}
  {% set next_leave_mod = next_leave | int %}
{%- endif %}

next_leave_mod as timestamp = {{ next_leave_mod }}

next_leave_mod as a custom timestamp date/time string = {{ next_leave_mod|timestamp_local }}

Determine the next_leave_mod day as number (0 - 6):
{%- set weekday = next_leave_mod | timestamp_custom('%w') | int %}

next day of week to leave = {{weekday}}

And determine if day of week to leave would be on a weekend day. If it would be on Saturday (day of week = 6) then add 48 hours (172800 seconds) to next_leave, or if it would be on Sunday (day of week = 0) then add 24 hours (86400 seconds) to next_leave:

{%- if weekday == 6 %}
  {% set next_leave_mod = next_leave | int + 172800 %}
  - The next day to leave would normally be Saturday. Adjusting day to Monday
  - next_leave_mod as timestamp + 48 hours = {{next_leave_mod}}
{%- endif %}
{%- if weekday == 0 %}
  {% set next_leave_mod = next_leave | int + 86400 %}
  - The next day to leave would normally be Sunday. Adjusting leave day to Monday
  - next_leave_ mod + 24 hours = {{next_leave_mod}}
{% endif %}

next_leave_mod as custom timestamp = {{ next_leave_mod | timestamp_custom('%Y-%m-%d %H:%M') }}

Therefore, leave time = {{ next_leave_mod | timestamp_custom('%Y-%m-%d %H:%M') }}

13 Likes

strptime(): - converts a string to a datetime object - strptime(date_string, format)

NOTE: I’ve tried to make all of these posts able to be copied as is into the template editor and work.
Due to some of the examples I’ve included you will get errors when you copy this post until you make corrections and use your correct entities in those examples.
I’ll annotate those sections that need to be modified in bold and with a “@@”.

Convert the strings ‘07:32’ & 00:30 to time datetimes and subtract:

Set t1 = ‘07:32’
{%- set t1 = '07:32' %}

Set t2 = ‘30’ (in minutes)
{%- set t2 = '30' %}

{{ t1 }} - {{ t2 }} = {{ strptime(t1, '%H:%M') - strptime(t2, '%M') }}

Or you can use a string instead of variables in the function:

07:32 - 00:30 = {{ strptime('07:32', '%H:%M') - strptime('30', '%M') }}

@@ The following section uses current values based on the sensors created by the Time & Date component in HA @@

date sensor as datetime object = {{ strptime(states('sensor.date'), '%Y-%m-%d') }}

date sensor year only as datetime object = {{ strptime(states('sensor.date'), '%Y-%m-%d').year }}

date & time sensor concatenated as a datetime object = t3 = {% set t3 = strptime(states.sensor.date.state+' '+states.sensor.time.state,'%Y-%m-%d %H:%M') %}

t3 = {{ t3 }}

t3 formatted with strftime = {{ t3.strftime("%b %d %Y %H:%M:%S") }}

/@@

Set time_string = 2013, 9, 30, 7, 6, 5
{%- set time_string = '2013, 9, 30, 7, 6, 5' %}

time_string as a string = {{time_string}}

time_string as a datetime object = {{ strptime(time_string, '%Y, %m, %d, %H, %M, %S') }}

Set time_string_2 to time_string as a datetime object
{%- set time_string_2 = strptime(time_string, '%Y, %m, %d, %H, %M, %S') %}

time_string_2 as a timestamp = {{ as_timestamp(time_string_2) }}

Year from time_string_2 = {{ time_string_2.strftime('%Y') }}

Set time_string2 = 2013, 9, 30
{%- set time_string2 = '2013, 9, 30' %}

time_string2 as a string = {{time_string2}}

time_string2 as a datetime = {{ strptime(time_string2, '%Y, %m, %d') }}

Set time12 to time_string2 as a datetime
{%- set time12 = strptime(time_string2, '%Y, %m, %d') %}

time12 as a datetime = {{ time12 }}

time12 as a timestamp = {{ as_timestamp(time12) }}

Set time_4 to time_string_2 as a timestamp
{%- set time_4 = as_timestamp(time_string_2) %}

time_4 = {{ time_4 }}

Set time_5 to now as timestamp
{%- set time_5 = as_timestamp(now()) %}

time_5 = {{ time_5 }}

{{ time_5 }} - {{ time_4 }} = {{ (time_5 - time_4) }}

Set time_6 = time_5 - time_4
{%- set time_6 = (time_5 - time_4) %}

time_6 = {{ time_6 }}

t6 as a custom timestamp = {{ time_6|timestamp_custom('%Y-%m-%d') }}

This section shows the time 0 as used in the UNIX Epoch

Set time_7 to 0 as a floating point number
{%- set time_7 = 0.0 %}

Then we can use it in the same way as the result of converting a date/time string to a timestamp with as_timestamp()

time_7 (as timestamp = 0) as custom timestamp = {{ time_7|timestamp_custom('%Y-%m-%d %H:%M:%S') }}

Checking to verify that each increment of 1 in the UNIX Epoch timestamp is equal to one second we can do the following:

Set time_8 to 1 as a floating point number
{%- set time_8 = 1.0 %}

time_8 (as timestamp = 1) as custom timestamp = {{ time_8|timestamp_custom('%Y-%m-%d %H:%M:%S') }}

Which is 1 second later than the time 0 above.

@@ The examples in this section will work as long as you pick an entity_id that are datetime objects:

(But I’m not sure which entities in HA are true datetime objects so you’ll need to experiment. And until you use good entities in these examples this section will cause this post to not render correctly in the template editor. YOU WILL GET ERRORS BECAUSE OF USING THIS SECTION UNMODIFIED.)

Google Calendar event start_time attribute as a string = {{ states.calendar.mycalendargmailcom.attributes.start_time }}

Google Calendar start_time attribute converted to datetime using strptime = {{strptime(states.calendar.mycalendargmailcom.attributes.start_time, '%Y-%m-%d %H:%M')}}

date__time sensor as a string = {{ states.sensor.date__time.state }}

date__time sensor converted to datetime using strptime = {{ strptime(states.sensor.date__time.state, '%Y-%m-%d, %H:%M') }}

automation last_triggered time as a string = {{ states.automation.deck_light_off_at_1am.attributes.last_triggered }}

BUT - This doesn’t work:

(NOTE: this isn’t in a template because it will blank out the result column due to the formatting error)

strptime(states.automation.deck_light_off_at_1am.attributes.last_triggered, ‘%Y-%m-%d %H:%M:%S.%f%z’)

I think it’s because the format of the UTC offset (+00:00) doesn’t match the expected format (+0000) but I haven’t been able to verify that.

But if you set a variable (t) to the above attribute then use it in strftime (covered in the next post) it will work.

t set to the attribute from above:
{%- set t = (states.automation.deck_light_off_at_1am.attributes.last_triggered) %}

t = {{ t }}

t formatted in strftime = {{ t.strftime('%Y-%m-%d %H:%M:%S.%f') }}

set t2 = t

{%- set t2 = (t.strftime('%Y-%m-%d %H:%M:%S.%f')) %}

t2 = {{t2}}

t2 converted to datetime with strptime = {{ strptime(t2, '%Y-%m-%d %H:%M:%S.%f') }}

Or you can nest them like this without the need to create another variable:

{{ strptime(t.strftime('%Y-%m-%d %H:%M:%S.%f'), '%Y-%m-%d %H:%M:%S.%f') }}

Does anyone know the format for incorporating the %z into the strptime() instruction?

6 Likes

strftime: - converts a datetime object or a UNIX timestamp to a string - datetime_object.strftime(format)

now = {{ now() }}

now as a string formatted as desired with strftime = {{ now().strftime("%H:%M %Y-%m-%d") }}

Number of the day in the year of now = {{ now().strftime('%j') }}

Week number in the year of now = {{ now().strftime('%U') }}

Set t = ‘2018-12-11 15:38:28.511’
{%- set t = '2018-12-11 15:38:28.511' %}

t as a string object = {{ t }}

Convert the string t to a datetime object using strptime. Remember, you must use the same format as the format of the input string
{%- set datetime_obj = strptime(t, '%Y-%m-%d %H:%M:%S.%f') %}

datetime_obj = {{ datetime_obj }}

Set t2 = ‘2018, 10, 24, 02, 14, 34, 543’
{%- set t2 = '2018, 10, 24, 02, 14, 34, 543' %}

t2 as a string object = {{ t2 }}

Convert the string t to a datetime object using strptime. Remember, you must use the same format as the format of the input string
{%- set datetime_obj2 = strptime(t2, '%Y, %m, %d, %H, %M, %S, %f') %}

datetime_obj2 = {{ datetime_obj2 }}

datetime_obj formatted using strftime = {{ datetime_obj.strftime("%b %d %Y %H:%M") }}

datetime_obj as timestamp = {{as_timestamp(datetime_obj)}}

datetime_obj2 formatted using strftime = {{ datetime_obj2.strftime("%b %d %Y %H:%M") }}

datetime_obj2 as timestamp = {{as_timestamp(datetime_obj2)}}

t3 = 2018-07-04 01:45:59.765987 -05:00

{%- set t3 = '2018-07-04 01:45:59.765987' %}

{%- set datetime_obj3 = strptime(t3, '%Y-%m-%d %H:%M:%S.%f') %}

datetime_obj3 = {{ datetime_obj3 }}

datetime_obj3 formatted using strftime = {{ datetime_obj3.strftime("%b %d %Y %H:%M") }}

now = {{ now() }}

Set t4 = now
{%- set t4 = now() %}

t4 = {{ t4 }}

t4 formatted with strftime = {{ t4.strftime("%b %d %Y %H:%M:%S") }}

Or you can use now() directly in the the template since it is a true datetime object:

Number of seconds in now = {{ now().strftime("%s") }}

Convert the number of seconds in now to an integer and the format it with a custom timestamp

{{ now().strftime("%s") | int | timestamp_custom("%H:%M") }}

Since %s is the number of seconds in now() if you copy the current result from above then this has the same result:

{{(1544880873) | int | timestamp_custom("%H:%M") }}

year hour:minute of now = {{ now().strftime("%s") | int | timestamp_custom("%Y %H:%M") }}

then you can also manipulate the values and format the result using a custom timestamp:

add 15 minutes (15 * 60 seconds) to now (hour:minute) = {{ (now().strftime("%s") | float + (15*60)) | timestamp_custom("%H:%M") }}

subtract 45 minutes (45 * 60 seconds) from now (hour:minute) = {{ (now().strftime("%s") | float - (45*60)) | timestamp_custom("%H:%M") }}

add 15 minutes (15 * 60 seconds) to now (hour:minute:seconds) = {{ (now().strftime("%s") | float + (15*60)) | timestamp_custom("%H:%M:%S") }}

subtract 45 minutes (45 * 60 seconds) from now (hour:minute:seconds) = {{ (now().strftime("%s") | float - (45*60)) | timestamp_custom("%H:%M:%S") }}

{{ now().strftime("%H:%M") }}

For some reason, this won’t change the result by using custom timestamp, but I’m not sure why…:
{{ now().strftime("%H:%M") | timestamp_custom("%D") }}

{{ now().strftime("%u") }}

{{ now().strftime("%u") | int | timestamp_custom("%H:%M") }}

{{ now().strftime("%V") }}

A simple example of a mathematical operation using both strftime & strptime:

{{ strptime(now().strftime("%H:%M:%S"), "%H:%M:%S") - strptime("10", "%M") }}

8 Likes

reserved for later

1 Like

This is an awesome thread and I’m going to steal it!

Just one curious thing…

My ISP usage reports time like this:

2019-03-29 14:27:02

Which is an utter PITA as I am reading it in via either MQTT or directly using a web hook to write to the sensor.

From my research, I believed that if I added a T in the middle like this:
2019-03-29T14:27:02 that this would be read as a LOCAL time and in fact that is exactly what happens… in Home Assistant… It was right. I set the device_class to a timestamp and then I could use the nifty format: datetime to display it in a friendly way on a card (This was when I was reading it into a sensor, not an attribute)

Anyway, after I did that, I looked on my iPad… and bugger me… it was ADDING the timezone to the date so it was saying ‘in 10 hours time’

Turned out I had to append the time zone to the end as well for my iPad to recognise it as a local date/time.

2019-03-29 14:27:02+11:00

Just a nice trap for iOS…

So I’m pretty sure the middle one is wrong. With the -5 on the end it means it’s in local time and the Timezone is -5 hrs to get GMT/UTC

Ie the ISO format of this time is 2018-12-14T14:57:27-05:00 which is local and in GMT it would be 2018-12-14T19:57:27Z (Z for Zulu or +00:00) At least that is what I have seen…

I’m glad you find it useful. That’s what it’s here for. :slightly_smiling_face:

I don’t think so but I’m open to being convinced.

GMT is five hours ahead of my timezone right now.

So to represent my timezone in UTC I have to subtract 5 hours from GMT to get to my local time. So GMT - 5 hours = my local time.

Looking quickly in wikipedia (ISO 8601 - Wikipedia ), if you want to represent your local time in UTC format you need to put in the timezone offset (-05:00 in my case). If no offset is specified then the time is considered to be in local time. If local time is GMT then the Z is added to make sure it is unambiguous that the local time is also GMT.

At least I think that’s what I’m getting from reading it.

And looking at your post again we may be saying the same thing but in a different way? But I wouldn’t say my statement there is “wrong”.

If it’s got a Timezone that isn’t 00:00 or z for Zulu it’s local time not GMT. The offset there says how it’s modified gmt. As I said when I read a local time in my iOS devices assumed they were gmt. Specifying the correct iso format with local time and +11 like we are now and everything works. Home Assistant was fine with no zone - it assumes local (which is the convention) iOS nor so much without the offset.

You are representing your local time in UTC format by putting in the timezone offset (-05:00 in my case).

I know. I said that. that’s why you need to have the timezone offset to define your local time in UTC format in terms of GMT. If there is no offset then it’s assumed to be local time. In the case that local time = GMT time then to make it clear that local time is also GMT when no offset is given then it’s customary per the ISO8601 standard to include at least the “Z” to signify that local time = GMT => it tells you unambiguously that local time (represented in UTC format) = GMT (represented in UTC format).

This all boils down to being a representation of local time in two different frames of reference. One is in relation to GMT (and needs to include the offset) and the other is assumed to be local time since it doesn’t include the offset.

Just wanted to say thanks for this thread, @finity. I find myself referring to it frequently. Maybe you should consider creating a version of it to put somewhere in the HA docs.

1 Like

Thanks for the kind words.

If I get the time maybe I’ll give your suggestion a try.

sorry, need some guidance please.
Im in the UK (So UTC+1 at the moment) and i have an attribute in my sensor thats showing as UTC.
So it shows as being 00:05 when it was actually 01:05

{{ states.vacuum.rover.attributes.clean_start }}
value = 2019-07-27 00:00:05

how do i change this value to add 60mintues to it, or is there more of a global setting that would automatically pass the correct value into the source data?

You need the sensor to include the time zone in the string then it will display correctly. Anything without an explicit time zone definition is treated as being in local time so the best solution is to ensure the sensor includes the TZ.

Hi, I’m trying to build an if-then test to get something done only when now() is not inside a specific time interval of the day. This is my most recent, but still failed attempt:

{%- set t1 = as_timestamp(now())| timestamp_custom('%H:%M') %}
{% if strptime(t1, '%H:%M') < strptime('09:30', '%H:%M') and
      strptime(t1, '%H:%M') > strptime('12:30', '%H:%M') %}
  Do_Something
{%- endif %}

Can you help me out?

I think it’s not the time manipulation that isn’t working. It’s the logic.

you will never have a time now that is both before 09:30 and after 12:30 at the same time.

I think for how you describe the desired result that you will need to change the “and” to an “or”.

That way if now is before 09:30 it will be true or if now is after 12:30 it will be true but not in between.

You are of course absolutely right. Funny how I got stuck in my own mind-loop. I should have thought of that. Anyway, I solved it by using if not :wink:
But still, it puzzles me that I had to use a temporary varible (t1) in order to get HA to tolerate the syntax.

Yeah, I’m no expert at any of this. I spent the better part of 3 or 4 days working on the content of the first posts here to try to understand it. TBH, I still have to go back to my notes when working with the more complex stuff.

but technically I don’t think have to use a variable. You could just substitute the stuff after the “set t1 =” part into everywhere you have t1 in the rest of the template. but that would get really messy. it’s cleaner to use that variable.

and also to make it a bit simpler you didn’t have to convert now to a timestamp. You could have gone straight to a string then when you use strptime it would convert the string to a time object for the comparison:

{%- set t1 = now().strftime('%H:%M') %}
{% if strptime(t1, '%H:%M') < strptime('09:30', '%H:%M') or
      strptime(t1, '%H:%M') > strptime('12:30', '%H:%M') %}
  Do_Something
{%- endif %}

Interesting. But I tried to substitute everything after the “set t1=” directly into the if statement, but HA complained about token irregularities and what not.
I must say that I do find these things overly complex. And finding documentation without presumption that your are already a python or java programmer with 10 years experience, makes it even harder. Take for instance the strftime and strptime operators (I don’t even know if calling them operators is correct?), I assume that someone “made” up these two words with something logical in mind. So probably str means string, and time is of course obvious, but the letter ‘f’ and ‘p’ stuck in between there? What do those mean? It would have been nice to know. it makes it easier to remember if you know what meaning or words these letters represent… just a sigh :roll_eyes:
Anyway, thanks for your suggestion. I’ll try it later.For now, at least, it works as-is.