DateTime Handler

Being able to handle date and time expressions is crucial for many applications. Think of bots for travel booking or insurance claims for example. Teneo provides a DateTime Handler that can be used to catch date and time expressions from the user input. The DateTime Handler is included in the Teneo Lexical Resources.

Many date and time expressions have to be interpreted with respect to an anchor date and time. For example, 'tomorrow' denotes different dates depending on when it has been said. The following table contains some examples returned by the DateTime Handler. DateTime expression is what the user said, DateTime representation1 reflects what the DateTime library caught, and DateTime interpretation is a map containing Java LocalDates and Java LocalTimes returned by the interpretation script. Note that the assumed anchor date for the interpretations below was June 8th 2021:

DateTime expression DateTime representation1 DateTime interpretation
Tomorrow [ date: [ named_relative: tomorrow ]] [ date: [ start: [ 2021-06-09 ], type: point ]
  time: [ start: [ 00:00 ]]]
August 19th, 2021 [ date: [ month: 8, day_of_month: 19, year: 2021 ]] [ date: [ start: [ 2021-08-19 ], type: point ]
  time: [ start: [ 00:00 ]]]
Monday at 3 o'clock [ date: [ weekday: mon ],
  time: [ hour:3, meridiem: ampm ]]
[ date: [ start: [ 2021-06-14 ], type: point ],
  time: [ start: [ 03:00, 15:00 ], type: point ]]
Today,
between 15:30 and 16:15
[ date: [ named_relative: today ],
  time: [ relation: between,
  time1: [ hour: 3, meridiem: pm, minute: 30 ],
  time2: [ hour: 4, meridiem: pm, minute: 15 ]]]
[ date: [ start: [ 2021-06-08 ], type: point ],
  time: [ start: [ 15:30 ], end: [ 16:15 ], type: range ]]

As you can see from the examples, there are cases where it is not clear which exact time the user meant. In such cases, the interpretation simply contains both alternatives, "03:00" and "15:00", for "3 o'clock". The ambiguity can then be resolved in the solution, if needed.

Assign the DateTime library

The DateTime library is assigned as soon as you have assigned the Lexical Resource. Visual instructions of how to do that can be found here.

Use the DateTime Handler in your flow

In the following, we will build a simple example flow that makes use of the DateTime Handler to book tables.

We will first set up the basic flow structure:

  1. Create a new flow and call it Book a table.
  2. Name the trigger I want to book a table.
  3. Paste the following learning examples:
    I want to book a table
    I would like to book a table
    I'd like to book a table
    Can I book a table
    Can I make a reservation for a table
    Could I book a table
  4. Click on the 'Generate' button under the 'Match Requirements' section. This will automatically generate a match requirement based on the example specification.
  5. Select the Output node and name it When?. Paste When do you want to book it? into the 'Answers' text field.
  6. Add an empty script node after this output node and name it Interpret date and time.
  7. Add an empty output node after the script node and name it Booking confirmation.

This is how your flow should now look:

Structure of the 'Book a table' flow

In the following, we will add content to the empty nodes and add conditions to the transitions.

Catch Date and Time using language objects

Teneo's DateTime library features numerous different language objects that can be used to catch date and time expressions in the user input. Some of these are illustrated in the table below. Depending on which kind of DateTime expressions you are expecting from the user, i.e only date or only time or only weekdays, you may use different language objects. All language objects in the English DateTime library start with the prefix 'DT'. You can thus simply search for DT* in Teneo's search interface (prefixes for languages other than English can be found in the grey box below), to see all language objects that belong to the DateTime library. We recommend to start from the main object, DT_DATE_TIME.REC and then click your way through the language objects used there and continue doing so until you find the level of granularity that you were looking for.

Language Object Name Coverage Examples
%DT_DATE_TIME.REC Combinations of date and time expressions 18:30 on August 14th, Tomorrow at 5 o'clock
%DT_DATE.REC Date expressions 14.08.2019, Monday next week, five days from now
%DT_TIME.REC Time expressions 13:40, at quarter past eight, at 5pm

