Galaxie Shell 0.2.6 documentation


Navigation:   | Index   | Search   | Top   | Up   |
Table of Content: |

© Copyright 2020-2024, Galaxie Shell Team.

Top » Module code » glxshell.lib.iso8601

Source code for glxshell.lib.iso8601

# """ISO 8601 date time string parsing
#
# Basic usage:
# >>> import iso8601
# >>> iso8601.parse_date("2007-01-25T12:00:00Z")
# datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.Utc ...>)
# >>>
#
# """

import datetime
import re

from decimal import Decimal

__all__ = ["parse_date", "ParseError", "UTC", "FixedOffset"]

# Adapted from http://delete.me.uk/2005/03/iso8601.html
ISO8601_REGEX = re.compile(
    r"""
    (?P<year>[0-9]{4})
    (
        (
            (-(?P<monthdash>[0-9]{1,2}))
            |
            (?P<month>[0-9]{2})
            (?!$)  # Don't allow YYYYMM
        )
        (
            (
                (-(?P<daydash>[0-9]{1,2}))
                |
                (?P<day>[0-9]{2})
            )
            (
                (
                    (?P<separator>[ T])
                    (?P<hour>[0-9]{2})
                    (:{0,1}(?P<minute>[0-9]{2})){0,1}
                    (
                        :{0,1}(?P<second>[0-9]{1,2})
                        ([.,](?P<second_fraction>[0-9]+)){0,1}
                    ){0,1}
                    (?P<timezone>
                        Z
                        |
                        (
                            (?P<tz_sign>[-+])
                            (?P<tz_hour>[0-9]{2})
                            :{0,1}
                            (?P<tz_minute>[0-9]{2}){0,1}
                        )
                    ){0,1}
                ){0,1}
            )
        ){0,1}  # YYYY-MM
    ){0,1}  # YYYY only
    $
    """,
    re.VERBOSE,
)


[docs] class ParseError(ValueError): """Raised when there is a problem parsing a date string""" pass
UTC = datetime.timezone.utc
[docs] def FixedOffset( offset_hours: float, offset_minutes: float, name: str ) -> datetime.timezone: return datetime.timezone( datetime.timedelta(hours=offset_hours, minutes=offset_minutes), name )
def parse_timezone( matches: dict[str, str], default_timezone=UTC) -> datetime.timezone: """Parses ISO 8601 time zone specs into tzinfo offsets""" tz = matches.get("timezone", None) if tz == "Z": return UTC # This isn't strictly correct, but it's common to encounter dates without # timezones so I'll assume the default (which defaults to UTC). # Addresses issue 4. if tz is None: return default_timezone sign = matches.get("tz_sign", None) hours = int(matches.get("tz_hour", 0)) minutes = int(matches.get("tz_minute", 0)) description = f"{sign}{hours:02d}:{minutes:02d}" if sign == "-": hours = -hours minutes = -minutes return FixedOffset(hours, minutes, description)
[docs] def parse_date(datestring: str, default_timezone=UTC) -> datetime.datetime: """Parses ISO 8601 dates into datetime objects The timezone is parsed from the date string. However it is quite common to have dates without a timezone (not strictly correct). In this case the default timezone specified in default_timezone is used. This is UTC by default. :param datestring: The date to parse as a string :type datestring: str :param default_timezone: A datetime tzinfo instance to use when no timezone is specified in the datestring. If this is set to None then a naive datetime object is returned. :type default_timezone: str or None :returns: A datetime.datetime instance :raises: ParseError when there is a problem parsing the date or constructing the datetime instance. """ try: m = ISO8601_REGEX.match(datestring) except Exception as e: raise ParseError(e) from e if not m: raise ParseError(f"Unable to parse date string {datestring!r}") # Drop any Nones from the regex matches # TODO: check if there's a way to omit results in regexes groups: dict[str, str] = { k: v for k, v in m.groupdict().items() if v is not None } try: return datetime.datetime( year=int(groups.get("year", 0)), month=int(groups.get("month", groups.get("monthdash", 1))), day=int(groups.get("day", groups.get("daydash", 1))), hour=int(groups.get("hour", 0)), minute=int(groups.get("minute", 0)), second=int(groups.get("second", 0)), microsecond=int( Decimal(f"0.{groups.get('second_fraction', 0)}") * Decimal("1000000.0") ), tzinfo=parse_timezone(groups, default_timezone=default_timezone), ) except Exception as e: raise ParseError(e) from e
def is_iso8601(datestring: str) -> bool: """Check if a string matches an ISO 8601 format. :param datestring: The string to check for validity :returns: True if the string matches an ISO 8601 format, False otherwise """ try: m = ISO8601_REGEX.match(datestring) return bool(m) except Exception as e: raise ParseError(e) from e

Top » Module code » glxshell.lib.iso8601

© Copyright 2020-2024, Galaxie Shell Team.
This page is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License (CC BY-NC-SA 4.0).
Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License.
See History and License for more information.

Last updated on None.
Created using Sphinx 8.0.2.