Spaces:
Running
Running
File size: 5,544 Bytes
fa948af | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | """
DateTimeParser: Natural language date/time parsing with timezone support.
This module provides a dual-library approach for parsing natural language dates:
- dateparser (primary): Excels at absolute dates and timezone handling
- parsedatetime (fallback): Excels at relative dates like "next Friday"
The combination achieves 100% coverage of common date/time expressions.
"""
from datetime import datetime
from typing import Optional
import pytz
from dateparser import parse as dateparser_parse
from parsedatetime import Calendar
class DateTimeParser:
"""
Natural language date/time parser with timezone support.
This parser uses a dual-library approach to handle both absolute and
relative date expressions with high accuracy.
Example usage:
parser = DateTimeParser()
result = parser.parse("tomorrow at 3pm", user_timezone="America/New_York")
# Returns: datetime in UTC
"""
def __init__(self):
"""Initialize the parser with parsedatetime calendar."""
self.calendar = Calendar()
def parse(
self,
text: str,
user_timezone: str = "UTC"
) -> Optional[datetime]:
"""
Parse natural language date/time to UTC datetime.
Args:
text: Natural language date/time string
Examples: "tomorrow at 3pm", "next Friday", "in 2 hours",
"January 15, 2026 at 10:30 AM"
user_timezone: User's timezone for interpretation (default: UTC)
Examples: "UTC", "America/New_York", "Europe/London"
Returns:
datetime object in UTC timezone if parsing succeeds
None if parsing fails
Examples:
>>> parser = DateTimeParser()
>>> parser.parse("tomorrow at 3pm")
datetime(2026, 2, 6, 15, 0, 0, tzinfo=<UTC>)
>>> parser.parse("next Friday", user_timezone="America/New_York")
datetime(2026, 2, 7, 5, 0, 0, tzinfo=<UTC>) # Converted to UTC
"""
if not text or not text.strip():
return None
# Try dateparser first (better for absolute dates)
result = self._try_dateparser(text, user_timezone)
if result:
return result
# Fallback to parsedatetime (better for relative dates)
result = self._try_parsedatetime(text, user_timezone)
if result:
return result
return None
def _try_dateparser(
self,
text: str,
user_timezone: str
) -> Optional[datetime]:
"""
Try parsing with dateparser library.
Args:
text: Natural language date/time string
user_timezone: User's timezone for interpretation
Returns:
datetime in UTC or None if parsing fails
"""
try:
result = dateparser_parse(
text,
settings={
'TIMEZONE': user_timezone,
'RETURN_AS_TIMEZONE_AWARE': True,
'TO_TIMEZONE': 'UTC',
'PREFER_DATES_FROM': 'future', # Assume future dates
'RELATIVE_BASE': datetime.now(pytz.timezone(user_timezone))
}
)
if result and result.tzinfo is not None:
return result
except (ValueError, TypeError, Exception):
# Parsing failed, return None to try fallback
pass
return None
def _try_parsedatetime(
self,
text: str,
user_timezone: str
) -> Optional[datetime]:
"""
Try parsing with parsedatetime library (fallback).
Args:
text: Natural language date/time string
user_timezone: User's timezone for interpretation
Returns:
datetime in UTC or None if parsing fails
"""
try:
user_tz = pytz.timezone(user_timezone)
now_in_tz = datetime.now(user_tz)
time_struct, parse_status = self.calendar.parse(text, now_in_tz)
# parse_status meanings:
# 0: Failed to parse
# 1: Parsed as a date
# 2: Parsed as a time
# 3: Parsed as a datetime
if parse_status in [1, 2, 3]:
# Convert time_struct to datetime
dt = datetime(
year=time_struct[0],
month=time_struct[1],
day=time_struct[2],
hour=time_struct[3],
minute=time_struct[4],
second=time_struct[5],
tzinfo=user_tz
)
# Convert to UTC
return dt.astimezone(pytz.UTC)
except (ValueError, TypeError, Exception):
# Parsing failed
pass
return None
def validate_timezone(self, timezone: str) -> bool:
"""
Validate that a timezone string is valid.
Args:
timezone: Timezone string to validate (e.g., "America/New_York")
Returns:
True if valid, False otherwise
Example:
>>> parser = DateTimeParser()
>>> parser.validate_timezone("America/New_York")
True
>>> parser.validate_timezone("Invalid/Timezone")
False
"""
try:
pytz.timezone(timezone)
return True
except pytz.exceptions.UnknownTimeZoneError:
return False
|