Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/arrow/util.py: 24%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Helpful functions used internally within arrow."""
3import datetime
4from typing import Any, Optional, cast
6from dateutil.rrule import WEEKLY, rrule
8from arrow.constants import (
9 MAX_ORDINAL,
10 MAX_TIMESTAMP,
11 MAX_TIMESTAMP_MS,
12 MAX_TIMESTAMP_US,
13 MIN_ORDINAL,
14)
17def next_weekday(
18 start_date: Optional[datetime.date], weekday: int
19) -> datetime.datetime:
20 """Get next weekday from the specified start date.
22 :param start_date: Datetime object representing the start date.
23 :param weekday: Next weekday to obtain. Can be a value between 0 (Monday) and 6 (Sunday).
24 :return: Datetime object corresponding to the next weekday after start_date.
26 Usage::
28 # Get first Monday after epoch
29 >>> next_weekday(datetime(1970, 1, 1), 0)
30 1970-01-05 00:00:00
32 # Get first Thursday after epoch
33 >>> next_weekday(datetime(1970, 1, 1), 3)
34 1970-01-01 00:00:00
36 # Get first Sunday after epoch
37 >>> next_weekday(datetime(1970, 1, 1), 6)
38 1970-01-04 00:00:00
39 """
40 if weekday < 0 or weekday > 6:
41 raise ValueError("Weekday must be between 0 (Monday) and 6 (Sunday).")
42 return cast(
43 datetime.datetime,
44 rrule(freq=WEEKLY, dtstart=start_date, byweekday=weekday, count=1)[0],
45 )
48def is_timestamp(value: Any) -> bool:
49 """Check if value is a valid timestamp."""
50 if isinstance(value, bool):
51 return False
52 if not isinstance(value, (int, float, str)):
53 return False
54 try:
55 float(value)
56 return True
57 except ValueError:
58 return False
61def validate_ordinal(value: Any) -> None:
62 """Raise an exception if value is an invalid Gregorian ordinal.
64 :param value: the input to be checked
66 """
67 if isinstance(value, bool) or not isinstance(value, int):
68 raise TypeError(f"Ordinal must be an integer (got type {type(value)}).")
69 if not (MIN_ORDINAL <= value <= MAX_ORDINAL):
70 raise ValueError(f"Ordinal {value} is out of range.")
73def normalize_timestamp(timestamp: float) -> float:
74 """Normalize millisecond and microsecond timestamps into normal timestamps."""
75 if timestamp > MAX_TIMESTAMP:
76 if timestamp < MAX_TIMESTAMP_MS:
77 timestamp /= 1000
78 elif timestamp < MAX_TIMESTAMP_US:
79 timestamp /= 1_000_000
80 else:
81 raise ValueError(f"The specified timestamp {timestamp!r} is too large.")
82 return timestamp
85# Credit to https://stackoverflow.com/a/1700069
86def iso_to_gregorian(iso_year: int, iso_week: int, iso_day: int) -> datetime.date:
87 """Converts an ISO week date into a datetime object.
89 :param iso_year: the year
90 :param iso_week: the week number, each year has either 52 or 53 weeks
91 :param iso_day: the day numbered 1 through 7, beginning with Monday
93 """
95 if not 1 <= iso_week <= 53:
96 raise ValueError("ISO Calendar week value must be between 1-53.")
98 if not 1 <= iso_day <= 7:
99 raise ValueError("ISO Calendar day value must be between 1-7")
101 # The first week of the year always contains 4 Jan.
102 fourth_jan = datetime.date(iso_year, 1, 4)
103 delta = datetime.timedelta(fourth_jan.isoweekday() - 1)
104 year_start = fourth_jan - delta
105 gregorian = year_start + datetime.timedelta(days=iso_day - 1, weeks=iso_week - 1)
107 return gregorian
110def validate_bounds(bounds: str) -> None:
111 if bounds != "()" and bounds != "(]" and bounds != "[)" and bounds != "[]":
112 raise ValueError(
113 "Invalid bounds. Please select between '()', '(]', '[)', or '[]'."
114 )
117__all__ = ["next_weekday", "is_timestamp", "validate_ordinal", "iso_to_gregorian"]