The DateTime Handler is available in the following languages:
Chinese, Danish, Dutch, English, French, German, Italian, Japanese, Norwegian, Portuguese, Spanish, and Swedish. More info can be found in the documentation.

In our example, we will use the most general one, DT_DATE_TIME.REC:

  1. Add the flow variable dateTimeRepresentation to your flow and assign it the default value [:].
  2. Select the transition between the output node 'When' and the script node 'Interpret date and time' and click on 'Match conditionally'.
  3. Call the transition Get date and time.
  4. Add a match requirement for 'Condition' and paste the following into the transition's condition field: %DT_DATE_TIME.REC^{dateTimeRepresentation = lob.datetime}.

While it's not fully functional yet, you can now use the output node 'Booking confirmation' to print the intermediate DateTime representation:

  1. Select the 'Booking confirmation' output node.
  2. Paste Great! I booked a table for ${dateTimeRepresentation} in the 'Answers' text field.
  3. Make sure that the transition from the empty script node 'Interpret date and time' to the 'Booking confirmation' node is dashed, indicating that it is not getting any input before continuing.
  4. Hit 'Save'.
  5. Give it a try!

You should now get the following response:

User: I want to book a table
Bot: When do you want to book it?
User: Tomorrow, 6:30.
Bot: Great! I booked a table for [ date: [ named_relative: tomorrow ], time: [ hour: 6, meridiem: ampm, minute: 30 ]]

Note that this is the intermediate DateTime representation that simply reflects what the user said. This representation is not really meant to be printed to the user. We just wanted to show you what happens internally here. For example, you can see that the time expression '6:30' is ambiguous with respect to am/pm. We will show you below how you can resolve such ambiguities. In order to get an actual date and time, this representation needs to be handed over to the interpreter. In the next section, we will show you how this is done.

Interpret Date and Time via script call

Go back to the 'Book a table' flow in edit mode. Then:

  1. Add a flow variable called dateTime and assign it the default value [:].
  2. Select the script node 'Interpret date and time'.
  3. Paste dateTime = datetime.Handler.interpret(dateTimeRepresentation) into the 'Execution Script' field.
  4. Select the Booking confirmation node, replace the existing answer with: Great! I booked a table for ${dateTime.time.start[0]} on ${dateTime.date.start[0]}. This selects the first time point ('time.start[0]') and the first date point ('date.start[0]') from the date time map that the interpreter returned and prints it to the user in a more readable format.
  5. Hit 'Save'.

Now go ahead and give it a try in Try Out! For the dialogue above, you should now get the following answer (assuming you spoke to the bot on June 8th):

User: I want to book a table.
Bot: When do you want to book it?
User: Tomorrow, 6:30.
Bot: Great! I booked a table for 6:30 on 2021-06-08.

Note that the expression 'DateTime.time.start[0]' picks the first available start time that the interpreter script returned. However, as you have seen from the internal representation above, this time expression was ambiguous with respect to am/pm ( 'meridiem : ampm' ). In the following, we illustrate some disambiguation strategies.

Disambiguation strategies

Many time expressions like "6:30" or "3 o'clock" are ambiguous as to whether they denote 'am' or 'pm'. The DateTimeInterpreter returns both possible times for such expressions: "06:30" and "18:30". However, for many applications it is crucial to correctly disambiguate such time expressions. We will illustrate two disambiguation strategies here that you may use in your solution. One is to disambiguate the representation before sending it to the interpretation script, and the other one is to disambiguate what the interpretation script returned.

Ambiguous representation

The safest way to disambiguate a time expression is to let the user disambiguate it. The fact that time expressions are ambiguous with respect to 'am'/'pm' is visible from the intermediate DateTime representation. Thus, it can be resolved there before it is handed over to the DateTime interpretation script:

  1. Open the 'Book a table' flow in edit mode.
  2. Select the transition 'Get date and time'. Right-click on it and select 'Insert after' → 'Junction' from the drop-down menu.
  3. Add an Output node after the Junction.
  4. Name this new output node Resolve ambiguity.
  5. Draw a transition between 'Resolve ambiguity' and 'Interpret date and time'.

The basic structure is now in place. Time to fill the transitions with content! We will start with the first one.

  1. Select the transition above 'Resolve ambiguity' and click on 'Match conditionally'.
  2. Call it Ambiguous Time.
  3. Add a match requirement for 'Condition' and paste the following, {dateTimeRepresentation.containsKey('time') && dateTimeRepresentation.time.meridiem=='ampm'} into the condition field. This condition is fulfilled if the DateTime expression holds a time and if this times does not hold a specified 'am' or 'pm'.
  4. This transition should be tested first. You can change its order at the bottom of the 'Triggering' panel: change it to 1. Notice how the output nodes change place once the ordering is changed.

Now that we are done with our first transition, it is time for the second one.

  1. Select the transition under 'Resolve ambiguity' and click on 'Match conditionally'.
  2. Name this transition Resolve am/pm.
  3. Add a match requirement for 'Condition' and paste the following. Depending on what the user said, this condition sets the meridiem to 'am' or 'pm'.
    (am)^{dateTimeRepresentation.time.meridiem='am'}
    /
    (pm)^{dateTimeRepresentation.time.meridiem='pm'}
  4. Hit 'Save'.

That's it. You should now get the following dialogue:

User: I want to book a table.
Bot: When do you want to book it?
User: Tomorrow, 6:30.
Bot Do you mean 6 am or pm?
User: pm
Bot: Great! I booked a table for 18:30 on 2021-06-09.

Ambiguous interpretation

In the previous section we have shown how the decision about 'am' vs 'pm' can be returned to the user to request clarification. Sometimes however, the context only allows one interpretation and in such cases we should avoid asking the user. Let's say that we still want to book a table. As the restaurant is open from 5pm to 11pm, all table bookings will denote 'pm'. In cases where the DateTime interpreter returns more than one possible time, we will thus always select 'pm'.

This is how we will go about: before returning the DateTime interpreter's answer to the user, we will select the 'pm' time, if applicable. Whenever two times are returned, the second one (at index 1) is always 'pm'. If this is the case, we copy the 'pm' value from index 1 to index 0. This way, we do not have to modify the output node:

  1. We start from the 'Book a table' flow that we created above (without disambiguation of the representation) in edit mode.
  2. Select the 'Interpret Date and Time' script node. and paste the following after its current content:
    if (dateTime.time.start[1]) {
      dateTime.time.start[0] = dateTime.time.start[1]
    }
  3. Hit 'Save'.

That's it. When talking to your bot, you should now always get the 'pm' interpretation for ambiguous time expressions like 6:30:

User: I want to book a table.
Bot: When do you want to book it?
User: Tomorrow, 6:30.
Bot: Great! I booked a table for 18:30 on 2021-06-09.

Ambiguous interpretation direction

For some date and time expressions the interpretation depends on the conversation context. For example, the expression 'on Wednesday' may be interpreted as a date in the past or in the future. The following example dialogue happened on Friday, June 25th 2021:

User: I arrived in Barcelona on Wednesday.
Bot: You mean 2021-06-30, right?

User: I will arrive in Barcelona on Wednesday.
Bot: You mean 2021-06-30, right?

