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

45 statements  

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

2 

3import datetime 

4from typing import Any, Optional, cast 

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 cast( 

43 datetime.datetime, 

44 rrule(freq=WEEKLY, dtstart=start_date, byweekday=weekday, count=1)[0], 

45 ) 

46 

47 

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 

59 

60 

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

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

63 

64 :param value: the input to be checked 

65 

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.") 

71 

72 

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 

83 

84 

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. 

88 

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 

92 

93 """ 

94 

95 if not 1 <= iso_week <= 53: 

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

97 

98 if not 1 <= iso_day <= 7: 

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

100 

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) 

106 

107 return gregorian 

108 

109 

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 ) 

115 

116 

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