DateTime Conversion (openHAB 3.x)

Because openHAB 2.x rules use Jodatime instead of Java Time API, there is still the old DateTime Conversion thread for openHAB 2.x.


In openHab 3.x there are different ways to handle Date/Time values.

  • DateTimeType
    A DateTime Item carries a DateTimeType.

  • Java Time
    By default openHAB 3.x Rules DSL use java.time API to represent date and time.
    Principle java date-time concepts are LocalDateTime (date and time without any offset or time-zone) and ZonedDateTime (“full” date-time with time-zone and resolved offset from UTC/Greenwich).

  • Epoch
    The lowest common denominator when working with time is to get at the epoc value. Epoc is the number of milliseconds that has passed since 1 January 1970 GMT and stored in a long. With epoc, one can compare two dates together, convert a Joda DateTime to a DateTimeType and visa versa. With epoc you can represent a duration.

  • String
    In certain cases it is needed to convert date/time values to a human readable.

This flow chart is intended to help you finding the code needed for conversion.
The numbers of the flow chart refer to the numbers of the following list.

#1 Get DateTimeType from String

String must be ISO 8601 formatted like this: “yyyy-MM-dd’T’HH:mm:ss.SSSZ”

val DateTimeType MyDateTimeTypeFromString = DateTimeType.valueOf(MyString) 

#2 Get DateTimeType from Epoch

(Preliminary: not yet tested)

Since no direct conversion is known, this code uses conversion #6 (Epoch to JavaTime) and then #3 (JavaTime to DateTimeType)

val long MyEpochMilliseconds = 999999000  // create a epoch value to work with`

val MyJavaTimeFromEpoch = Instant.ofEpochMilli(MyEpochMilliseconds).atZone(ZoneId.systemDefault()) // #6 convert epoch to JavaTime

val MyDateTimeTypeFromJavaTime = new DateTimeType(MyJavaTimeFromEpoch) // #3 convert JavaTime to DateTimeType

#3 Get DateTimeType from Java Time

Variant A (from LocalDateTime)

val LocalDateTime MyJavaLocalDateTime = new LocalDateTime() // create a LocalDateTime value to work with`
val DateTimeType MyDateTimeTypeFromJavaLocalDateTime = new DateTimeType(MyJavaLocalDateTime ) // conversion

Variant B

Because JavaTime and DateTimeType both accept ISO 8601 formatted date/time strings it is easy to convert from one to the other using their toString.
Regarding to the flow chart this code converts JavaTime => #11 => to String => #1 => to DateTimeType

val DateTimeType MyDateTimeTypeFromJavaTime = DateTimeType.valueOf(MyJavaTime.toLocalDateTime().toString())

Timestamp (DateTimeType)

val DateTimeType MyDateTimeTypeTimestamp = DateTimeType.valueOf(now.toLocalDateTime().toString())

#4 Get Java Time from DateTimeType

val MyJavaTimeFromDateTimeItem = (MyDateTimeItem.state as DateTimeType).getZonedDateTime()

More info about Java ZonedDateTime Class

#5 Get Java Time from String

String must be ISO 8601 formatted like this: “yyyy-MM-dd’T’HH:mm:ss.SSSZ”

val MyZonedDateTimeFromString = ZonedDateTime.parse("2020-12-25T00:00:00.000Z").withZoneSameInstant(ZoneId.systemDefault())

#6 Get Java Time from Epoch

(Preliminary: not yet tested)

Variant A

val long MyMilliseconds = 999999000  // create a value we can work with`

val MyJavaTimeFromEpoch_VariantA = Instant.ofEpochMilli(MyMilliseconds).atZone(ZoneId.systemDefault()) // #6

#7 Get Epoch from Java Time

(Preliminary: not yet tested)

Variant A

val MyEpochFromJavaTime_VariantA = MyJavaTime.toInstant.toEpochMilli

Variant B

val MyEpochFromJavaTime_VariantB = MyJavaTime.toEpochSecond * 1000


val MyEpochTimestamp_VariantA = now.toInstant.toEpochMilli
val MyEpochTimestamp_VariantB = now.toEpochSecond * 1000

#8 Get Epoch from DateTimeType

