Jython datetime.time exception

I compare two time objects, and if I put all the code in one script, it works.
When I move the function ‘getTime’ to a module in lib, I get an exception, at the line:
if aTime > now:

Here’s to code in automation\jsr223\python\personal\test.py

> from datetime import datetime
> from core.rules import rule
> from core.triggers import when
> import personal.utilities
> reload(personal.utilities)
> from personal.utilities import getTime
> @rule("TestNGE")
>     @when("Item Test changed")
>     def testNGE(event):
>         aTime = getTime(items.TimeDuskLightsOff)
>         now = datetime.now().time()
>         if aTime > now:
>             # do stuff
>             pass

and the code in automation\lib\python\personal\utilities.py

> from datetime import time
> def getTime(timeRep):
>     '''Takes a DecimalType as e.g. 1234 and returns a datetime.time object set to 12:34'''
>     hours = timeRep.intValue() / 100
>     minutes = timeRep.intValue() % 100
>     return time(hours, minutes)

and here is the exception:

2020-01-27 16:46:25.204 [ERROR] [jsr223.jython.TestNGE               ] - Traceback (most recent call last):
  File "/etc/openhab2/automation/lib/python/core/log.py", line 51, in wrapper
    return fn(*args, **kwargs)
  File "<script>", line 45, in testNGE
  File "/etc/openhab2/automation/jython/jython-standalone-2.7.1.jar/Lib/datetime.py", line 1284, in __gt__
    _cmperror(self, other)
  File "/etc/openhab2/automation/jython/jython-standalone-2.7.1.jar/Lib/datetime.py", line 390, in _cmperror
    raise TypeError("can't compare '%s' to '%s'" % (
TypeError: can't compare 'time' to 'time'

2020-01-27 16:46:25.226 [WARN ] [e.automation.internal.RuleEngineImpl] - Fail to execute action: 1
org.python.core.PyException: null

If I add logging to check the datatypes, they are both the same: datetime.time
If I change the function to return a datetime.datetime, and also adjust the script, then it works ok.

Doing this on RPI3+ with openhabian and openHAB 2.5.1-2 (Release Build)
using Jython 2.7.1

Please post that too, for reference. It may be a simple mistake when breaking things apart.

I hope it’s that simple :slightly_smiling_face:

from datetime import datetime, time
from core.rules import rule
from core.triggers import when
#import personal.utilities
#from personal.utilities import getTime

def getTime(timeRep):
    '''Takes a NumberItem state as e.g. 1234 and returns a datetime.time object set to 12:34'''
    hours = timeRep.intValue() / 100
    minutes = timeRep.intValue() % 100
    return time(hours, minutes)

    @when("Item Test changed")
    def testNGE(event):
        aTime = getTime(items.TimeDuskLightsOff)
        now = datetime.now().time()
        if aTime > now:
            # do stuff

I should add that TimeDuskLightsOff is a Number item, and aTime gets set to a correct time.

I think the first quote here should be from datetime import datetime, time

I’m afraid not. time() is an instance method of datetime.
I tried adding time to the import anyway, but it still fails.

What happens if you put the test.py in the same path (lib not jsr223) as utilities.py ?

But then the script engine won’t pick it up. Or am I misunderstanding you?

Sorry, think I have it reversed and not certain it will work, just an idea to try. It’s been over a year since I used this when testing installs on openhabian with Scott but I remember something odd about jsr223. :thinking:

Move utilities.py to automation\jsr223\python\personal

OK I’ll give that a try, but as I said, returning a datetime.datetime object works fine (by changing the code).

@DavidR looking over the OP I noticed Jython 2.7.1 and was curious how you installed Jython. Did you use the beta bundle or have EXTRA_JAVA_OPTS configured? I ask b/c there is a bug with that Jython version and the new bundle comes with a downgrade to 2.7.0.

From the HP about core.date:

The functions in this module can accept any of the following date types:

datetime.datetime (Python)

1 Like

Yes I installed it early on with EXTRA_JAVA_OPTS. The beta bundle looked a bit complicated, or at least the follow up thread was very long so I skipped it.
I’ll give 2.7.0 a go tomorrow.

So just to follow up with this - I tried moving utilities.py to automation\jsr223\python\personal, but the Script Engine complains:

2020-01-27 19:36:46.713 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab2/automation/jsr223/python/personal/test.py.7b9eb29f2839fe6b8b3d11cbd2f14ca7.py': ImportError: No module named utilities in <script> at line number 10

The first post is all you need :slightly_smiling_face:. It has been tested enough that I will be adding the bundle to the helper library installation instructions. I’m working on a couple more things that I plan to get in before doing that.

I recommend 2.7.0 for several reasons. I do not see any of those affecting you here, but it’s still worth downgrading!

I tested the script and the module using both 2.7.0 and 2.7.1 and did not get an exception.

Where did you add the logging? You must have not been logging variables, since they should have been java.sql.Time. It’s really just a preference, but I recommend using Java date and time objects, since these are what OH uses. Also, try core.date and see if it has something that you could use for what you are trying to do. If not, we can add it.

I’m not sure what you are using this for, but be careful with the lack of timezone information. If your times are just right, the conversion to UTC could make one of them cross over midnight but not the other, giving the wrong result. I’ve also never seen datetime.datetime.time objects compared like this, so maybe that is the root of your problem.

I downgraded to 2.7.0, but instead of restarting openHAB as I usually do, I rebooted the RPi.
Testing the script and module did not give an exception. Hooray.
Just for interest, I changed the EXTRA_JAVA_OPTS back to 2.7.1, restarted openHAB, tested, and it still works correctly.

I don’t think I have rebooted the RPi since installing the scripting several months ago, and I’ve been doing a lot of experimenting while moving my DSL over, so I may have messed something up.

No matter, I will go back to 2.7.0 and continue playing.

1 Like

I added the logging to the script like this:

    now = datetime.now().time()
    log.debug('now: {}, type {}'.format(now, type(now)))

and it gives

2020-01-27 16:28:52.063 [DEBUG] [jsr223.jython.test                  ] - now: 16:28:52.039000, type <class 'datetime.time'>

The same datatype is returned from the getTime method.

All the time setting controls for my heating are done the same way.
In my sitemap, I use a setpoint with the step=15, and have a rule to handle incrementing or decrementing when the hour rolls over. That’s why I need the getTime method. It’s fairly specific to me, so I don’t think adding it to core.date would be useful.

I only compare time and don’t care about the date, so I think I’m good. I’ve been using java.time.LocalTime in the DSL rules.

Thanks for all you suggestions, and excellent work on the scripting. I’ll install the bundle soon.

Very odd… I’m curious about this! Which type and version of Java are you using? All of these return java.sql.Time for me, which is what I would expect from Jython…

from datetime import datetime, time
from personal.test import get_time
from core.log import logging, LOG_PREFIX#, log_traceback
LOG = logging.getLogger("{}.TEST".format(LOG_PREFIX))

import sys
LOG.warn("Jython version [{}]\n".format(sys.version))

time_1 = time(12, 5)
LOG.warn("time_1 [{}], type(time_1) [{}]".format(time_1, type(time_1)))

time_2 = get_time(DecimalType(5))
LOG.warn("time_2 [{}], type(time_2) [{}]".format(time_2, type(time_2)))

time_3 = datetime.now().time()
LOG.warn("time_3 [{}], type(time_3) [{}]".format(time_3, type(time_3)))
2020-01-28 08:53:04.967 [WARN ] [jsr223.jython.TEST] - Jython version [2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11) 
[OpenJDK 64-Bit Server VM (Oracle Corporation)]]

2020-01-28 08:53:04.969 [WARN ] [jsr223.jython.TEST] - time_1 [07:05:00], type(time_1) [<type 'java.sql.Time'>]
2020-01-28 08:53:04.969 [WARN ] [jsr223.jython.TEST] - time_2 [19:05:00], type(time_2) [<type 'java.sql.Time'>]
2020-01-28 08:53:04.970 [WARN ] [jsr223.jython.TEST] - time_3 [03:53:04], type(time_3) [<type 'java.sql.Time'>]

You’re also getting a class object returned from type. Hmmm…

Curious indeed.
I created a new file with the same content as yours, and this is what I get:

2020-01-29 11:22:48.041 [WARN ] [jsr223.jython.test                  ] - Jython version [2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11) 
[OpenJDK Client VM (Azul Systems, Inc.)]]

2020-01-29 11:22:48.055 [WARN ] [jsr223.jython.test                  ] - time_1 [13:05:00], type(time_1) [<type 'java.sql.Time'>]
2020-01-29 11:22:48.073 [WARN ] [jsr223.jython.test                  ] - time_2 [01:05:00], type(time_2) [<type 'java.sql.Time'>]
2020-01-29 11:22:48.086 [WARN ] [jsr223.jython.test                  ] - time_3 [12:22:48], type(time_3) [<type 'java.sql.Time'>]

If I put

    time_1 = time(12, 5)
    log.warn("time_1 [{}], type(time_1) [{}]".format(time_1, type(time_1)))

inside a rule, I get

2020-01-29 11:32:28.426 [WARN ] [jsr223.jython.test                  ] - time_1 [12:05:00], type(time_1) [<class 'datetime.time'>]

I guess the @rule (or @when) decorator changes the scope for datetime.

I also tried putting the time_1 = time(12, 5) etc outside of the @rule scope, and it shows a [<type ‘java.sql.Time’>]

That could get confusing!

Ah… that makes sense! I assumed you were running from the script and not in a rule. Thank you!