Getting Structured Dates From A Conversation

In chatgtd, we often need to extract structured datetimes from a conversation. This is most common when working with the tickler file.

We want to allow users to discuss dates conversationally - in a few days, next month, tomorrow morning, etc. This adds some complexity.

Solution

Within ChatGTD, any commands that reference calendar dates return those dates as natural language.

We then use a separate method language_to_datetime that is basically a souped up version of Python’s dateutil.parser.parse:

def language_to_datetime(
    phrase: str, timezone: ZoneInfo = ZoneInfo("America/New_York")
) -> datetime.datetime:
    """Get datetime from arbitrary english phrase.

    Returned datetime has tzinfo set to `timezone`. 
    We assume this is also the timezone where the phrase was spoken. """

    # LLM turns `phrase` into specific date and time

    # convert that to timezone-aware datetime 

Prompt details

The prompt is the interesting part. We use few shot prompting, with a few extra considerations:

Dates are (sometimes) relative.

The model can’t interpret "tomorrow" accurately unless it knows the current date. So, we give the current time as context at the start of the prompt:

- The current date is `2023-5-5`. 
- The current time is `14:00`. 
- The user's timezone is `America/New_York`. All times are in `America/New_York`. 

Examples must have a range of “current times”.

“Next Tuesday” means something different on Monday and Friday, so we need examples like this:

...

Current Weekday: Monday
Current Date: 2025-01-06
Current Time: 14:00
Input: next tuesday
Date: 2025-01-14
Time: 14:00

Current Weekday: Friday
Current Date: 2023-06-02
Current Time: 14:00
Input: next tuesday
Date: 2023-06-06
Time: 14:00

...

Timezones

For simplicity, we assume the user and the assistant are always in the user’s local timezone. We convert back to a timezone-aware datetime immediately before returning.

ISO 8601 is juuuust complex enough to hide from the model.

We could ask for responses in the format 2000-10-31T01:30:00.000-05:00, but that’s a high surface area for errors. Instead, we ask for outputs in a more human representation:

Date: 2023-01-23
Time: 13:45

We then use regexs to convert that into a datetime.

The final prompt

Here’s the final prompt template (jinja2). I’ve just removed some of the examples.

Your job is to extract structured date and time from human input.
The input may be a specific date or time, or a friendly phrase like "tomorrow morning".

You must consider the current date:
- The current date is `{{ now.strftime("%Y-%m-%d") }}`.
- The current time is `{{ now.strftime("%H:%M") }}`.
- The current weekday is `{{ now.strftime("%A") }}`.
- The user's timezone is `America/New_York`. All times are in `America/New_York`.

You must also follow the following rules:
- Times should be in military time. Do not use AM or PM.
- "next month" always refers to the first of the month. 
- "next week" always refers to Monday. 

Examples: 

Current Weekday: Monday
Current Date: 2025-01-06
Current Time: 14:00
Input: next tuesday
Date: 2025-01-14
Time: 14:00

Current Weekday: Friday
Current Date: 2023-06-02
Current Time: 14:00
Input: next tuesday
Date: 2023-06-06
Time: 14:00

Current Weekday: Thursday
Current Date: 2023-06-08
Current Time: 14:00
Input: friday
Date: 2023-06-09
Time: 14:00

...

Current Weekday: `{{ now.strftime("%A") }}`
Current Date: `{{ now.strftime("%Y-%m-%d") }}`
Current Time: {{ now.strftime("%H:%M") }}`
Input: {{phrase}}