Compare datetime item with today

hi,
I struggle with datetime items and their way of formating it. I need to compare today with an datetime item. I don’t care about the time, only date is important.
how to convert the item to match the format of “now.toLocalDate”.

rule "Check if DateTime Item is today"
when
    Item vSwitch1 changed
then
    // Get the current date
    val currentDate1 = now
    val currentDate2 = now.toLocalDate

    // Get the DateTime Item value
    val itemDate1 = (BirthdayDate.state as DateTimeType).getZonedDateTime()
    val itemDate2 = BirthdayDate.state
    //val itemDate2 = BirthdayDate.valueOf(now.toLocalDateTime().toString())
    //val itemDate3 = BirthdayDate.getZonedDateTime.toLocalDate
    

    // Check if the DateTime Item is set to today
    if (currentDate1 == itemDate1)  {
        // Do something because the DateTime Item is set to today
        // For example, trigger another Item or execute some actions
        logInfo("testrule2", "success")
    }
    logInfo("testrule2", "currentdate1: " + currentDate1 + " currentdate2: " + currentDate2 + ", itemdate1: " + itemDate1 + " itemdate2: " + itemDate2 )
end
2023-12-27 12:11:38.243 [INFO ] [.openhab.core.model.script.testrule2] - currentdate1: 2023-12-27T12:11:38.242701233+01:00[Europe/Berlin]currentdate2: 2023-12-27, itemdate1: 2023-12-27T00:00+01:00 itemdate2: 2023-12-27T00:00:00.000+0100

…ok
val itemDate2 = (BirthdayDate.state as DateTimeType).getZonedDateTime.toLocalDate
did the trick :wink:

You could use Ephemeris for this. Define the BirthdayDate(s) in a config file following Actions | openHAB (let’s say it’s defined in /etc/openhab/services/birthdays.xml) the rule would become:

rule "Check if DateTime Item is today"
when
    Item vSwitch1 changed
then
    if(isBankHoliday(0, '/etc/openhab/services/birthdays.xml')) {
        logInfo("testrule2", "success")
    }
end

The link above has an example that can easily be modified to define the dates you are interested in.

The first argument is an offset so you can do stuff like see if tomorrow is a birthday. There are other actions to do stuff like see how many days until a birthday, etc.

I use the icalendar binding as it is more convenient to edit birthdays in a calendar instead of an xml file. (WAF!). I try to do the following but it takes me days already…:

  1. birthday events are formatted this way: “Birthday: John Doe”. I’d like to remove “Birthday:” from the String but struggle to do so. I tried a ton of variants but even the docs let me behind:
val newValue = BirthdayEvent.replace("Birthday", "").toString
also a regex in the item def does not work:
transform="REGEX:(?i)Birthday "
  1. check if today = BirthdayDate
  2. create new String that includes all birthday kids: “Today is John’s and Lisa’s birthday”
  3. use TTS and send string to Alexa.

can you help me with pt1 and 3 pls? it drives me crazy already… thx

How often does one’s birthday change? This is a rare event where one would need to edit the birthdays, no?

BirthdayEvent is an Item? If so it’s not a String. You can’t just replace part of it. But an Item has a state and the state can be converted to a String and you can call replace on that.

BirthdayEvent.state.toString.replace("Birthday: ", "")

That is not correct syntax for using the transform profile in a .item file. If you are working through the UI, the transform goes on the Link, not the Item. And the syntax is still incorrect.

This is solved above.

Presumably you have a separate Item for each kid?

Not without a whole lot more details. What Items? How are the states formatted?

BirthdayEvent is a String Item:
image

BirthdayDate is a DateTime Item:

meanwhile I sorted this out:

rule "Filter Birthday Entry"
when
    Time cron "5 0 0 * * ?" or // Run the rule daily at midnight
    Item vSwitch1 changed
then
    var kid0_ok = ""
    var kid1_ok = ""
    var kid2_ok = ""

    val today = now.toLocalDate
    val BirthdayDate0 = (Kalender_Filter_Geburtstag_Result_0_Begin.state as DateTimeType).getZonedDateTime.toLocalDate
    val BirthdayDate1 = (Kalender_Filter_Geburtstag_Result_1_Begin.state as DateTimeType).getZonedDateTime.toLocalDate
    val BirthdayDate2 = (Kalender_Filter_Geburtstag_Result_2_Begin.state as DateTimeType).getZonedDateTime.toLocalDate

    var kid0 = Kalender_Filter_Geburtstag_Result_0_Title.state.toString.replace("Geburtstag", "")
    var kid1 = Kalender_Filter_Geburtstag_Result_1_Title.state.toString.replace("Geburtstag", "")
    var kid2 = Kalender_Filter_Geburtstag_Result_2_Title.state.toString.replace("Geburtstag", "")
       
    if (today == BirthdayDate2) {
        kid2_ok = kid2
    }
    if (today == BirthdayDate1) {
        kid1_ok = kid1
    }
    if (today == BirthdayDate0) {
        kid0_ok = kid0
        
        var String message = ("Today is " + kid0_ok + " " + kid1_ok + " " + kid2_ok + "`s birthday")  
        //Alexa_EchoDot_TTS.sendCommand(message)
        logInfo("rule birthdayfilter", "message: " + message)

    }
    
