The EPIC Time Conversion and Manipulation Thread!

Yes of course, but I am completely useless when it comes to date and times in Python!

I have some input numbers that hold a number of minutes. I add them up and create a string in the format HH:MM:SS.

I want to add that to a start time that is user selected from an input select and is in the form HH:MM.

So in a nutshell I want to add two strings representing times like this:

HH:MM + HH:MM:SS

e.g.
09:30 + 01:23:10 = 10:53:10

Preferably ending up as a string as all I want to do is display it (but that part is not the problem… yet :slight_smile:).

{% set d1 = strptime('07:32', '%H:%M') %}
{% set d2 =strptime('30', '%M') %}
{% set t1 = d1.hour*3600 + d1.minute*60 %}
{% set t2 = d2.hour*3600 + d2.minute*60 %}
{{ (t1+t2) | timestamp_custom('%H:%M:%S') }}

Simply put, strptime('07:32', '%H:%M') does not create a proper datetime object, it just stores what you give to in into appropriate fields but things like .timestamp() won’t work as it’s incomplete.
if you manually create a “timestamp” you can then feed it to timestamp_xxx for conversion.
Have a look.

Well, that seems to work, almost…

I needed to add a ‘local_boolean’… (timestamp_custom(format_string, local_boolean)

Something to do with timezones I think but to be honest I am not sure. I’m on GMT here which is currently UTC so why it needed a flag set I don’t know? Will it work in DST? And what is it adjusting for when the flag is true?

And feel free to tear apart any part of this. I really am growing to dislike working with dates and times in Python.

#=== This is included only for completeness. It simply sums some input 
#=== numbers to give a total in minutes which I then convert to HH:MM
{% set ns = namespace(duration = 0) %}
{% for zone in states.input_number if zone.entity_id.startswith('input_number.irrigation_cycle') and 
                                      zone.entity_id.endswith('duration') %}
  {% set ns.duration = ns.duration + zone.state | float %}
{% endfor %}

{% set hrs = ((ns.duration * 60) // 3660) | int %}
{% set min = (((ns.duration * 60) // 60) | int % 60) | round %}
#============================================

{% set duration = '{:02}:{:02}'.format(hrs, min) %}
Duration - {{ duration }}

{% set start = states('input_select.irrigation_cycle1_start_time') %}
Start - {{ start }}

{% set d1 = strptime(start, '%H:%M') %}
{% set d2 =strptime(duration, '%H:%M') %}
{% set t1 = d1.hour*3600 + d1.minute*60 %}
{% set t2 = d2.hour*3600 + d2.minute*60 %}

{{ (t1+t2) | timestamp_custom('%H:%M') }}
{{ (t1+t2) | timestamp_custom('%H:%M', false) }}

Sure, but I think it should be True/False - try it out with False.
I’m on GMT, too. Think it should work in DST.
Have no idea what it adjusts. Also fed up a bit with all wrappers etc as we usually don’t deal with normal python and it’s pita to find out why on earth it works that way. Oh, well…

I think it might have to do with what the function of the + sign is.

the + sign can be either an mathematical operator when using it with numerical data types or it can be a concatenator if used with strings but since a datetime object (even an incomplete one like here) is neither a number or a string then I think the renderer can’t figure out what you want to do with it.

The - sign doesn’t come with that baggage. At least that’s what I think in my very un-expert opinion.

From Jinja2’s documentation:

+
Adds two objects together. Usually the objects are numbers, but if both are strings or lists, you can concatenate them this way. This, however, is not the preferred way to concatenate strings! For string concatenation, have a look-see at the ~ operator. {{ 1 + 1 }} is 2 .

~
Converts all operands into strings and concatenates them.
{{ "Hello " ~ name ~ "!" }} would return (assuming name is set to ‘John’)
Hello John!.

tl;dr
Use tilde ~ to concatenate strings.

I think that’s what i was trying to say. :laughing:

@finity, @123 I understand when you say

but do you think in this code the goal is to have a combination of 2 string representations of times?

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

He actually wants this

which he mistakenly explains as

but in fact he wants a much more complex thing.

My understanding is + and - are functions and their result depends on the data you pass to it and some implicit type conversions that might happen. And the type error it generates speaks for it I think.
Some data types may even have ‘+’ but not ‘-’ (if it does not make sense) and vice versa.

“In the land of the blind, the one-eyed is king”

@123, @AhmadK
Maybe this isn’t the right place but I’ve often come across the term ‘Pythonic’ to describe the Python language structure wrt deciding how to implement it. As I understand it this describes the principle that Python should be readable to non Python programmers.

This ambiguous use of the ‘+’ operator perturbs me especially in light of the single use of it’s opposite and natural ‘companion’, ‘-’ and it just feels, wrong. As a Python novice, I just wonder how you two feel about this?

It’s purely an intellectual question, nothing more…

I have’t finished my first book on Python yet so can’t tell you what exactly ‘Pythonic’ is (perhaps each person has its own understanding when they use this term) but I slightly doubt that it means ‘readable to non Python programmers’.

Yeah, I had a quick look-up and you are probably right that is not accurate, although I have definitely seen that said or at least implied, in the past.

I found this:
"Exploiting the features of the Python language to produce code that is clear, concise and maintainable.

Pythonic means code that doesn’t just get the syntax right but that follows the conventions of the Python community and uses the language in the way it is intended to be used."

Compare the example I posted here for finding the last item in a list, using python’s [-1], versus the previous post in the same thread that employs Jinja2’s length filter. Basically, “pythonic” means doing it the ‘python way’ and taking advantage of its strengths.

Quick question - if I have several sensors I want to format in a particular way, do I have to create the same template sensor each time with a different entity, or is there a way to create a “template” template sensor which can be reused by just passing the entity each time? Kinda like the way we can template Lovelace cards, but for the sensor itself, if you see what I mean?

Thanks to @finity for this thread, and also to @pedro who also added some very useful info on this Date formatting thread, both have been very helpful and enlightening, so thank you both!

as far as i know that is correct.

Also, it’s not @pedro, it’s @petro. :wink:

1 Like

Ah dammit, thats a shame! Thanks for confirming :+1:

Oh Boll*cks! Sorry @petro! Thanks for correcting me :slight_smile:

No hay problema

1 Like

You can reduce some of the duplication by using YAML anchors and aliases. I provided an example here:

2 Likes

Aaah! Interesting (I’d heard of the yaml anchors, but never looked into them further so didnt realise I could use them in the sensors (rather than in the lovelace side of things). Thank you @123, that should help somewhat :+1:

1 Like

Thanks for that.

I never use those so I never remember to think about them.

Just wanted to thank you again for this thread @finity.
Every time I get stuck on something time/date related the answer is always right here! I bet you if you were to organize it all formal like and submit it, it could become part of the official docs.

1 Like