_datetime.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. # Copyright 2022-present, the HuggingFace Inc. team.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Contains utilities to handle datetimes in Huggingface Hub."""
  15. from datetime import datetime, timezone
  16. def parse_datetime(date_string: str) -> datetime:
  17. """
  18. Parses a date_string returned from the server to a datetime object.
  19. This parser is a weak-parser is the sense that it handles only a single format of
  20. date_string. It is expected that the server format will never change. The
  21. implementation depends only on the standard lib to avoid an external dependency
  22. (python-dateutil). See full discussion about this decision on PR:
  23. https://github.com/huggingface/huggingface_hub/pull/999.
  24. Example:
  25. ```py
  26. > parse_datetime('2022-08-19T07:19:38.123Z')
  27. datetime.datetime(2022, 8, 19, 7, 19, 38, 123000, tzinfo=timezone.utc)
  28. ```
  29. Args:
  30. date_string (`str`):
  31. A string representing a datetime returned by the Hub server.
  32. String is expected to follow '%Y-%m-%dT%H:%M:%S.%fZ' pattern.
  33. Returns:
  34. A python datetime object.
  35. Raises:
  36. :class:`ValueError`:
  37. If `date_string` cannot be parsed.
  38. """
  39. try:
  40. # Normalize the string to always have 6 digits of fractional seconds
  41. if date_string.endswith("Z"):
  42. # Case 1: No decimal point (e.g., "2024-11-16T00:27:02Z")
  43. if "." not in date_string:
  44. # No fractional seconds - insert .000000
  45. date_string = date_string[:-1] + ".000000Z"
  46. # Case 2: Has decimal point (e.g., "2022-08-19T07:19:38.123456789Z")
  47. else:
  48. # Get the fractional and base parts
  49. base, fraction = date_string[:-1].split(".")
  50. # fraction[:6] takes first 6 digits and :0<6 pads with zeros if less than 6 digits
  51. date_string = f"{base}.{fraction[:6]:0<6}Z"
  52. return datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.utc)
  53. except ValueError as e:
  54. raise ValueError(
  55. f"Cannot parse '{date_string}' as a datetime. Date string is expected to"
  56. " follow '%Y-%m-%dT%H:%M:%S.%fZ' pattern."
  57. ) from e