end

maybe not the cleanest code but it seems to work…
thx

This is only going to do anything when today BirthdayDate0 is today. If only BirthdayDate1 or BirthdayDate2 are today, nothing happens. If you want it to log if it’s anyone’s birthday put those two lines after the }.

If all three share the same birthday, this rule is way overly complex. Just check the one date and have a hard coded message for everyone.

If the number of kids is going to change over time and/or birthday dates change, this rule won’t work as written and it’s going to be a huge pain to maintain.

Put all the BirthdayDate Items into a Group Birthdays and all the Result Items into a Group called Kalender_Filter_Geburtstag_Results. Then loop through the Group members (note below is easier to implement in Blockly or JS Scripting)

import org.openhab.model.script.ScriptServiceUtil

rule "Filter Birthday Entry"
when
    Time cron "5 0 0 * * ?" or // Run the rule daily at midnight
    Item vSwitch1 changed
then
    // Get all the birthdays that are today
    val todayBirthdays = Birthdays.members.filter[ bd | (bd.state as DateTimeType).getZonedDateTime.toLocalDate == now.toLocalDate]
    // exit if there are no birthdays today
    if(todayBirthdays.size == 0) {
        return;
    }
    // Get the names of all of today's birthdays
    val names = todayBirthdays.map[ bd | Kalender_Filter_Geburtstag_Results.members.findFirst[ item | "Kalender_Filter_Geburtstag_Result_"+bd.name.replace("BirthdayDate", "")+"_Begin" ].state.toString.replace("Geburtstag", "") ]
                              .reduce[ list, name | list + ", " + name]
    val message = "Today is " + names + "'s birthday"
    //Alexa_EchoDot_TTS.sendCommand(message)   
    logInfo("rule birthdayfilter", "message: " + message)
end

The first line filters all the BirthdayDate Items down to only those that are toady.

The second clause immediately exits if there are no birthdays today.

The third line uses a map loops through all of today’s birthdays, extracts the number part of the Item name, then finds the Kalender_Filter_Geburstag_Result Item with the same number in the name, and gets the state, replacing the Geburstag with an empty string, presumably leaving just the name. the result of the map is an array of just the names of the children.

Then we use a reduce on the result of the map to convert the list to a single comma separated String, e.g. "kid1, kid2, kid3".

Finally we create the message and send it.

With this approach, it works with any number of birthdays and it only does something when there is a birthday today.

Note, I just typed in the above, there might be typos. See Design Pattern: Working with Groups in Rules for details on filter, findFirst, map and reduce.

thanks Rick, that rule is nice!
it took some time to find the typo :wink:

val names = todayBirthdays.map[ bd | gKalender_Filter_Geburtstag_Results.members.findFirst[ item | item.name == "KalenderGeburtstage_resultTitle"+bd.name.replace("KalenderGeburtstage_resultStart", "") ].state.toString.replace("Geburtstag", "") ].reduce[ list, name | list + " und " + name ]

if someone comes along this post, the icalendar “filter calender” does only show the upcoming events, but not the current one. as a birthday is mostly an all-day event one should run the rule eg 5min before midnight to work with the rule result the next day… I feed a chat-GPT channel with the names of the birthday kids which provides nice birthday reminders.

here’s the complete rule:

rule "Filter Birthday Entry"
when
    Time cron "0 55 23 1/1 * ? *"  // Run the rule daily 5min to midnight
then
    // Get all the birthdays that are today
    val todayBirthdays = gGeburtstage_Dates.members.filter[ bd | (bd.state as DateTimeType).getZonedDateTime.toLocalDate == now.toLocalDate.plusDays(1) ]
    logInfo("rule birthdayfilter", "message: " + todayBirthdays)// exit if there are no birthdays today
    
    if(todayBirthdays.size == 0) {
        Birthdaykids.sendCommand("no")  
        return;
    }
    // Get the names of all of today's birthdays
    val names = todayBirthdays.map[ bd | gKalender_Filter_Geburtstag_Results.members.findFirst[ item | item.name == "KalenderGeburtstage_resultTitle"+bd.name.replace("KalenderGeburtstage_resultStart", "") ].state.toString.replace("Geburtstag", "") ].reduce[ list, name | list + " und " + name ]
    val message = "Heute ist " + names + "'s Geburtstag"
    //Alexa_EchoDot_TTS.sendCommand(message)   
    Birthdaykids.sendCommand(names)
    logInfo("rule birthdayfilter", "message: " + message)
end