Skip to content

Instantly share code, notes, and snippets.

@jeffmylife
Last active June 27, 2025 18:38
Show Gist options
  • Save jeffmylife/b2ed8ad8608c6f321ba8f1e04a53d04d to your computer and use it in GitHub Desktop.
Save jeffmylife/b2ed8ad8608c6f321ba8f1e04a53d04d to your computer and use it in GitHub Desktop.
Convert natural language dates and time expressions (like "next Tuesday", "in 2 weeks", "next summer") into Python datetime objects using Claude/LLM. Parse relative dates, fuzzy time expressions, and human language into structured timestamps.

Natural Language Time Parser using LLM

A Python utility that converts natural language time expressions into structured datetime objects using LLMs. Perfect for parsing user inputs like "next Tuesday", "in a few months", or "sometime next summer".

Features:

  • Converts natural language to precise dates
  • Handles relative time expressions
  • Season awareness
  • Configurable word translations (e.g., "few" = 3-4, "several" = 5-6)
  • Returns structured output with confidence indicators
  • Built-in vagueness detection

Output Format:

Returns a dictionary containing:

  • datetime: Parsed datetime object
  • is_time_related: Boolean indicating time relevance
  • reason: Explanation of the parsing logic
  • is_vague: Boolean indicating if the input was too ambiguous

Requirements:

  • langchain-anthropic
  • python-dotenv
  • rich (for pretty printing)

Note: Requires an Anthropic API key set in environment variables.

import json
import os
from datetime import datetime
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, SystemMessage
from dotenv import load_dotenv
from rich import print
from rich.pretty import pprint
def month_to_season(month: str) -> str:
if month in ["December", "January", "February"]:
return "Winter"
elif month in ["March", "April", "May"]:
return "Spring"
elif month in ["June", "July", "August"]:
return "Summer"
elif month in ["September", "October", "November"]:
return "Fall"
else:
raise ValueError(f"Invalid month: {month}")
def parse_natural_time(text: str) -> datetime:
"""
Convert natural language time expressions into datetime objects using LLM.
Args:
text (str): Natural language time expression (e.g. "next Tuesday", "in 2 weeks", etc)
Returns:
datetime: Parsed datetime object
"""
# Load environment variables
load_dotenv()
# Initialize ChatAnthropic
llm = ChatAnthropic(
anthropic_api_key=os.getenv("ANTHROPIC_API_KEY"),
model="claude-3-5-sonnet-20241022",
temperature=0.1
)
current_date = datetime.now().strftime("%Y-%m-%d")
print(f"\n[green]Current date:[/green] {current_date}")
current_day_of_week = datetime.now().strftime("%A")
print(f"\n[green]Current day of the week:[/green] {current_day_of_week}")
current_season = month_to_season(datetime.now().strftime("%B"))
print(f"\n[green]Current season:[/green] {current_season}")
# Create system message
system_msg = f'''
Here's the adjusted prompt to include the desired JSON structure:
You are a time parsing assistant. Convert natural language time expressions into specific ISO format datetime strings (YYYY-MM-DD).
Always return a JSON object containing the following fields:
- **datetime**: The ISO format datetime string (YYYY-MM-DD).
- **is_time_related**: A boolean indicating whether the input is related to time.
- **reason**: A string explaining your reasoning behind the datetime or why it is not time-related.
- **is_vague**: A boolean indicating if the input is too vague to determine an exact datetime.
`is_vague` is NOT whether the user uses vague language but rather when you cannot determine a specific date without guessing.
Use the current time as a reference point.
The current date is: {current_date}
The current day of the week is: {current_day_of_week}
The current season is: {current_season}
Seasons are as follows:
- Winter: December, January, February
- Spring: March, April, May
- Summer: June, July, August
- Fall: September, October, November
Word Translation:
- "few" means 3-4
- "several" means 5-6
- "a while" means 6 months
- "a couple" means 2-3
Guidelines:
- Never return a date in the past (before {current_date}).
- If the input is not time-related, or if it's too vague, populate **is_time_related** as false and/or **is_vague** as true and provide the appropriate **reason** field.
- If the input is an emoji or has emojis, try to see if the emoji is related to time, holidays, or seasons.
- Don't worry about existing holidays or weekends; just follow the user's mention of time closely.
- output format is raw JSON, no other text, no markdown, no code blocks, etc.
Example 1:
User Prompt: "next Tuesday"
With today's date of 2024-12-05 and current day of the week being Thursday
Output:
{{
"datetime": "2024-12-10",
"is_time_related": true,
"reason": "User specified a specific day of the week relative to the current date.",
"is_vague": false
}}
Example 2:
User Prompt: "a few months"
With today's date of 2023-03-15
Output:
{{
"datetime": "2023-06-15",
"is_time_related": true,
"reason": "User specified 'a few months,' interpreted as 3-4 months from the current date.",
"is_vague": true
}}
Example 3:
User Prompt: "someday"
With today's date of 2023-03-15
Output:
{{
"datetime": null,
"is_time_related": false,
"reason": "The input is too vague to determine a specific datetime.",
"is_vague": true
}}
Example 4:
User Prompt: "hey great talking to you. how's it going?"
With today's date of 2024-12-05
Output:
{{
"datetime": null,
"is_time_related": false,
"reason": "The input is irrelevant to time.",
"is_vague": true
}}
'''.strip()
messages = [
SystemMessage(content=system_msg),
HumanMessage(content=text)
]
# Get response from LLM
response = llm.invoke(messages)
# Parse the response into structured output
try:
# Extract JSON from response
json_str = response.content.strip()
print(f"\n[blue]LLM Response:[/blue] {json_str}") # Debugging line to print the raw response
result = json.loads(json_str)
# Return parsed result dictionary
return {
"datetime": datetime.fromisoformat(result["datetime"]) if result["datetime"] else None,
"is_time_related": result["is_time_related"],
"reason": result["reason"],
"is_vague": result["is_vague"]
}
except (ValueError, json.JSONDecodeError) as e:
print(f"\n[red]Error parsing JSON:[/red] {str(e)}") # More specific error message
raise ValueError(f"Could not parse LLM response: {response.content}") from e
if __name__ == "__main__":
import argparse
# Set up argument parser
parser = argparse.ArgumentParser(description='Parse natural language time expressions into datetime objects.')
parser.add_argument('time_expression', type=str, help='Natural language time expression (e.g. "next Tuesday", "in 2 weeks")')
# Parse arguments
args = parser.parse_args()
try:
# Parse the time expression
result = parse_natural_time(args.time_expression)
pprint(result)
except Exception as e:
print(f"\n[red]Error:[/red] {str(e)}")
@jeffmylife
Copy link
Author

tweeted about it

pics related

"maybe next year"

Screenshot 2024-12-16 at 7 44 03 PM

Christmas version "🎄"

Screenshot 2024-12-16 at 7 45 23 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment