|
6 | 6 | import itertools |
7 | 7 | import re |
8 | 8 | import string |
9 | | -from collections import Counter |
10 | 9 | from datetime import date, datetime, time |
11 | 10 |
|
12 | 11 | # define the supported intrinsic types for each list element read by Tabbed |
@@ -84,6 +83,12 @@ def datetime_formats() -> list[str]: |
84 | 83 | return fmts |
85 | 84 |
|
86 | 85 |
|
| 86 | +# GLOBALS OF FORMATS |
| 87 | +TIME_FORMATS = time_formats() |
| 88 | +DATE_FORMATS = date_formats() |
| 89 | +DATETIME_FORMATS = datetime_formats() |
| 90 | + |
| 91 | + |
87 | 92 | def find_format(astring: str, formats: list[str]) -> str | None: |
88 | 93 | """Returns the date, time, or datetime format of astring. |
89 | 94 |
|
@@ -144,7 +149,7 @@ def is_time(astring: str) -> bool: |
144 | 149 | """ |
145 | 150 |
|
146 | 151 | # all times contain 2 ':' separators |
147 | | - if Counter(astring)[':'] < 2: |
| 152 | + if not re.search(r'^\d{1,2}:\d{2}:\d{2}', astring): |
148 | 153 | return False |
149 | 154 |
|
150 | 155 | # another method to time detect without fmt testing could give speedup |
@@ -200,18 +205,16 @@ def as_numeric(astring: str, decimal: str) -> int | float | complex | str: |
200 | 205 | if decimal != '.': |
201 | 206 | astring = astring.replace(decimal, '.') |
202 | 207 |
|
203 | | - # look for imag part for complex |
204 | | - if re.findall(r'[ij]', astring): |
205 | | - return complex(astring) |
206 | | - |
207 | | - # look for a decimal |
208 | | - if re.findall(r'\.', astring): |
209 | | - return float(astring) |
210 | | - |
211 | 208 | try: |
212 | | - return int(astring) |
| 209 | + x = float(astring) |
213 | 210 | except ValueError: |
214 | | - return astring |
| 211 | + if re.search(r'i|j', astring): |
| 212 | + try: |
| 213 | + return complex(astring) |
| 214 | + except ValueError: |
| 215 | + return astring |
| 216 | + |
| 217 | + return int(x) if x.is_integer() else x |
215 | 218 |
|
216 | 219 |
|
217 | 220 | def as_time(astring: str, fmt: str) -> time | str: |
@@ -332,23 +335,25 @@ def convert( |
332 | 335 | return as_numeric(astring, decimal) |
333 | 336 |
|
334 | 337 | # simple string a subset of ascii |
335 | | - if set(astring.lower()).issubset(string.ascii_letters): |
| 338 | + # dates and times will have a separator that is non-ascii letters or digits |
| 339 | + if set(astring.lower()).issubset(string.ascii_letters + string.digits): |
336 | 340 | return astring |
337 | 341 |
|
| 342 | + # dates and times are slower -- room for improvement |
338 | 343 | # times,dates, datetimes - use asserts for mypy type narrowing |
339 | 344 | if is_time(astring): |
340 | | - fmt = find_format(astring, time_formats()) |
| 345 | + fmt = find_format(astring, TIME_FORMATS) |
341 | 346 | assert isinstance(fmt, str) |
342 | 347 | return as_time(astring, fmt) |
343 | 348 |
|
344 | 349 | if is_date(astring): |
345 | | - fmt = find_format(astring, date_formats()) |
| 350 | + fmt = find_format(astring, DATE_FORMATS) |
346 | 351 | assert isinstance(fmt, str) |
347 | 352 | return as_date(astring, fmt) |
348 | 353 |
|
349 | 354 | if is_datetime(astring): |
350 | 355 | # perform datetime last since it has many fmts to test |
351 | | - fmt = find_format(astring, datetime_formats()) |
| 356 | + fmt = find_format(astring, DATETIME_FORMATS) |
352 | 357 | assert isinstance(fmt, str) |
353 | 358 | return as_datetime(astring, fmt) |
354 | 359 |
|
|
0 commit comments