Teneo Developers

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 expressionDateTime representation1DateTime 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
  1. Click on the Plus icon under the trigger, followed by 'Match' and 'Generate' to automatically generate a match based on the example specification.
  2. Name the Output node When?. Add the answer When do you want to book it? to this node.
  3. Click on the Plus icon under the output node and select 'Continue with' followed by 'Transaction'.
  4. Now click on the Plus icon just above the empty output node that was just added and select 'Continue with' and 'Script'.
  5. Name the last output node Booking confirmation.

This is how your flow should now look:

1-new flow

Next, 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, e.g. only date or only time or only weekdays, you may want to 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 starting with the main object, DT_DATE_TIME.REC and then clicking your way through the language objects used there and continuing to do so until you find the level of granularity that you are looking for.

Language Object NameCoverageExamples
%DT_DATE_TIME.RECCombinations of date and time expressions18:30 on August 14th, Tomorrow at 5 o'clock
%DT_DATE.RECDate expressions14.08.2019, Monday next week, five days from now
%DT_TIME.RECTime expressions13: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. Click on the Plus icon above the script node and select 'Match' and 'TLML Syntax'.
  3. Paste the following into the transition's TLML Syntax field: %DT_DATE_TIME.REC^{dateTimeRepresentation = lob.datetime}.
  4. Name the User Intent node Get date and time.

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

  1. Set the answer text in the 'Booking confirmation' output node to Great! I booked a table for ${dateTimeRepresentation}.
  2. Hit 'Save'.
  3. Give it a try in Tryout!

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. Navigate to the script node.
  3. Paste dateTime = datetime.Handler.interpret(dateTimeRepresentation) into the script field.
  4. In 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 Tryout! For the dialogue above, you should now get an answer that looks like the following (with the date varying depending on when you speak to the bot):

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 2022-10-01.

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.
  2. Click on the Plus icon below the 'Get date and time' transition and select 'Split path' followed by 'transaction element' and 'Output'.
  3. Name this new output node Resolve ambiguity.
  4. Give it the answer Do you mean ${dateTimeRepresentation.time.hour} am or pm?.
  5. Draw a transition between 'Resolve ambiguity' and the script node.

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

  1. Click on the Plus icon above 'Resolve ambiguity' and add a 'Match' for 'TLML Syntax'.
  2. Paste the following into the 'TLML Syntax' field: {dateTimeRepresentation.containsKey('time') && dateTimeRepresentation.time.meridiem=='ampm'}. This condition is fulfilled if the DateTime expression holds a time and if this times does not hold a specified 'am' or 'pm'.

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

  1. Click on the Plus icon under 'Resolve ambiguity' and add a 'Match' for 'TLML Syntax'.
  2. Paste in 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'}
  3. 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 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, October 14th 2022:

User: I arrived in Barcelona on Wednesday.
Bot: You mean 2022-10-19, right?

User: I will arrive in Barcelona on Wednesday.
Bot: You mean 2022-10-19, 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. 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. Click on the Plus icon beneath the trigger, followed by 'Match' and 'TLML Syntax', and paste the following: %DT_DATE_TIME.REC^{dateTimeRepresentation=lob.datetime} &^ (%$PAST.POS^{pastTense = true}):o
  3. Click on the Plus icon above the output and select 'Continue with' and 'Script'.
  4. Name the script node Call Interpreter.
  5. Paste the following into the script node. 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).
    dateTime = pastTense?datetime.Handler.interpret(dateTimeRepresentation, "back"): datetime.Handler.interpret(dateTimeRepresentation)
  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.