Coverage for chalicelib/utils/TimeUTC.py: 51%

84 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:56 +0000

1from calendar import monthrange 

2from datetime import datetime, timedelta 

3 

4import zoneinfo 

5 

6UTC_ZI = zoneinfo.ZoneInfo("UTC") 

7 

8 

9class TimeUTC: 

10 MS_MINUTE = 60 * 1000 

11 MS_HOUR = MS_MINUTE * 60 

12 MS_DAY = MS_HOUR * 24 

13 MS_WEEK = MS_DAY * 7 

14 MS_MONTH = MS_DAY * 30 

15 MS_MONTH_TRUE = monthrange(datetime.now(UTC_ZI).astimezone(UTC_ZI).year, 

16 datetime.now(UTC_ZI).astimezone(UTC_ZI).month)[1] * MS_DAY 

17 RANGE_VALUE = None 

18 

19 @staticmethod 

20 def midnight(delta_days=0): 

21 return int((datetime.now(UTC_ZI) + timedelta(delta_days)) \ 

22 .replace(hour=0, minute=0, second=0, microsecond=0) \ 

23 .astimezone(UTC_ZI).timestamp() * 1000) 

24 

25 @staticmethod 

26 def __now(delta_days=0, delta_minutes=0, delta_seconds=0): 

27 return (datetime.now(UTC_ZI) + timedelta(days=delta_days, minutes=delta_minutes, seconds=delta_seconds)) \ 

28 .astimezone(UTC_ZI) 

29 

30 @staticmethod 

31 def now(delta_days: int = 0, delta_minutes: int = 0, delta_seconds: int = 0) -> int: 

32 return int(TimeUTC.__now(delta_days=delta_days, delta_minutes=delta_minutes, 

33 delta_seconds=delta_seconds).timestamp() * 1000) 

34 

35 @staticmethod 

36 def month_start(delta_month=0): 

37 month = TimeUTC.__now().month + delta_month 