val Number MyEpochFromDateTimeTypeItem = (MyDateTimeTypeItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli

#9 Get Epoch from String

String must be ISO 8601 formatted like this: “yyyy-MM-dd’T’HH:mm:ss.SSSZ”

val Number MyEpochFromString = new DateTime(MyDateTimeString).millis

#10 Get String from Epoch

Variant A
Regarding to the flow chart this code converts from Epoch => #6 => to Java Time => #11 => to String. This means formatting can be done like shown in #11

ToDo …

Variant B

Here is an option utilizing SimpleDateFormat:

import java.text.SimpleDateFormat
import java.util.Date

val SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
val String MyStringFromEpoch_VariantB = sdf.format(new Date(MyEpoch))

#11 Get String from Java Time

Variant A

val String MyStringFromJavaTime = MyJavaTime.toLocalDateTime().toString()

Timestamp (String)

val String MyStringTimestamp = now.toLocalDateTime().toString()

#12 Get String from DateTimeType

Formatting can be done like shown here.
For more details look at this.

val String MyStringFromDateTimeType = MyDateTimeType_item.state.format("%1$td.%1$tm.%1$ty %1$tH:%1$tM")
now.getMinuteOfDay in openHAB 3
How to convert DateTime-Item (DateTimeType) to DateTime in rules
DayOfWeek intValue oh3 DSL rule failure
openHAB 3.0 issues after migrating
OH3, Error 'millis' is not a member of 'java.time.ZonedDateTime'
New DateTime in OH3/Java Time
Problems with time comparison
Migration issues OH2 to OH3
Failed to parse state from DateTime with Remote openHAB 2 ==> 3
How to extract hour and min from DateTime
OH3 substringAfter is undefined for the type String
Easier way for timestamps possible?
Problems with Rule since update to OH3 (DateTime cannot be resolved to a type)
[SOLVED] Another JavaTime conversion Thread... DateTime
Another Rule Migration to OH3 Issue - An error occurred during the script execution: null
[Deprecated] Design Pattern: Time Of Day
[SOLVED] Rule DSL DateTimeType add minutes
DateTime Conversion OH3 JavaScript
OH3 DateTime-Conversion again (with Astro items)
Timeconversion: Astro Binding (DateTime) to Date() javascript object
[Deprecated] Design Pattern: Time Of Day
VERY simple date diff
VERY simple date diff
Change rules not to use DateTime before migrating to OH3?
Rule broken after OH3 upgrade - calculate difference between 2 dates/times [SOLVED]
Converting rule to OH3 --> Issue time
OH3: update a datetime item with a ZonedDateTime
Rule for calculating the cost of electricity
[Deprecated] Design Pattern: Time Of Day
LocalTime - not showing in Sitemap (Basic UI @OH3)
Getting time a value changed -OpenHab3
Design Pattern - Timer Management
Rule: Calc end time from start time and duration
Rule: Calc end time from start time and duration
DSL Rule DateTime formatting?
VSCode extension issues
OH3 Setting current DateTime to a custom item in a rule
OH3 Setting current DateTime to a custom item in a rule
Get epoch new DateTime ZonedDateTime
Get epoch new DateTime ZonedDateTime
Saving a time to an Item and comparing later on
historicState Date format error (OH3.1)
OH3 - Coming from 2.5 Automatic Shutter dont work anymore
OH 3 Time converting issue
MQTT binding upgrade to OH3 rule not working
Rules with Timer OH3 after Update from OH2
Help with rule from OH2.5 to OH3.1
Date-Time Calculation issue OH2 to OH3
OH3 comparing date string to now
OH3 Astro Rule isnt executed
Dynamic Temperature Rule
Failed: 'getMinuteOfDay'
OH3 - ECMA: totally confused by date-variables, coding, casting and stuff
DateTime conversion & calculation not working after moving from OH2 to OH3
JSScripting DateTimeType to ZonedDateTime Syntax Error
OH3: after migration old rules are full of errors (authorization failed, array element type mismatch...)
How to convert (and format) a DateTime item in a rule
Solved: Openhab3 problem with hashmap containing datetime: validation issue
ECMAScript DateTime item convert to/from ZonedDateTime/LocalDateTime
OH3 .getHour "is not in a valid format"
Complete watering system based on openHAB
How to update am Item of DateTime type?
Date Time conversion error in a rule
Rule to Check LastWakeUp on ZWave battery devices
The right code for JavaScript rules or how to save ZonedDateTime into an item
OH3: date time replacements
[solved] Javascript postupdate datetime format?
[Solved] How to find the First Occurrence in Time Range use Influxdb

I used the following two steps in 3.0.0.M2 to convert a Java Time (now) to a DateTimeType (no imports needed):
# 11 Get String from Java Time
val String dts = now.toLocalDateTime().toString()

Then, I converted the string to a DateTimeType:
# 3 Get DateTimeType from Java Time (using String)
val DateTimeType dt = DateTimeType.valueOf(dts)


Because I’m still on oh 2.4 yet, I can’t test it. But I think you did.
Thanks a lot!

Thought I would share this as my upgrade to OH3 broke all my timers that used " now.plusMillis ".
I wasn’t able to find a function that would use milliseconds as OH2 did but I was ok with using seconds. I converted everything from milliseconds to seconds and in a way it makes it easier to read and calculate. If you need to get the fractional seconds, you can use plusNanos instead…

I essentially went from this that would now present me with an error in VSS

rule "Test plusSeconds"
    Item testSwitch changed
    logInfo("testing.rules", "*****************  Begin!  ********************" )   
    testSwitch_Timer_off = createTimer(now.plusMillis(5000)) [|
                logInfo("testing.rules", "1 - 2 - 3 - 4 - 5 seconds have passed" )   	

   logInfo("testing.rules", "******************  All good things come to an end!  ********************" )   

The error…

to the following…

rule "Test plusSeconds"
    Item testSwitch changed
    logInfo("testing.rules", "******************  Begin!  ********************" )   
    testSwitch_Timer_off = createTimer(now.plusSeconds(5)) [|
                logInfo("testing.rules", "1 - 2 - 3 - 4 - 5 seconds have passed" )   	

   logInfo("testing.rules", "******************  All good things come to an end!  ********************" )   

Anyway hope that helps someone…

and finally some info on the Java ZonedDateTime Class in case that helps.


Can anyone give me a hint so I can do conversion #4?
I’ve tried to use datetime parser to convert a datetimetype to a Java time, but it won’t work.
I want to use now.isAfter(item state).
Example (works in OH 2.5.x):

val morning=new DateTime(Morning_Time.state.toString)
val day=new DateTime(Day_Time.state.toString)
var curr=""
if (now.isAfter(morning) && now.isBefore(day)) { curr="Morning"}

Because now is in OH3 Java time, this won’t work anymore.
I’ve tried to change it to this:

val morning=ZonedDateTime.parse(Morning_Time.state.toString, DateTimeFormatter.ISO_OFFSET_DATE_TIME)
val day=ZonedDateTime.parse(Day_Time.state.toString, DateTimeFormatter.ISO_OFFSET_DATE_TIME)
var curr=""
if (now.isAfter(morning) && now.isBefore(day)) { curr="Morning"}

But I get the error that it can’t be parse correctly.

Thanks to @rlkoshak I was able to figure this out for my use case. Here are two examples that work for me.

val day_start = (AstroSunData_Rise_StartTime.state as DateTimeType).getZonedDateTime()
val evening_start = (AstroSunData_Set_StartTime.state as DateTimeType).getZonedDateTime()

Thank you and @rlkoshak! It works.

1 Like

maybe you can share the solution here ?

This is the solution:

A post was split to a new topic: Time conversion


val MyZoneDateTime = ZoneDateTime.parse("2020-12-25T00:00:00.000Z").withZoneSameInstant(ZoneId.systemDefault())
1 Like

#4 Get Java Time from DateTimeType
Tested in OH3, works fine:
val MyJavaTimeFromDateTimeItem = (MyDateTimeItem.state as DateTimeType).getZonedDateTime()



I had in OH 2.5 the following rule - that calculates my power consuption for one day:

rule “Strom Verbrauch Tag”
tem homematic_stecker_Energiezaehler_kwh received update
if(homematic_stecker_Energiezaehler_kwh.state instanceof Number){
Stromzaehler_Verbrauch_Tag.postUpdate(homematic_stecker_Energiezaehler_kwh.deltaSince(now.withTimeAtStartOfDay, “influxdb”) as Number)

Unfortunately i am not a programmer. Could someone please give me a hint on how to recreate the rule for OH3?




thanks - now it works

I replaced




I didn’t understand Java at all.
I need to convert


in oh3 format because now one rule dont work properly.

Many thanks


When rewriting my rules for OH3, I encountered a problem with the conversion between DateTimeType and Java Time: for some reason, when I convert Java Time to String, the name of my timezone is added to the string, e.g. 2021-01-01T07:00+01:00[Europe/Amsterdam]. This probably has to do with the time zone configuration of the Java environment. Obviously, this leads to problems when the string is parsed.

I also had a problem the other way around. When a DateTimeType was converted to a String, the string was missing a : in the time zone identifier:

DateTimeType to String: "2021-01-01T07:00:00.000+0100"
Java Time expects:      "2021-01-01T07:00:00.000+01:00"

I was able to solve both issues by using a formatter:

  val formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")

  val morning_start = // 7:00

  // Java time to DateTimeType:

  // DateTimeType to Java time:
  val day_start = ZonedDateTime.parse(vSunrise_Time.state.toString, formatter) 

I hope this helps someone. I’d think this method is more robust in all cases, so it might be recommended to do this, even if you don’t encounter problems yet.


You can use the .toLocaleDateTime method to get the time with the time zone. Then you don’t the formatter.



now.toEpochSecond * 1000
1 Like


val zdt = Instant.ofEpochMilli(XXX).atZone(ZoneId.systemDefault())
val zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(XXX), ZoneId.systemDefault())
1 Like