|
| 1 | +# coding=utf-8 |
| 2 | +# Copyright 2022-present, the HuggingFace Inc. team. |
| 3 | +# |
| 4 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +# you may not use this file except in compliance with the License. |
| 6 | +# You may obtain a copy of the License at |
| 7 | +# |
| 8 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +# |
| 10 | +# Unless required by applicable law or agreed to in writing, software |
| 11 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +# See the License for the specific language governing permissions and |
| 14 | +# limitations under the License. |
| 15 | +"""Contains utilities to handle datetimes in Huggingface Hub.""" |
| 16 | +from datetime import datetime, timezone |
| 17 | + |
| 18 | + |
| 19 | +# Local machine offset compared to UTC |
| 20 | +# Taken from https://stackoverflow.com/a/3168394. |
| 21 | +UTC_OFFSET = datetime.now(timezone.utc).astimezone().utcoffset() |
| 22 | + |
| 23 | + |
| 24 | +def parse_datetime(date_string: str) -> datetime: |
| 25 | + """ |
| 26 | + Parses a date_string returned from the server to a datetime object. |
| 27 | +
|
| 28 | + This parser is a weak-parser is the sense that it handles only a single format of |
| 29 | + date_string. It is expected that the server format will never change. The |
| 30 | + implementation depends only on the standard lib to avoid an external dependency |
| 31 | + (python-dateutil). See full discussion about this decision on PR: |
| 32 | + https://github.com/huggingface/huggingface_hub/pull/999. |
| 33 | +
|
| 34 | + Usage: |
| 35 | + ```py |
| 36 | + > parse_datetime('2022-08-19T07:19:38.123Z') |
| 37 | + datetime.datetime(2022, 8, 19, 7, 19, 38, 123000, tzinfo=timezone.utc) |
| 38 | + ``` |
| 39 | +
|
| 40 | + Args: |
| 41 | + date_string (`str`): |
| 42 | + A string representing a datetime returned by the Hub server. |
| 43 | + String is expected to follow '%Y-%m-%dT%H:%M:%S.%fZ' pattern. |
| 44 | +
|
| 45 | + Returns: |
| 46 | + A python datetime object. |
| 47 | +
|
| 48 | + Raises: |
| 49 | + :class:`ValueError`: |
| 50 | + If `date_string` cannot be parsed. |
| 51 | + """ |
| 52 | + try: |
| 53 | + # Datetime ending with a Z means "UTC". Here we parse the date as local machine |
| 54 | + # timezone and then move it to the appropriate UTC timezone. |
| 55 | + # See https://en.wikipedia.org/wiki/ISO_8601#Coordinated_Universal_Time_(UTC) |
| 56 | + # Taken from https://stackoverflow.com/a/3168394. |
| 57 | + |
| 58 | + dt = datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%S.%fZ") |
| 59 | + dt += UTC_OFFSET # By default, datetime is not timezoned -> move to UTC time |
| 60 | + return dt.astimezone(timezone.utc) # Set explicit timezone |
| 61 | + except ValueError as e: |
| 62 | + raise ValueError( |
| 63 | + f"Cannot parse '{date_string}' as a datetime. Date string is expected to" |
| 64 | + " follow '%Y-%m-%dT%H:%M:%S.%fZ' pattern." |
| 65 | + ) from e |
0 commit comments