38 return int(datetime.now(UTC_ZI) \ 

39 .replace(year=TimeUTC.__now().year + ((-12 + month) // 12 if month % 12 <= 0 else month // 12), 

40 month=12 + month % 12 if month % 12 <= 0 else month % 12 if month > 12 else month, 

41 day=1, 

42 hour=0, minute=0, 

43 second=0, 

44 microsecond=0) \ 

45 .astimezone(UTC_ZI).timestamp() * 1000) 

46 

47 @staticmethod 

48 def year_start(delta_year=0): 

49 return int(datetime.now(UTC_ZI) \ 

50 .replace(year=TimeUTC.__now().year + delta_year, month=1, day=1, hour=0, minute=0, second=0, 

51 microsecond=0) \ 

52 .astimezone(UTC_ZI).timestamp() * 1000) 

53 

54 @staticmethod 

55 def custom(year=None, month=None, day=None, hour=None, minute=None): 

56 args = locals() 

57 return int(datetime.now(UTC_ZI) \ 

58 .replace(**{key: args[key] for key in args if args[key] is not None}, second=0, microsecond=0) \ 

59 .astimezone(UTC_ZI).timestamp() * 1000) 

60 

61 @staticmethod 

62 def future(delta_day, delta_hour, delta_minute, minutes_period=None, start=None): 

63 this_time = TimeUTC.__now() 

64 if delta_day == -1: 

65 if this_time.hour < delta_hour or this_time.hour == delta_hour and this_time.minute < delta_minute: 

66 return TimeUTC.custom(hour=delta_hour, minute=delta_minute) 

67 

68 return TimeUTC.custom(day=TimeUTC.__now(1).day, hour=delta_hour, minute=delta_minute) 

69 elif delta_day > -1: 

70 if this_time.weekday() < delta_day or this_time.weekday() == delta_day and ( 

71 this_time.hour < delta_hour or this_time.hour == delta_hour and this_time.minute < delta_minute): 

72 return TimeUTC.custom(day=TimeUTC.__now(delta_day - this_time.weekday()).day, hour=delta_hour, 

73 minute=delta_minute) 

74 

75 return TimeUTC.custom(day=TimeUTC.__now(7 + delta_day - this_time.weekday()).day, hour=delta_hour, 

76 minute=delta_minute) 

77 if start is not None: 

78 return start + minutes_period * 60 * 1000 

79 

80 return TimeUTC.now(delta_minutes=minutes_period) 

81 

82 @staticmethod 

83 def from_ms_timestamp(ts): 

84 return datetime.fromtimestamp(ts // 1000, UTC_ZI) 

85 

86 @staticmethod 

87 def to_human_readable(ts, fmt='%Y-%m-%d %H:%M:%S UTC'): 

88 return datetime.utcfromtimestamp(ts // 1000).strftime(fmt) 

89 

90 @staticmethod 

91 def human_to_timestamp(ts, pattern="%Y-%m-%dT%H:%M:%S.%f"): 

92 return int(datetime.strptime(ts, pattern).timestamp() * 1000) 

93 

94 @staticmethod 

95 def datetime_to_timestamp(date): 

96 if date is None: 

97 return None 

98 if isinstance(date, str): 98 ↛ 99line 98 didn't jump to line 99 because the condition on line 98 was never true

99 fp = date.find(".") 

100 if fp > 0: 

101 date += '0' * (6 - len(date[fp + 1:])) 

102 date = datetime.fromisoformat(date) 

103 return int(datetime.timestamp(date) * 1000) 

104 

105 @staticmethod 

106 def get_start_end_from_range(range_value): 

107 range_value = range_value.upper() 

108 if TimeUTC.RANGE_VALUE is None: 

109 this_instant = TimeUTC.now() 

110 TimeUTC.RANGE_VALUE = { 

111 "TODAY": {"start": TimeUTC.midnight(), "end": this_instant}, 

112 "YESTERDAY": {"start": TimeUTC.midnight(delta_days=-1), "end": TimeUTC.midnight()}, 

113 "LAST_7_DAYS": {"start": TimeUTC.midnight(delta_days=-7), "end": this_instant}, 

114 "LAST_30_DAYS": {"start": TimeUTC.midnight(delta_days=-30), "end": this_instant}, 

115 "THIS_MONTH": {"start": TimeUTC.month_start(), "end": this_instant}, 

116 "LAST_MONTH": {"start": TimeUTC.month_start(delta_month=-1), "end": TimeUTC.month_start()}, 

117 "THIS_YEAR": {"start": TimeUTC.year_start(), "end": this_instant}, 

118 "CUSTOM_RANGE": {"start": TimeUTC.midnight(delta_days=-7), "end": this_instant} # Default is 7 days 

119 } 

120 return TimeUTC.RANGE_VALUE[range_value]["start"], TimeUTC.RANGE_VALUE[range_value]["end"] 

121 

122 @staticmethod 

123 def get_utc_offset(): 

124 return int((datetime.now(UTC_ZI).now() - datetime.now(UTC_ZI).replace(tzinfo=None)).total_seconds() * 1000) 

125 

126 @staticmethod 

127 def trunc_day(timestamp): 

128 dt = TimeUTC.from_ms_timestamp(timestamp) 

129 return TimeUTC.datetime_to_timestamp(dt 

130 .replace(hour=0, minute=0, second=0, microsecond=0) 

131 .astimezone(UTC_ZI)) 

132 

133 @staticmethod 

134 def trunc_week(timestamp): 

135 dt = TimeUTC.from_ms_timestamp(timestamp) 

136 start = dt - timedelta(days=dt.weekday()) 

137 return TimeUTC.datetime_to_timestamp(start 

138 .replace(hour=0, minute=0, second=0, microsecond=0) 

139 .astimezone(UTC_ZI))