By default, the interpretation direction is 'forward', so 'on Wednesday' would be interpreted as the following Wednesday. It is possible to change this interpretation direction to 'back' in order to point to the previous Wednesday instead. We will show you how to edit the default interpretation direction in the next section. Here, we will show you how you can take advantage of the verb tense to make an appropriate decision. One component of the Teneo Input Processing Chain assigns part-of-speech tags to all words of the user input. We will make use of one of these tags, %$PAST.POS, which indicates that a verb occurred in past tense in order to set the interpretation direction to 'back'. In the following, we build a small example flow in order to illustrate the functionality:

  1. In the main solution window, click on 'Flow' to create a new flow.
  2. Name the flow Disambiguate Interpretation Direction.
  3. Add a flow variable called dateTimeRepresentation and assign it the default value [:]. It will store the representation of the DateTime expression uttered by the user.
  4. Add a flow variable called pastTense and assign it the default value null. This one will store whether or not a past tense tag was found in the user input.
  5. Add a flow variable called dateTime and assign it the default value null. It will store the DateTime interpretation.

Now that the variables are created, let us proceed with building the flow structure.

  1. Name the trigger DateTime + Tense.
  2. Add a match requirement for 'Condition' and paste the following.
    %DT_DATE_TIME.REC^{dateTimeRepresentation=lob.datetime} 
    &^ 
    (%$PAST.POS^{pastTense = true}):o
  3. Add a script node, name it Call Interpreter and make it the Start node by clicking 'Set Start Node' in the top ribbon.
  4. Paste the following into the script node:
    dateTime = pastTense?datetime.Handler.interpret(dateTimeRepresentation, "back"): datetime.Handler.interpret(dateTimeRepresentation)

    This will call the interpreter with the interpretation direction 'back' if the pastTense variable has been set in the Trigger condition. Otherwise, the interpreter will be called with the default interpretation direction "forward" (which does not need to be explicitly specified).

  5. Connect 'Call Interpreter' to the existing output node.
  6. Paste You mean ${DateTime.date.start[0]}, right? into the Output node and name it You mean ... right?.
  7. Hit 'Save'.

Edit default settings

Anchor date
By default, the DateTime interpretation script assumes the current day to be the anchor date for relative date expressions like 'tomorrow' or 'Monday next week'. Should it be necessary for you to change this anchor date, you can do so by adding the desired anchor date to the DateTime interpreter call. Say you want "14th of August 2019" to be your anchor date. Then, the DateTime interpreter call in the script node of your flow will look like this: dateTime = datetime.Handler.interpret(dateTimeRepresentation, "2019-08-14"). Please note that the new anchor date must be passed in ISO-8601 format and put into quotes (""). If you want to change the anchor date globally for the whole solution, go to the global pre-processing script and paste datetime.Handler.setAnchor("2019-08-14"). Note that you have to restart the conversation before changes in the global script will apply.

Anchor time
You may not only modify the anchor date, but also the anchor time, if necessary. In order to do so, simply add the time to the anchor date by keeping the ISO-8601 format, like so: dateTime = datetime.Handler.interpret(dateTimeRepresentation, "2018-11-21T1630"). The first two digits after the 'T' denote the hour and the last two digits the minutes of the anchor time.

Interpretation direction
For some expressions, like 'on Monday' or 'in September', the interpretation depends on the context. They may denote a date in the past or in the future. Depending on the domain for which you created your bot, different interpretation directions might be appropriate. For example, in our table booking domain, one can assume that all dates occurring in the conversation denote future events. However, in an insurance claim domain a backwards interpretation direction is more suitable. By default the interpretation direction is set to 'forward', but you can change it to 'back' by adding it to the DateTime interpreter call: dateTime = datetime.Handler.interpret(dateTimeRepresentation, "back").

Both
You may also change both the anchor date and the interpretation direction at once: dateTime = datetime.Handler.interpret(dateTimeRepresentation, "2018-11-21T1630", "back").

Advanced Usage

The DateTime Handler covers a wide range of different date and time expressions and it always returns the most plausible interpretation. However, if necessary, you may adapt the DateTime Handler to, for example, recognize named days or adjust the interpretation of existing dates and times. For more information, read this page on Advanced Usage.

 


  1. See DateTime Handler Reference for more details on the language-independent DateTime representation.

Was this page helpful?