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
« 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
4import zoneinfo
6UTC_ZI = zoneinfo.ZoneInfo("UTC")
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
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)
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)
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)
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)
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)
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)
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)
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)
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
80 return TimeUTC.now(delta_minutes=minutes_period)
82 @staticmethod
83 def from_ms_timestamp(ts):
84 return datetime.fromtimestamp(ts // 1000, UTC_ZI)
86 @staticmethod
87 def to_human_readable(ts, fmt='%Y-%m-%d %H:%M:%S UTC'):
88 return datetime.utcfromtimestamp(ts // 1000).strftime(fmt)
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)
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)
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"]
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)
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))
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))