Breaking
- Python 3.8 is no longer supported.
- `TimeLength().strict` is deprecated. Its functionality has been replaced with `timelength.FailureFlags` and `timelength.ParserSettings`. Find a full list of options for each in the `README`.
- The default for `FailureFlags` is `FailureFlags.NONE` which means as long as a single value is parsed, it will be considered a success.
- The spiritual successor to `strict = True` is `FailureFlags.ALL`, which means if any failure flag is met, the parsing will be considered a failure.
- `decimal_separators` and `thousand_separators` in the config were renamed to `decimal_delimiters` and `thousand_delimiters`.
- This will only affect you if you utilized a `CustomLocale`.
- `CustomLocale` was removed in favor of just using `Locale`.
- `BufferType` and `buffer_type()` renamed to `StringType` and `string_type()` respectively.
- `TimeLength().result.invalid` and `TimeLength().result.valid` are now tuples of tuples instead of lists of tuples.
- The invalid tuple is of type `tuple[tuple[float | str, FailureFlags], ...]`.
- The valid tuple is of type `tuple[tuple[float, Scale], ...]`.
Features
- `timelength.FailureFlags` is an IntFlag enum which holds all the currently possible reasons for a parse to fail. It is not guaranteed that every flag will be used by every parser, but every flag from all parsers will be present. See the `README` for a full list.
- Set specific failure flags by passing them to your locale.
- Example: `locale = English(flags=(FailureFlags.LONELY_VALUE | FailureFlags.UNKNOWN_TERM))`.
- In this scenario, parsing will only fail if `LONELY_VALUE` or `UNKNOWN_TERM` appear in the `result.invalid` tuple, or if no valid results were parsed.
- Modify parsing behavior by passing a `timelength.ParserSettings()` to `locale.settings`. It is not guaranteed that every setting will be used by every parser, but every setting from all parsers will be present.
- Example: `locale = English(settings=ParserSettings(allow_duplicate_scales=False))`
- `assume_seconds` (`Literal["LAST", "SINGLE", "NEVER"]`): Defaults to `SINGLE`. How to handle no scale being provided. If seconds are disabled, the first loaded scale will take its place.
- `LAST`: Assume seconds only for the last value if no scale is provided for it.
- `SINGLE`: Only assume seconds when a single number is provided.
- `NEVER`: Never assume seconds when no scale is provided.
- `limit_allowed_terms` (`bool`): Defaults to `True`. Prevent terms from the `allowed_terms` list in the config from being used in the middle of a segment/sentence, thus interrupting a value/scale pair. The affected segment will become abandoned and added to the invalid tuple. The terms may still be used at the beginning or end of a segment/sentence. If `False`, The terms will be ignored (within other limitations) and not affect parsing.
- `allow_duplicate_scales` (`bool`): Defaults to `True`. Allow scales to be parsed multiple times. The values will stack. If `False`, the first scale will be used and subsequent duplicates will be added to the invalid tuple.
- `prevent_thousands_extra_digits` (`bool`): Defaults to `True`. Prevent thousands from being parsed if it has more than three digits following the thousand delimiter. Ex: `1,2345`. The number will be added to the invalid tuple. If `False`, the number will be interpreted as `12,345`. This does not allow for thousands to be parsed with less than three digits following the thousand delimiter.
- `allow_thousands_lacking_digits` (`bool`): Defaults to `False`. If `True`, allow thousands to be parsed with less than three digits following the thousand delimiter. Ex: `1,23` will be interpreted as `123`, or `2,455 5` as `24,555` (depending on terms in config).
- `HH:MM:SS` notation (roughly).
- It is not strictly adherent to typical `HH:MM:SS` standards. Any parsable numbers work in each slot, whether they are single digits, multiple digits, or include decimals.
- For example, `2.56:27/3:270:19.2231` is a valid input in place of `2.56 days, 9 hours, 270 minutes, and 19.2231 seconds`.
- It also accepts a single connector, such as a space, between deliminators. Example: `2: 6: 3`.
- Supports up to as many segments as there are scales defined, including custom scales (10 default, `Millisecond` to `Century`).
- The segments are parsed in reverse order, so smallest to largest (the order defined in the config and therefore the order loaded into the `Locale`, may differ for custom defined `Scale`s) to ensure the correct scales are applied.
- EXCEPTION: The default base from which `HH:MM:SS` starts at is `Second`. Any scales (typically of lesser value) listed prior in the config, or appended to the scales list before `Second` in the case of custom scales, will not be utilized unless `Second` is disabled or as many `HH:MM:SS` segments are parsed as there are scales defined.
- `12:30:15` is `12 hours, 30 minutes, and 15 seconds`, NOT `12 minutes, 30 seconds, and 15 milliseconds`.
- `1:10:100:12:52:30:24:60:60:1000` will make use of `Century` to `Millisecond`.
- Delimiters are modifiable in the config under the `hhmmss_delimiters` key. Values must be unique from `decimal_delimiters` and `thousand_delimiters`.
- Quickly access the parsed `TimeLength` as a `timedelta` with `TimeLength().result.delta`.
- Find the relative `datetime` of your input in the past or future with `TimeLength().ago()` and `TimeLength().hence()`.
- Both take an optional `base` argument which is a `datetime`. It defaults to the present in `UTC`.
- Have `TimeLength` guess the `Locale` by passing `Guess()` for the locale.
- It will attempt to parse with all locales currently available and return the best result. The best result is the one with the least invalid results.
- Add custom `Locale`s by appending to `Guess().locales`.
- `TimeLength().locale` will be set to the guessed locale.
- Guess subsequent parses by using `TimeLength().parse(guess_locale=True)` or `TimeLength().parse(guess_locale=Guess())`.
- Perform arithmetic operations on `TimeLength` objects.
- Check the README for a full list of supported operations and types.
- All operations not involving a `datetime` are absolute. For example, `TimeLength("1 minute") - TimeLength("5 minutes")` will return `TimeLength("4 minutes")`.
- Any resultant `TimeLength`s will inherit the `locale` from the first encountered `TimeLength` in the arithmetic.
- Perform comparison operations on `TimeLength` objects.
- Comparison based on their parsed seconds for all but `bool` and `len` comparisons.
- `bool` returns if parsing succeeded based on the `result.success` attribute.
- `len` returns the length of the passed `content` attribute.
- Other things I've likely forgotten.
Improvements
- Seconds will now only be assumed as the input if the `Second` scale is enabled (and assuming is enabled). If `Second` is disabled, the first loaded scale (typically the smallest value wise unless you have defined custom scales out of order) will be used instead.
- Overhauled various `__str__` and `_repr__` functions.
- Added full type hinting.
- Commented areas where the reasoning for doing things is unclear (many places).
- A lot of things are now properties to discourage modifying them.
- Various errors added for incorrect usage.
- Tests expanded to 100% coverage.
- The `ruff` formatter now uses a line-length of 120 instead of the default 88.
- Other things I've likely forgotten.