datefind 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
datefind/__init__.py ADDED
@@ -0,0 +1,44 @@
1
+ """Datefind."""
2
+
3
+ from collections.abc import Generator
4
+ from typing import Literal
5
+ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
6
+
7
+ from tzlocal import get_localzone
8
+
9
+ from datefind.constants import FirstNumber
10
+ from datefind.datefind import Date, DateFind
11
+
12
+
13
+ def find_dates(
14
+ text: str, first: Literal["month", "day", "year"] = "month", tz: str = ""
15
+ ) -> Generator[Date, None, None]:
16
+ """Search text for dates and return them as Date objects.
17
+
18
+ Parse the input text to locate and extract dates in various formats. When ambiguous dates are encountered (e.g. 01/02/03), use the `first` parameter to determine the order of month/day/year. Convert all dates to the specified timezone.
19
+
20
+ Args:
21
+ text (str): The text to search for dates
22
+ first (Literal["month", "day", "year"]): The position of month/day/year when parsing ambiguous dates. Defaults to "month"
23
+ tz (str): The timezone name (e.g. "America/New_York") to localize dates. Uses system timezone if empty. Defaults to ""
24
+
25
+ Returns:
26
+ Generator[Date, None, None]: A generator yielding Date objects for each date found in the text
27
+
28
+ Raises:
29
+ ValueError: If an invalid first parameter or timezone is provided
30
+ """
31
+ try:
32
+ first_number = FirstNumber[first.upper()]
33
+ except KeyError as e:
34
+ msg = f"Invalid first number: {first}"
35
+ raise ValueError(msg) from e
36
+
37
+ try:
38
+ timezone = ZoneInfo(tz) if tz else get_localzone()
39
+ except ZoneInfoNotFoundError as e:
40
+ msg = f"Invalid timezone: {tz}"
41
+ raise ValueError(msg) from e
42
+
43
+ datefind = DateFind(text=text, tz=timezone, first_number=first_number)
44
+ return datefind.find_dates()
datefind/constants.py ADDED
@@ -0,0 +1,82 @@
1
+ """Constants for datefind."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class FirstNumber(Enum):
7
+ """Enum for the first number to find in ambiguous dates."""
8
+
9
+ MONTH = "month"
10
+ DAY = "day"
11
+ YEAR = "year"
12
+
13
+
14
+ class DayToNumber(Enum):
15
+ """Enum for the day to number."""
16
+
17
+ FIRST = 1
18
+ SECOND = 2
19
+ THIRD = 3
20
+ FOURTH = 4
21
+ FIFTH = 5
22
+ SIXTH = 6
23
+ SEVENTH = 7
24
+ EIGHTH = 8
25
+ NINTH = 9
26
+ TENTH = 10
27
+ ELEVENTH = 11
28
+ TWELFTH = 12
29
+ THIRTEENTH = 13
30
+ FOURTEENTH = 14
31
+ FIFTEENTH = 15
32
+ SIXTEENTH = 16
33
+ SEVENTEENTH = 17
34
+ EIGHTEENTH = 18
35
+ NINETEENTH = 19
36
+ TWENTIETH = 20
37
+ TWENTYFIRST = 21
38
+ TWENTYSECOND = 22
39
+ TWENTYTHIRD = 23
40
+ TWENTYFOURTH = 24
41
+ TWENTYFIFTH = 25
42
+ TWENTYSIXTH = 26
43
+ TWENTYSEVENTH = 27
44
+ TWENTYEIGHTH = 28
45
+ TWENTYNINTH = 29
46
+ THIRTIETH = 30
47
+ THIRTYFIRST = 31
48
+
49
+
50
+ CENTURY = 20 # Used for years provided with two digits
51
+ SEP_CHARS = r"/\:\-\,\.\s\_\+\@ " # Define separator chars for reuse
52
+ SEP_CHARS_REGEX = rf"[{SEP_CHARS}]*?"
53
+ DD_FLEXIBLE = r"[12][0-9]|3[01]|0?[1-9]"
54
+ DD = r"0[1-9]|[12][0-9]|3[01]"
55
+ MM = r"0[1-9]|1[012]"
56
+ MM_FLEXIBLE = r"1[012]|0?[1-9]"
57
+ YYYY = r"19\d\d|20\d\d"
58
+ YYYY_FLEXIBLE = rf"{YYYY}|\d\d"
59
+ JANUARY = r"january|jan?"
60
+ FEBRUARY = r"february|feb?"
61
+ MARCH = r"march|mar?"
62
+ APRIL = r"april|apr?"
63
+ MAY = r"may"
64
+ JUNE = r"june?"
65
+ JULY = r"july?"
66
+ AUGUST = r"august|aug?"
67
+ SEPTEMBER = r"september|sept?"
68
+ OCTOBER = r"october|oct?"
69
+ NOVEMBER = r"november|nov?"
70
+ DECEMBER = r"december|dec?"
71
+ MONTH = rf"{JANUARY}|{FEBRUARY}|{MARCH}|{APRIL}|{MAY}|{JUNE}|{JULY}|{AUGUST}|{SEPTEMBER}|{OCTOBER}|{NOVEMBER}|{DECEMBER}"
72
+ DAY_NUMBERS = rf"first|second|third|fourth|fifth|sixth|seventh|eighth|ninth|tenth|eleventh|twelfth|thirteenth|fourteenth|fifteenth|sixteenth|seventeenth|eighteenth|nineteenth|twentieth|twenty{SEP_CHARS_REGEX}first|twenty{SEP_CHARS_REGEX}second|twenty{SEP_CHARS_REGEX}third|twenty{SEP_CHARS_REGEX}fourth|twenty{SEP_CHARS_REGEX}fifth|twenty{SEP_CHARS_REGEX}sixth|twenty{SEP_CHARS_REGEX}seventh|twenty{SEP_CHARS_REGEX}eighth|twenty{SEP_CHARS_REGEX}ninth|thirtieth|thirty{SEP_CHARS_REGEX}first"
73
+ DIGIT_SUFFIXES = r"st|th|rd|nd"
74
+ TODAY = r"today'?s?"
75
+ YESTERDAY = r"yesterday'?s?"
76
+ TOMORROW = r"tomorrow'?s?"
77
+ LAST_WEEK = rf"last{SEP_CHARS_REGEX}week'?s?"
78
+ NEXT_WEEK = rf"next{SEP_CHARS_REGEX}week'?s?"
79
+ WEEKDAYS = (
80
+ r"monday|tuesday|wednesday|thursday|friday|saturday|sunday|mon|tues?|wed|thu|thurs?|fri|sat|sun"
81
+ )
82
+ TIME_PERIOD = r"am|pm"
datefind/datefind.py ADDED
@@ -0,0 +1,192 @@
1
+ """Datefind is a Python library for finding dates in text."""
2
+
3
+ from collections.abc import Generator
4
+ from dataclasses import dataclass
5
+ from datetime import datetime, timedelta
6
+ from zoneinfo import ZoneInfo
7
+
8
+ import regex as re
9
+ from rich.console import Console
10
+
11
+ from datefind.utils import DatePatternFactory
12
+
13
+ from .constants import (
14
+ APRIL,
15
+ AUGUST,
16
+ CENTURY,
17
+ DECEMBER,
18
+ FEBRUARY,
19
+ JANUARY,
20
+ JULY,
21
+ JUNE,
22
+ MARCH,
23
+ MAY,
24
+ NOVEMBER,
25
+ OCTOBER,
26
+ SEP_CHARS,
27
+ SEPTEMBER,
28
+ DayToNumber,
29
+ FirstNumber,
30
+ )
31
+
32
+ console = Console()
33
+
34
+
35
+ @dataclass
36
+ class Date:
37
+ """Store information about a date found in text.
38
+
39
+ Properties:
40
+ date (datetime): The parsed datetime object
41
+ text (str): The full text that was searched
42
+ match (str): The specific text that matched the date pattern
43
+ span (tuple[int, int]): The start and end positions of the match in the text
44
+ """
45
+
46
+ date: datetime
47
+ text: str
48
+ match: str
49
+ span: tuple[int, int]
50
+
51
+
52
+ class DateFind:
53
+ """Find and parse dates within text using pattern matching.
54
+
55
+ Searches text for date patterns and returns Date objects containing the parsed datetime, original text, matched pattern, and location of the match.
56
+
57
+ Args:
58
+ text (str): The text to search for dates
59
+ tz (ZoneInfo): The timezone to use for the parsed dates
60
+ first_number (FirstNumber): Whether the first number in a date pattern represents the day or month
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ text: str,
66
+ tz: ZoneInfo,
67
+ first_number: FirstNumber,
68
+ ):
69
+ self.text = text
70
+ self.tz = tz
71
+ self.first_number = first_number
72
+ self.base_date = datetime(datetime.now(self.tz).year, 1, 1, tzinfo=self.tz)
73
+ self.factory = DatePatternFactory(first_number=self.first_number)
74
+
75
+ def find_dates(self) -> Generator[Date, None, None]:
76
+ """Search the text for date patterns and parse them into Date objects.
77
+
78
+ Parse dates in various formats including relative dates like 'today', 'yesterday', 'next week' as well as explicit dates with day, month and year components. All dates are converted to datetime objects in the timezone specified during initialization.
79
+
80
+ Yields:
81
+ Generator[Date, None, None]: A sequence of Date objects containing the parsed datetime, original text, matched pattern, and location of the match.
82
+ """
83
+ regex = self.factory.make_regex()
84
+ matches = regex.finditer(self.text)
85
+
86
+ for match in matches:
87
+ if match.groupdict().get("today"):
88
+ as_dt = datetime.now(self.tz)
89
+
90
+ elif match.groupdict().get("yesterday"):
91
+ as_dt = datetime.now(self.tz) - timedelta(days=1)
92
+
93
+ elif match.groupdict().get("tomorrow"):
94
+ as_dt = datetime.now(self.tz) + timedelta(days=1)
95
+
96
+ elif match.groupdict().get("last_week"):
97
+ as_dt = datetime.now(self.tz) - timedelta(days=7)
98
+
99
+ elif match.groupdict().get("next_week"):
100
+ as_dt = datetime.now(self.tz) + timedelta(days=7)
101
+
102
+ else:
103
+ day = self._day_to_number(match=match) or 1
104
+ month = self._month_to_number(match=match) or datetime.now(self.tz).month
105
+ year = self._year_to_number(match=match) or datetime.now(self.tz).year
106
+
107
+ as_dt = datetime(year=int(year), month=month, day=day, tzinfo=self.tz)
108
+
109
+ date = Date(
110
+ date=as_dt,
111
+ text=self.text,
112
+ match=match.group(),
113
+ span=match.span(),
114
+ )
115
+ yield date
116
+
117
+ @staticmethod
118
+ def _year_to_number(match: re.Match) -> int:
119
+ """Parse a year string from a regex match into a numeric year.
120
+
121
+ Convert 2-digit years to 4-digit years by prepending the current century. For example, '23' becomes '2023'.
122
+
123
+ Args:
124
+ match (re.Match): The regex match containing year information in named groups
125
+
126
+ Returns:
127
+ int: The numeric year value
128
+ """
129
+ if year := match.groupdict().get("year"):
130
+ if len(year) == 2: # noqa: PLR2004
131
+ return int(f"{CENTURY}{year}")
132
+ return int(year)
133
+
134
+ return None
135
+
136
+ @staticmethod
137
+ def _month_to_number(match: re.Match) -> int:
138
+ """Parse a month string from a regex match into a numeric month (1-12).
139
+
140
+ Convert both text month names (e.g. "January", "Feb") and numeric months from the regex match. Text matching is case-insensitive.
141
+
142
+ Args:
143
+ match (re.Match): The regex match containing month information in named groups
144
+
145
+ Returns:
146
+ int: The numeric month value (1-12)
147
+ """
148
+ month_patterns = {
149
+ JANUARY: 1,
150
+ FEBRUARY: 2,
151
+ MARCH: 3,
152
+ APRIL: 4,
153
+ MAY: 5,
154
+ JUNE: 6,
155
+ JULY: 7,
156
+ AUGUST: 8,
157
+ SEPTEMBER: 9,
158
+ OCTOBER: 10,
159
+ NOVEMBER: 11,
160
+ DECEMBER: 12,
161
+ }
162
+
163
+ if month_as_text := match.groupdict().get("month_as_text"):
164
+ for pattern, number in month_patterns.items():
165
+ if re.search(pattern, month_as_text, re.IGNORECASE):
166
+ return number
167
+
168
+ if month := match.groupdict().get("month"):
169
+ return int(month)
170
+
171
+ return None
172
+
173
+ @staticmethod
174
+ def _day_to_number(match: re.Match) -> int | None:
175
+ """Parse a day string from a regex match into a numeric day of month.
176
+
177
+ Convert both text day representations (e.g. "1st", "2nd") and numeric days from the regex match.
178
+
179
+ Args:
180
+ match (re.Match): The regex match containing day information in named groups
181
+
182
+ Returns:
183
+ int | None: The numeric day value, or None if no day found
184
+ """
185
+ if day_as_text := match.groupdict().get("day_as_text"):
186
+ day_as_text = re.sub(rf"[{SEP_CHARS}]", "", day_as_text)
187
+ return DayToNumber[day_as_text.upper()].value
188
+
189
+ if day := match.groupdict().get("day"):
190
+ return int(day)
191
+
192
+ return None
datefind/py.typed ADDED
File without changes
@@ -0,0 +1,5 @@
1
+ """Utilities for datefind."""
2
+
3
+ from .date_factory import DatePatternFactory
4
+
5
+ __all__ = ["DatePatternFactory"]
@@ -0,0 +1,163 @@
1
+ """Create regex patterns for matching dates in text.
2
+
3
+ Contains pattern definitions and factory classes for building flexible date-matching regular expressions that handle various date formats and styles.
4
+ """
5
+
6
+ from typing import assert_never
7
+
8
+ import regex as re
9
+
10
+ from datefind.constants import (
11
+ DAY_NUMBERS,
12
+ DD,
13
+ DD_FLEXIBLE,
14
+ DIGIT_SUFFIXES,
15
+ LAST_WEEK,
16
+ MM,
17
+ MM_FLEXIBLE,
18
+ MONTH,
19
+ NEXT_WEEK,
20
+ SEP_CHARS,
21
+ TODAY,
22
+ TOMORROW,
23
+ YESTERDAY,
24
+ YYYY,
25
+ YYYY_FLEXIBLE,
26
+ FirstNumber,
27
+ )
28
+
29
+ DD = f"(?P<day>{DD})(?:{DIGIT_SUFFIXES})?"
30
+ DD_AS_TEXT = f"(?P<day_as_text>{DAY_NUMBERS})"
31
+ DD_FLEXIBLE = f"(?P<day>{DD_FLEXIBLE})(?:{DIGIT_SUFFIXES})?"
32
+ END = rf"(?![0-9]|[{SEP_CHARS}][0-9])"
33
+ LAST_WEEK = rf"(?P<last_week>{LAST_WEEK})"
34
+ MM = rf"(?P<month>{MM})"
35
+ MM_FLEXIBLE = rf"(?P<month>{MM_FLEXIBLE})"
36
+ MONTH_AS_TEXT = f"(?P<month_as_text>{MONTH})"
37
+ NEXT_WEEK = rf"(?P<next_week>{NEXT_WEEK})"
38
+ SEPARATOR = rf"[{SEP_CHARS}]*?"
39
+ MONTH_DAY_SEPARATOR = rf"{SEPARATOR}(?:the|of)?{SEPARATOR}"
40
+ START = rf"(?<![0-9]|[0-9][{SEP_CHARS}])"
41
+ TODAY = rf"(?P<today>{TODAY})"
42
+ TOMORROW = rf"(?P<tomorrow>{TOMORROW})"
43
+ YESTERDAY = rf"(?P<yesterday>{YESTERDAY})"
44
+ YYYY = rf"(?P<year>{YYYY})"
45
+ YYYY_FLEXIBLE = rf"(?P<year>{YYYY_FLEXIBLE})"
46
+
47
+ ISO8601 = r"(?P<year>-?(\:[1-9][0-9]*)?[0-9]{4})\-(?P<month>1[0-2]|0[1-9])\-(?P<day>3[01]|0[1-9]|[12][0-9])T(?P<hour>2[0-3]|[01][0-9])\:(?P<minute>[0-5][0-9]):(?P<seconds>[0-5][0-9])(?:[\.,]+(?P<microseconds>[0-9]+))?(?P<offset>(?:Z|[+-](?:2[0-3]|[01][0-9])\:[0-5][0-9]))?"
48
+
49
+
50
+ YYYY_MONTH_DD = rf"""
51
+ {START}
52
+ {YYYY}
53
+ {SEPARATOR}
54
+ {MONTH_AS_TEXT}
55
+ {SEPARATOR}
56
+ ({DD_FLEXIBLE}|{DD_AS_TEXT})
57
+ {END}
58
+ """
59
+ DD_MONTH_YYYY = rf"""
60
+ {START}
61
+ ({DD_FLEXIBLE}|{DD_AS_TEXT})
62
+ {MONTH_DAY_SEPARATOR}
63
+ {MONTH_AS_TEXT}
64
+ {SEPARATOR}
65
+ {YYYY}
66
+ {END}
67
+ """
68
+ MONTH_DD_YYYY = rf"""
69
+ {START}
70
+ {MONTH_AS_TEXT}
71
+ {MONTH_DAY_SEPARATOR}
72
+ ({DD_FLEXIBLE}|{DD_AS_TEXT})
73
+ {SEPARATOR}
74
+ {YYYY}
75
+ {END}
76
+ """
77
+ YYYY_MONTH = rf"""
78
+ {START}
79
+ {YYYY}
80
+ {SEPARATOR}
81
+ {MONTH_AS_TEXT}
82
+ {END}
83
+ """
84
+ MONTH_DD = rf"""
85
+ {START}
86
+ {MONTH_AS_TEXT}
87
+ {SEPARATOR}
88
+ ({DD_FLEXIBLE}|{DD_AS_TEXT})
89
+ {END}
90
+ """
91
+ MONTH_YYYY = rf"""
92
+ {START}
93
+ {MONTH_AS_TEXT}
94
+ {SEPARATOR}
95
+ {YYYY}
96
+ {END}
97
+ """
98
+ NATURAL_DATE = rf"""
99
+ {START}
100
+ {TODAY}|{YESTERDAY}|{TOMORROW}|{LAST_WEEK}|{NEXT_WEEK}
101
+ {END}
102
+ """
103
+
104
+
105
+ class DatePatternFactory:
106
+ """Factory for creating date patterns."""
107
+
108
+ def __init__(self, first_number: FirstNumber) -> None:
109
+ """Initialize the factory. Defaults to US date format with month first.
110
+
111
+ Args:
112
+ first_number (FirstNumber): The first number to use.
113
+ """
114
+ self.first_number = first_number
115
+
116
+ @staticmethod
117
+ def _make_pattern(order: list[str]) -> str:
118
+ """Create a regex pattern by joining date components in the specified order.
119
+
120
+ Combine date pattern components (year, month, day) with separators to form a complete regex pattern. The components are joined in the order specified, with each component separated by the SEPARATOR pattern except for the last one.
121
+
122
+ Args:
123
+ order (list[str]): List of date pattern components in the desired order (e.g. [YYYY, MM, DD])
124
+
125
+ Returns:
126
+ str: A raw formatted string containing the complete regex pattern with start/end markers
127
+ """
128
+ pattern = f"{START}"
129
+ for part in order:
130
+ pattern += part
131
+ if part != order[-1]:
132
+ pattern += f"{SEPARATOR}"
133
+ pattern += f"{END}"
134
+ return rf"{pattern}"
135
+
136
+ def make_regex(self) -> re.Pattern:
137
+ """Create a compiled regular expression pattern for matching dates in text.
138
+
139
+ Generate a regex pattern that matches various date formats including natural language dates (today, tomorrow, etc) and numeric dates. The order of numeric components is determined by the first_number setting specified during initialization.
140
+
141
+ Returns:
142
+ re.Pattern: A compiled regex pattern with flags for case-insensitive and verbose matching
143
+ """
144
+ common_patterns = rf"{DD_MONTH_YYYY}|{MONTH_DD_YYYY}|{YYYY_MONTH_DD}|{MONTH_DD}|{YYYY_MONTH}|{MONTH_YYYY}|{NATURAL_DATE}"
145
+
146
+ # Set patterns that rely on the first_number flag.
147
+ match self.first_number:
148
+ case FirstNumber.MONTH:
149
+ yyyy_xx_xx = self._make_pattern([YYYY, MM_FLEXIBLE, DD_FLEXIBLE])
150
+ xx_xx_xx = self._make_pattern([MM_FLEXIBLE, DD_FLEXIBLE, YYYY_FLEXIBLE])
151
+ case FirstNumber.DAY:
152
+ yyyy_xx_xx = self._make_pattern([YYYY, DD_FLEXIBLE, MM_FLEXIBLE])
153
+ xx_xx_xx = self._make_pattern([DD_FLEXIBLE, MM_FLEXIBLE, YYYY_FLEXIBLE])
154
+ case FirstNumber.YEAR:
155
+ yyyy_xx_xx = self._make_pattern([YYYY, MM_FLEXIBLE, DD_FLEXIBLE])
156
+ xx_xx_xx = self._make_pattern([YYYY_FLEXIBLE, MM_FLEXIBLE, DD_FLEXIBLE])
157
+ case _: # pragma: no cover
158
+ assert_never(self.first_number)
159
+
160
+ return re.compile(
161
+ f"{common_patterns}|{yyyy_xx_xx}|{xx_xx_xx}",
162
+ re.IGNORECASE | re.VERBOSE | re.MULTILINE | re.UNICODE | re.DOTALL,
163
+ )
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.4
2
+ Name: datefind
3
+ Version: 0.1.0
4
+ Summary: A python module for locating dates within text.
5
+ Project-URL: Homepage, https://github.com/natelandau/datefind
6
+ Project-URL: Repository, https://github.com/natelandau/datefind
7
+ Author-email: Nate Landau <github@natenate.org>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Python: <=3.13,>=3.11
15
+ Requires-Dist: regex>=2024.11.6
16
+ Requires-Dist: tzlocal>=5.3.1
17
+ Description-Content-Type: text/markdown
18
+
19
+ [![Tests](https://github.com/natelandau/datefind/actions/workflows/test.yml/badge.svg)](https://github.com/natelandau/datefind/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/natelandau/datefind/graph/badge.svg?token=CXstf6zblD)](https://codecov.io/gh/natelandau/datefind)
20
+
21
+ # datefind
22
+
23
+ A python module for locating dates within text. Use this package to search for dates and convert them to datetime objects.
24
+
25
+ Finds dates in many different formats.
26
+
27
+ - All numeric - `2024-01-01`, `01/01/2024`, `01012024`
28
+ - Natural language - `January 1st, 2024`, `March nineteenth, 2024`, `twenty fifth of January`
29
+ - Fuzzy dates - `today`, `yesterday`, `tomorrow`, `last week`, `next week`
30
+
31
+ > [!NOTE]\
32
+ > datefind is designed to be used with year, month, and day only. It does not support hours, minutes, seconds, or microseconds.
33
+
34
+ ## Installation
35
+
36
+ Requires Python 3.11 or higher.
37
+
38
+ ```bash
39
+ pip install datefind
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ```python
45
+ from datefind import find_dates
46
+
47
+ string = "2024-01-01 and 2024-01-02"
48
+
49
+ for date in find_dates(string, tz="America/New_York"):
50
+ print(date)
51
+
52
+ >>> Date(
53
+ date=datetime.datetime(2024, 1, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')),
54
+ text='2024-01-01 and 2024-01-02',
55
+ match='2024-01-01',
56
+ span=(0, 10)
57
+ )
58
+ >>> Date(
59
+ date=datetime.datetime(2024, 1, 2, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')),
60
+ text='2024-01-01 and 2024-01-02',
61
+ match='2024-01-02',
62
+ span=(15, 25)
63
+ )
64
+ ```
65
+
66
+ ### find_dates
67
+
68
+ The `find_dates()` function is the main entry point for the datefind package. It takes a string of text and returns a generator of Date objects.
69
+
70
+ **Arguments**
71
+
72
+ - `text` - The text to search for dates.
73
+ - `first` - The first number to find in ambiguous dates. (one of `month`, `day`, `year`) Default is `month`
74
+ - `tz` - The timezone to use. Defaults to the local timezone.
75
+
76
+ For each date found, a Date object is returned. The Date object has the following properties:
77
+
78
+ - `date` - The datetime object.
79
+ - `text` - The full original text.
80
+ - `match` - The matched portion of the text.
81
+ - `span` - The span of the matched text in the original text.
82
+
83
+ ## Contributing
84
+
85
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
@@ -0,0 +1,10 @@
1
+ datefind/__init__.py,sha256=GpoYC8sodoyz_k3waK_W1vgBfvijaYy-FbdUjyKQv8A,1684
2
+ datefind/constants.py,sha256=052CtVYb-ZR8lfO4012wsR6VmjG2Ovq29GjSiJFiW2M,2419
3
+ datefind/datefind.py,sha256=TzG0wu07aQ0Ott5jvjOjKSH2CcbuPgWQHhKilbRIc-A,6150
4
+ datefind/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ datefind/utils/__init__.py,sha256=XI246xDLK81gQ0mfL9XoUytPX-_XoHJfDgvoEPbex2Y,110
6
+ datefind/utils/date_factory.py,sha256=mvLXt1PDV13nyWeZ3c2AxmAU-dAtGKU4cMTfBVwE6EY,5190
7
+ datefind-0.1.0.dist-info/METADATA,sha256=iPQPi0hP_sl7_xKkP1W9wnkqDXfp_0ry3HRB8tuiOIg,2888
8
+ datefind-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ datefind-0.1.0.dist-info/licenses/LICENSE,sha256=Ur0dbtYXGPg840eCwDlk8pyTq7q16l6-LCK2xZDoU6s,1078
10
+ datefind-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Nate Landau
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.