DateTime Conversion (openHAB 2.x)

Because openHAB 3.x rules now use Java Time API instead of Jodatime, there is a new DateTime Conversion thread for openHAB 3.x.

Overview

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

  • DateTimeType
    A DateTime Item carries a DateTimeType.

  • Joda DateTime
    By default openHAB 2.x-Rules use a Joda DateTime class to represent time, most notably now.

  • 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 easily find 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

Regarding to the flow chart this code converts Epoch => #6 => to Joda => #11 => to String => #1 => to DateTimeType

val DateTimeType MyDateTimeTypeFromEpoch = new DateTimeType(new DateTime(MyEpoch).toString)

#3 Get DateTimeType from Joda

Variant A

val calendar = java.util.Calendar::getInstance
calendar.timeInMillis = now.millis
val MyDateTimeTypeFromJoda_VariantA = new DateTimeType(calendar)

Variant B

(probably less efficient but much easier to read and remember)

Because Joda DateTime 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 Joda => #11 => to String => #1 => to DateTimeType

val MyDateTimeTypeFromJoda_VariantB = new DateTimeType(now.toString)

#4 Get Joda from DateTimeType

Varinat A

val MyJodaFromDateTimeType_VariantA = new DateTime((MyDateTimeItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)

Variant B

Because Joda DateTime 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 DateTimeType => #12 => to String => #5 => to Joda

val MyJodaFromDateTimeType_VariantB = new DateTime(MyDateTimeItem.state.toString)

#5 Get Joda from String

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

Varinat A

val DateTime MyJodaFromStringV1 = new DateTime("2018-05-05T05:55:55.000-04:00")

Varinat B

val DateTime MyJodaFromStringV2 = parse("1963-01-01T00:00:00")

#6 Get Joda from Epoch

Varinat A

val MyJodaFromEpoch = new DateTime(MyEpoch)

Variant B
If the Epoch value is stored in a NumberItem, this NumberItem must be converted to Epoch first.

var MyEpochFromNumberItem = (MyNumberItem.state as Number).longValue * 1000
val MyJodaFromNumberItem = new DateTime(MyEpochFromNumberItem)

#7 Get Epoch from Joda

val Number MyEpochFromJoda = now.millis

#8 Get Epoch from DateTimeType

Varinat A

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

Varinat B

val Number MyEpochFromDateTimeTypeItem_VariantB = new DateTime(MyDateTimeTypeItem.state.toString).millis

#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 Epoch => #6 => to Joda => #11 => to String. This means formatting can be done like shown in #11

val String MyStringFromEpoch_VariantA = new DateTime(MyEpoch).toString

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 Joda

This will return a time in your local timezone.

Variant A

This gives you the complete ISO 8601 DateTime-string. (“yyyy-MM-dd’T’HH:mm:ss.SSSZ”)

val String MyStringFromJoda = now.toString

Variant B

Using patterns you can get a substring that exactly fits to your needs.

val String MyStringFromJoda = now.toString("yyyy-MM-dd'T'HH:mm:ss.SSSZ")

Copy&Paste example, to use a pattern

Copy this complete code into a rules.file. The code shows how patterns are used and …

rule "rJodaDateTimeToSubstring_Examples"
 when 
	Time cron "0 */1 * ? * *" // every minute. Source: https://crontab.guru/examples.html
 then 
    // first prepare the log entry
        val String          myLogFile = "mySeparateLogFile"   // if 'mySeparateLogFile' does not exist, logging goes into openhab.log (to create a separate logfile see https://www.openhab.org/docs/administration/logging.html#logging-into-separate-file)
        val StringBuilder   myLogMessage = new StringBuilder
        myLogMessage.append( "\n" + "\n" + " ----------   myJodaNow (JodaDateTime) to substring - examples   ----------    Source: http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html" + "\n" + "\n" )

    // second create a JodaDateTime we can work with 
        val DateTime myJodaNow = now  
        myLogMessage.append( "    myJodaNow = " + myJodaNow.toString + "\n" + "\n" ) 

    // then use pattern to get the desired substring
        myLogMessage.append( "     pattern  name                          substring" + "\n" )
        myLogMessage.append( "     -------  ----                          ---------" + "\n" )
        myLogMessage.append( "        G     era                         = " + myJodaNow.toString("G") + "\n" )
        myLogMessage.append( "        C     century of era (>=0)        = " + myJodaNow.toString("C") + "\n" )
        myLogMessage.append( "        Y     year of era (>=0)           = " + myJodaNow.toString("Y") + "\n" )
        myLogMessage.append( "        yyyy  Year                        = " + myJodaNow.toString("yyyy") + "\n" )
        myLogMessage.append( "        yyy   Year                        = " + myJodaNow.toString("yy") + "\n" )
        myLogMessage.append( "        MMMM  Month                       = " + myJodaNow.toString("MMMM") + "\n" )
        myLogMessage.append( "        MMM   Month                       = " + myJodaNow.toString("MMM") + "\n" )
        myLogMessage.append( "        MM    Month                       = " + myJodaNow.toString("MM") + "\n" )
        myLogMessage.append( "        M     Month                       = " + myJodaNow.toString("M") + "\n" )
        myLogMessage.append( "        w     week of weekyear            = " + myJodaNow.toString("w") + "\n" )
        myLogMessage.append( "        D     day of year                 = " + myJodaNow.toString("D") + "\n" )
        myLogMessage.append( "        e     day of week                 = " + myJodaNow.toString("e") + "\n" )
        myLogMessage.append( "        EEE   day of week                 = " + myJodaNow.toString("EEEE") + "\n" )
        myLogMessage.append( "        E     day of week                 = " + myJodaNow.toString("E") + "\n" )
        myLogMessage.append( "        dd    Day                         = " + myJodaNow.toString("dd") + "\n" )
        myLogMessage.append( "        d     Day                         = " + myJodaNow.toString("d") + "\n" )
        myLogMessage.append( "        a     halfday of day              = " + myJodaNow.toString("a") + "\n" )
        myLogMessage.append( "        K     hour of halfday (0~11)      = " + myJodaNow.toString("K") + "\n" )
        myLogMessage.append( "        h     clockhour of halfday (1~12) = " + myJodaNow.toString("h") + "\n" )
        myLogMessage.append( "        H     hour of day                 = " + myJodaNow.toString("H") + "\n" )
        myLogMessage.append( "        k     clockhour of day (1~24)     = " + myJodaNow.toString("k") + "\n" )
        myLogMessage.append( "        mm    minute of hour              = " + myJodaNow.toString("mm") + "\n" )
        myLogMessage.append( "        m     minute of hour              = " + myJodaNow.toString("m") + "\n" )
        myLogMessage.append( "        ss    second of minute            = " + myJodaNow.toString("ss") + "\n" )
        myLogMessage.append( "        s     second of minute            = " + myJodaNow.toString("s") + "\n" )
        myLogMessage.append( "        z     time zone                   = " + myJodaNow.toString("z") + "\n" )
        myLogMessage.append( "        ZZZ   time zone offset/id         = " + myJodaNow.toString("ZZZ") + "\n" )
        myLogMessage.append( "        ZZ    time zone offset/id         = " + myJodaNow.toString("ZZ") + "\n" )
        myLogMessage.append( "        Z     time zone offset/id         = " + myJodaNow.toString("Z") + "\n" )
        myLogMessage.append( "        German date string 1              = " + myJodaNow.toString("EEEE, d. MMMM yyyy") + "\n" ) 
        myLogMessage.append( "        German date string 2              = " + myJodaNow.toString("dd.MM.yyyy") + "\n" ) 
        myLogMessage.append( "\n" + "    More patterns see http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html" + "\n" )

    // finally, write the log-entry into the log-file 
        logInfo( myLogFile, myLogMessage.toString )

end // EndOfRule rJodaDateTimeToSubstring_Examples

… and creates an entry in openhab.log with the resulting substrings.

 ----------   myJodaNow (JodaDateTime) to substring - examples   ----------    Source: http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html

    myJodaNow = 2019-02-03T07:16:00.067+01:00 

     pattern  name                          substring
     -------  ----                          ---------
        G     era                         = n. Chr.
        C     century of era (>=0)        = 20
        Y     year of era (>=0)           = 2019
        yyyy  Year                        = 2019
        yyy   Year                        = 19
        MMMM  Month                       = Februar
        MMM   Month                       = Feb
        MM    Month                       = 02
        M     Month                       = 2
        w     week of weekyear            = 5
        D     day of year                 = 34
        e     day of week                 = 7
        EEE   day of week                 = Sonntag
        E     day of week                 = So
        dd    Day                         = 03
        d     Day                         = 3
        a     halfday of day              = AM
        K     hour of halfday (0~11)      = 7
        h     clockhour of halfday (1~12) = 7
        H     hour of day                 = 7
        k     clockhour of day (1~24)     = 7
        mm    minute of hour              = 16
        m     minute of hour              = 16
        ss    second of minute            = 00
        s     second of minute            = 0
        z     time zone                   = MEZ
        ZZZ   time zone offset/id         = Europe/Berlin
        ZZ    time zone offset/id         = +01:00
        Z     time zone offset/id         = +0100
        German date string 1              = Sonntag, 3. Februar 2019
        German date string 2              = 03.02.2019

    More patterns see http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html

#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")
45 Likes

Thanks.

That’s something we should definitely improve and contribute back to the docs when it has filled some of the todos.

A way to do it would be:

val DateTimeType timestamp = new DateTimeType(new DateTime(epoch).toString)

NOTE: Because Joda DateTime and DateTimeType both accept ISO8601 formatted date/time strings it is easy to convert from one to the other using their toString.

There are other ways I’m sure. For example, #3 is basically doing just that. Just replace now.millis with epoch that you got some other way.

I recommend:

val dtt = new DateTimeType(now.toString)

It is probably less efficient but much easier to read and remember.

val readableString = now.toString

Creates the same format as in #1.

val joda = new DateTime(epoch)

Another alternative:

val Number epochFromDateTimeType = new DateTime(MyDateTimeItem.state.toString).millis

Assuming it is ISO 8601 format:

val epoch = new DateTime(MyTimestampString).millis

If it is in ISO 8601 format (as illustrated in the example)

val String MyTimestampString = new DateTime(epoch).toString

Otherwise see 11 for how to adjust the outputted format.

See previous example. To format it:

val String MyTimestampString = now.toString("yyyy-MM-dd'T'HH:mm:ss.SSSZ")

See http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html for full documentation on the format string which I think matches those for SimpleDateFormat.

I haven’t tested any of these but am pretty sure they are all correct. There may be a typo here and there though.

2 Likes

Thank you very much!
I will test it.

Hi, i get the string from a webpage and can´t use it as datetime inside openhab:

2018-10-25 11:00

(The whole string is “2018-10-26 11:00 Uhr”, so i cutted out the charcters 0 to 16.)

How can i convert this into a datetime item?

My rule is:

rule "Pollen datetime"
when
    Item DWD_Pollen_last_update changed or
    System started
then
    PollenLastUpdateDateTime.postUpdate(DWD_Pollen_last_update.state.toString().substring(0, 16))
end

But this doesn´t work, because of the wrong string format, i think? The “T” is missing between date and time?

This is my error:

Cannot convert '2018-10-25 11:00' to a state type which item 'PollenLastUpdateDateTime' accepts: [DateTimeType, UnDefType].

Could you make your OP a wiki, so that we can add the different solutions provided to the OP, please?
Thanks

I am trying to convert from a Human-Readable String to epoch which I have troubles with to read as it does not seem to be in ISO format, how can I specify the format?

I’m not sure regular users have the permissions necessary to turn their post into a wiki. @anfaenger, if you don’t have that option under the three dots menu let me know and I can do it. As a moderator I have that power <evil laugh>.

1 Like

I have much less experience with this. Personally I’d do some String manipulation to convert it to ISO8601 format. See https://stackoverflow.com/questions/999172/how-to-parse-a-date for how to do it with Java core classes and https://stackoverflow.com/questions/12166956/understanding-jodatime-datetime-parsestring-formatter for how to do it with Joda.

This are the options I have:
grafik

I will insert all the suggestions to my first post on the weekend.

If you want I can make it a wiki for you.

Yes please.
Maybe a stupid question: What does this mean?
Couldn’t find anything about it in the docs.

1 Like

A wiki will make the posting editable by anyone.

I’ll make the change right now. I can always change it back.

1 Like

Feel free to ping me, when we are ready to move this to the documentation.

I will help then.
But @rlkoshak is already experienced with moving content to the docs anyway. :slight_smile:

1 Like

Hi @vzorglub,
Just realized, that I’ve been working on the first post and in the meantime you edited it too.
Sorry if I messed up something. I will try to fix it. How can I notice this in the future.

Don’t worry go ahead

I don’t think one can…

This is #11 instead of #5, right?

Looking back I see no difference between 5 and 11. Duplicates?

#5 should get joda from string
#11 should get string from joda

I like #5 :wink::

val DateTime dateTimeFromString = new DateTime("2018-05-05T05:55:55.000-04:00")

Be careful with #11, since it will return a time in your local timezone if done like dateTime.toString.

Here’s one that doesn’t really fit anywhere, but it’s very handy… and the easiest way I’ve found to manually define a specific Joda DateTime.

val DateTime fiveDay = DateTime.now().withMonthOfYear(5).withDayOfMonth(5).withTime(5,55,55,0)

2 Likes