Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/arrow/util.py: 26%

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

46 statements  

1"""Helpful functions used internally within arrow.""" 

2 

3import datetime 

4from typing import Any, Optional 

5 

6from dateutil.rrule import WEEKLY, rrule 

7 

8from arrow.constants import ( 

9 MAX_ORDINAL, 

10 MAX_TIMESTAMP, 

11 MAX_TIMESTAMP_MS, 

12 MAX_TIMESTAMP_US, 

13 MIN_ORDINAL, 

14) 

15 

16 

17def next_weekday( 

18 start_date: Optional[datetime.date], weekday: int 

19) -> datetime.datetime: 

20 """Get next weekday from the specified start date. 

21 

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. 

25 

26 Usage:: 

27 

28 # Get first Monday after epoch 

29 >>> next_weekday(datetime(1970, 1, 1), 0) 

30 1970-01-05 00:00:00 

31 

32 # Get first Thursday after epoch 

33 >>> next_weekday(datetime(1970, 1, 1), 3) 

34 1970-01-01 00:00:00 

35 

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 rrule(freq=WEEKLY, dtstart=start_date, byweekday=weekday, count=1)[0] 

43 

44 

45def is_timestamp(value: Any) -> bool: 

46 """Check if value is a valid timestamp.""" 

47 if isinstance(value, bool): 

48 return False 

49 if not isinstance(value, (int, float, str)): 

50 return False 

51 try: 

52 float(value) 

53 return True 

54 except ValueError: 

55 return False 

56 

57 

58def validate_ordinal(value: Any) -> None: 

59 """Raise an exception if value is an invalid Gregorian ordinal. 

60 

61 :param value: the input to be checked 

62 

63 """ 

64 if isinstance(value, bool) or not isinstance(value, int): 

65 raise TypeError(f"Ordinal must be an integer (got type {type(value)}).") 

66 if not (MIN_ORDINAL <= value <= MAX_ORDINAL): 

67 raise ValueError(f"Ordinal {value} is out of range.") 

68 

69 

70def normalize_timestamp(timestamp: float) -> float: 

71 """Normalize millisecond and microsecond timestamps into normal timestamps.""" 

72 if timestamp > MAX_TIMESTAMP: 

73 if timestamp < MAX_TIMESTAMP_MS: 

74 timestamp /= 1000 

75 elif timestamp < MAX_TIMESTAMP_US: 

76 timestamp /= 1_000_000 

77 else: 

78 raise ValueError(f"The specified timestamp {timestamp!r} is too large.") 

79 return timestamp 

80 

81 

82# Credit to https://stackoverflow.com/a/1700069 

83def iso_to_gregorian(iso_year: int, iso_week: int, iso_day: int) -> datetime.date: 

84 """Converts an ISO week date into a datetime object. 

85 

86 :param iso_year: the year 

87 :param iso_week: the week number, each year has either 52 or 53 weeks 

88 :param iso_day: the day numbered 1 through 7, beginning with Monday 

89 

90 """ 

91 

92 if not 1 <= iso_week <= 53: 

93 raise ValueError("ISO Calendar week value must be between 1-53.") 

94 

95 if not 1 <= iso_day <= 7: 

96 raise ValueError("ISO Calendar day value must be between 1-7") 

97 

98 # The first week of the year always contains 4 Jan. 

99 fourth_jan = datetime.date(iso_year, 1, 4) 

100 delta = datetime.timedelta(fourth_jan.isoweekday() - 1) 

101 year_start = fourth_jan - delta 

102 gregorian = year_start + datetime.timedelta(days=iso_day - 1, weeks=iso_week - 1) 

103 

104 return gregorian 

105 

106 

107def validate_bounds(bounds: str) -> None: 

108 if bounds != "()" and bounds != "(]" and bounds != "[)" and bounds != "[]": 

109 raise ValueError( 

110 "Invalid bounds. Please select between '()', '(]', '[)', or '[]'." 

111 ) 

112 

113 

114__all__ = ["next_weekday", "is_timestamp", "validate_ordinal", "iso_to_gregorian"]