-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathdatetime_utils.py
More file actions
115 lines (87 loc) · 3.18 KB
/
Copy pathdatetime_utils.py
File metadata and controls
115 lines (87 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import pandas as pd
import pytz
from datetime import datetime, timedelta
format_date = "%Y-%m-%d"
format_datetime = '%Y-%m-%dT%H:%M:%S.%fZ'
class DatetimeUtils:
def convert_datetime_to_epoch(str_date):
str_to_dt = datetime.strptime(str_date, format_date)
t = str_to_dt.timestamp()
return int(t)
def convert_epoch_to_datetime(int_epoch_date):
ts = datetime.fromtimestamp(int_epoch_date)
return ts.strftime(format_date)
def convert_to_utc(datetime_str):
"""Convert datetime string with timezone offset to UTC."""
dt = datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S.%f%z")
return dt.astimezone(pytz.UTC)
def to_naive_utc(value):
"""Normalize a datetime / pd.Timestamp / None to a tz-naive UTC
pd.Timestamp. MySQL DATETIME columns are tz-naive, but
convert_to_utc returns tz-aware — use this to reconcile both
sides of an upsert cursor comparison."""
if value is None or pd.isna(value):
return None
ts = pd.Timestamp(value)
if ts.tzinfo is not None:
ts = ts.tz_convert("UTC").tz_localize(None)
return ts
def start_date(num_days, end_date=''):
""" given num_days, calculate a start_date
given an end_date (default: now), calculate a start date num_days
number of days in the past.
If num_days is empty, return a blank start date."""
if num_days:
n = int(num_days)
else:
return ''
if not end_date:
end_date = datetime.now()
else:
end_date = datetime.strptime(end_date, format_date)
d = end_date - timedelta(days=n)
return d.strftime(format_date)
def parse_iso_timestamp(ts):
if ts:
return datetime.fromisoformat(ts.replace("Z", "+00:00"))
return None
def delta_days(n):
return timedelta(days=n)
def delta_hours(n):
return timedelta(hours=n)
def delta_seconds(n):
return timedelta(seconds=n)
def create_date(year, month, day):
return datetime(year, month, day)
def parse_date(date_str: str):
return datetime.strptime(date_str, "%Y-%m-%d").date()
def resolve_date_range(start_date=None, end_date=None, num_days=None):
"""
Resolve effective start/end dates using precedence:
1. explicit start_date/end_date
2. num_days rolling window
3. default = Jan 1 current year -> today
"""
today = datetime.now().date()
# explicit range
if start_date:
if not end_date:
end_date = today
return (
start_date.strftime("%Y-%m-%d"),
end_date.strftime("%Y-%m-%d")
)
# rolling window
if num_days:
end = today
start = end - timedelta(days=int(num_days))
return (
start.strftime("%Y-%m-%d"),
end.strftime("%Y-%m-%d")
)
# default: year-to-date
start = datetime(today.year, 1, 1).date()
return (
start.strftime("%Y-%m-%d"),
today.strftime("%Y-%m-%d")
)