terryutils 1.0.5__py3-none-any.whl → 1.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- terryutils/__init__.py +2 -1
- terryutils/date_util.py +228 -0
- {terryutils-1.0.5.dist-info → terryutils-1.0.6.dist-info}/METADATA +1 -1
- terryutils-1.0.6.dist-info/RECORD +8 -0
- terryutils-1.0.5.dist-info/RECORD +0 -7
- {terryutils-1.0.5.dist-info → terryutils-1.0.6.dist-info}/WHEEL +0 -0
- {terryutils-1.0.5.dist-info → terryutils-1.0.6.dist-info}/top_level.txt +0 -0
terryutils/__init__.py
CHANGED
terryutils/date_util.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
from datetime import datetime, date, timedelta
|
|
2
|
+
import calendar
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
class DateUtils:
|
|
6
|
+
"""企业级 Python 日期工具类"""
|
|
7
|
+
|
|
8
|
+
COMMON_FORMATS = [
|
|
9
|
+
"%Y-%m-%d %H:%M:%S",
|
|
10
|
+
"%Y-%m-%d %H:%M",
|
|
11
|
+
"%Y-%m-%d",
|
|
12
|
+
"%d/%m/%Y",
|
|
13
|
+
"%d-%m-%Y",
|
|
14
|
+
"%Y/%m/%d",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# -------------------- 基础获取 --------------------
|
|
18
|
+
@staticmethod
|
|
19
|
+
def now(fmt="%Y-%m-%d %H:%M:%S"):
|
|
20
|
+
"""当前时间"""
|
|
21
|
+
return datetime.now().strftime(fmt)
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def today(fmt="%Y-%m-%d"):
|
|
25
|
+
"""今天"""
|
|
26
|
+
return date.today().strftime(fmt)
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def datetime_now():
|
|
30
|
+
"""返回datetime对象"""
|
|
31
|
+
return datetime.now()
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def date_today():
|
|
35
|
+
"""返回date对象"""
|
|
36
|
+
return date.today()
|
|
37
|
+
|
|
38
|
+
# -------------------- 字符串与datetime互转 --------------------
|
|
39
|
+
@staticmethod
|
|
40
|
+
def parse(date_str, fmt=None):
|
|
41
|
+
"""尝试多种格式解析字符串"""
|
|
42
|
+
if isinstance(date_str, (int, float)):
|
|
43
|
+
return datetime.fromtimestamp(date_str)
|
|
44
|
+
if isinstance(date_str, datetime):
|
|
45
|
+
return date_str
|
|
46
|
+
if fmt:
|
|
47
|
+
return datetime.strptime(date_str, fmt)
|
|
48
|
+
for f in DateUtils.COMMON_FORMATS:
|
|
49
|
+
try:
|
|
50
|
+
return datetime.strptime(date_str, f)
|
|
51
|
+
except:
|
|
52
|
+
continue
|
|
53
|
+
raise ValueError(f"无法解析日期: {date_str}")
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def format(dt, fmt="%Y-%m-%d %H:%M:%S"):
|
|
57
|
+
if isinstance(dt, str):
|
|
58
|
+
dt = DateUtils.parse(dt)
|
|
59
|
+
return dt.strftime(fmt)
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def to_timestamp(dt):
|
|
63
|
+
dt = DateUtils.parse(dt) if isinstance(dt, (str, int, float)) else dt
|
|
64
|
+
return int(dt.timestamp())
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def from_timestamp(ts):
|
|
68
|
+
return datetime.fromtimestamp(ts)
|
|
69
|
+
|
|
70
|
+
# -------------------- 日期加减 --------------------
|
|
71
|
+
@staticmethod
|
|
72
|
+
def add_days(dt, days):
|
|
73
|
+
dt = DateUtils.parse(dt)
|
|
74
|
+
return dt + timedelta(days=days)
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def add_seconds(dt, seconds, ft):
|
|
78
|
+
dt = DateUtils.parse(dt, ft)
|
|
79
|
+
return dt + timedelta(seconds=seconds)
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def add_months(dt, months):
|
|
83
|
+
dt = DateUtils.parse(dt)
|
|
84
|
+
month = dt.month - 1 + months
|
|
85
|
+
year = dt.year + month // 12
|
|
86
|
+
month = month % 12 + 1
|
|
87
|
+
day = min(dt.day, calendar.monthrange(year, month)[1])
|
|
88
|
+
return dt.replace(year=year, month=month, day=day)
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def add_years(dt, years):
|
|
92
|
+
dt = DateUtils.parse(dt)
|
|
93
|
+
try:
|
|
94
|
+
return dt.replace(year=dt.year + years)
|
|
95
|
+
except ValueError:
|
|
96
|
+
# 闰年2月29日处理
|
|
97
|
+
return dt.replace(month=2, day=28, year=dt.year + years)
|
|
98
|
+
|
|
99
|
+
# -------------------- 周/月/季度起止 --------------------
|
|
100
|
+
@staticmethod
|
|
101
|
+
def start_of_day(dt=None):
|
|
102
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
103
|
+
return datetime(dt.year, dt.month, dt.day, 0, 0, 0)
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def end_of_day(dt=None):
|
|
107
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
108
|
+
return datetime(dt.year, dt.month, dt.day, 23, 59, 59)
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def start_of_week(dt=None):
|
|
112
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
113
|
+
start = dt - timedelta(days=dt.weekday())
|
|
114
|
+
return datetime(start.year, start.month, start.day)
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def end_of_week(dt=None):
|
|
118
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
119
|
+
end = dt + timedelta(days=6 - dt.weekday())
|
|
120
|
+
return datetime(end.year, end.month, end.day, 23, 59, 59)
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def start_of_month(dt=None):
|
|
124
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
125
|
+
return datetime(dt.year, dt.month, 1)
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def end_of_month(dt=None):
|
|
129
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
130
|
+
last_day = calendar.monthrange(dt.year, dt.month)[1]
|
|
131
|
+
return datetime(dt.year, dt.month, last_day, 23, 59, 59)
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def start_of_quarter(dt=None):
|
|
135
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
136
|
+
month = ((dt.month - 1) // 3) * 3 + 1
|
|
137
|
+
return datetime(dt.year, month, 1)
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def end_of_quarter(dt=None):
|
|
141
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
142
|
+
month = ((dt.month - 1) // 3 + 1) * 3
|
|
143
|
+
last_day = calendar.monthrange(dt.year, month)[1]
|
|
144
|
+
return datetime(dt.year, month, last_day, 23, 59, 59)
|
|
145
|
+
|
|
146
|
+
# -------------------- 日期判断 --------------------
|
|
147
|
+
@staticmethod
|
|
148
|
+
def is_leap_year(year):
|
|
149
|
+
return calendar.isleap(year)
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def weekday(dt=None):
|
|
153
|
+
dt = DateUtils.parse(dt) if dt else datetime.now()
|
|
154
|
+
return dt.weekday() # 0=周一
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def is_weekend(dt=None):
|
|
158
|
+
return DateUtils.weekday(dt) >= 5
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def days_between(start, end):
|
|
162
|
+
start = DateUtils.parse(start)
|
|
163
|
+
end = DateUtils.parse(end)
|
|
164
|
+
return (end - start).days
|
|
165
|
+
|
|
166
|
+
# -------------------- 日期区间 --------------------
|
|
167
|
+
@staticmethod
|
|
168
|
+
def date_range(start, end, step=1):
|
|
169
|
+
start = DateUtils.parse(start)
|
|
170
|
+
end = DateUtils.parse(end)
|
|
171
|
+
days = []
|
|
172
|
+
delta = timedelta(days=step)
|
|
173
|
+
current = start
|
|
174
|
+
while current <= end:
|
|
175
|
+
days.append(current)
|
|
176
|
+
current += delta
|
|
177
|
+
return days
|
|
178
|
+
|
|
179
|
+
# -------------------- 自然语言解析 --------------------
|
|
180
|
+
@staticmethod
|
|
181
|
+
def smart_parse(text):
|
|
182
|
+
"""支持 '今天','明天','昨天','上周一','下个月15号'"""
|
|
183
|
+
today = datetime.now()
|
|
184
|
+
text = text.strip()
|
|
185
|
+
if text == "今天":
|
|
186
|
+
return today
|
|
187
|
+
elif text == "明天":
|
|
188
|
+
return today + timedelta(days=1)
|
|
189
|
+
elif text == "昨天":
|
|
190
|
+
return today - timedelta(days=1)
|
|
191
|
+
m = re.match(r"上周([一二三四五六日])", text)
|
|
192
|
+
if m:
|
|
193
|
+
weekday_map = {"一":0,"二":1,"三":2,"四":3,"五":4,"六":5,"日":6}
|
|
194
|
+
target_weekday = weekday_map[m.group(1)]
|
|
195
|
+
delta_days = today.weekday() - target_weekday + 7
|
|
196
|
+
return today - timedelta(days=delta_days)
|
|
197
|
+
# TODO: 可继续扩展自然语言解析
|
|
198
|
+
return DateUtils.parse(text)
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def add_days_str(date_str, days, fmt="%Y-%m-%d"):
|
|
202
|
+
"""直接对字符串日期加减天数,返回字符串"""
|
|
203
|
+
dt = datetime.strptime(date_str, fmt)
|
|
204
|
+
dt += timedelta(days=days)
|
|
205
|
+
return dt.strftime(fmt)
|
|
206
|
+
|
|
207
|
+
@staticmethod
|
|
208
|
+
def format_day(date_str, fmt="%Y-%m-%d"):
|
|
209
|
+
"""直接对字符串日期加减天数,返回字符串"""
|
|
210
|
+
dt = datetime.strptime(date_str, fmt)
|
|
211
|
+
return dt.strftime("%Y年%m月%d日")
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def to_timestamp(dt):
|
|
215
|
+
dt = DateUtils.parse(dt) if isinstance(dt, (str, int, float)) else dt
|
|
216
|
+
return int(dt.timestamp())
|
|
217
|
+
# ------------------------ 测试 ------------------------
|
|
218
|
+
if __name__ == "__main__":
|
|
219
|
+
print("现在时间:", DateUtils.now())
|
|
220
|
+
print("今天:", DateUtils.today())
|
|
221
|
+
print("月初:", DateUtils.start_of_month())
|
|
222
|
+
print("月末:", DateUtils.end_of_month())
|
|
223
|
+
print("季度开始:", DateUtils.start_of_quarter())
|
|
224
|
+
print("季度结束:", DateUtils.end_of_quarter())
|
|
225
|
+
print("加3天:", DateUtils.add_days(datetime.now(), 3))
|
|
226
|
+
print("是否周末:", DateUtils.is_weekend())
|
|
227
|
+
print("自然语言解析 明天:", DateUtils.smart_parse("明天"))
|
|
228
|
+
print("日期区间:", [d.strftime("%Y-%m-%d") for d in DateUtils.date_range("2025-12-01", "2025-12-05")])
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
terryutils/__init__.py,sha256=rDDyXE01Cmz90yMbiDhWa9AOdsB7JX-KqSmYBYt9PAY,99
|
|
2
|
+
terryutils/date_util.py,sha256=KdsTOd8EjKu7p9gbfBjm0Yd06zokM8WDAD2NfnogPu0,7832
|
|
3
|
+
terryutils/log.py,sha256=ul9DF7-6AwjH-vE7aPB96hFM0OKrYUjjUFmd4xP4678,1063
|
|
4
|
+
terryutils/mysql_util.py,sha256=UwnF8z-KNJlMuOnH03iHVPKkpSNj073YSsrvI3x8pkM,3292
|
|
5
|
+
terryutils-1.0.6.dist-info/METADATA,sha256=4V80JUySGSBCEisXF1jeiBGelCvLGPz7tJ4cre5CpZk,344
|
|
6
|
+
terryutils-1.0.6.dist-info/WHEEL,sha256=WnJ8fYhv8N4SYVK2lLYNI6N0kVATA7b0piVUNvqIIJE,91
|
|
7
|
+
terryutils-1.0.6.dist-info/top_level.txt,sha256=LQtzsZsXViaVLHb_rJ4SJiT9ScUfK6dUoy89dP1mLIo,11
|
|
8
|
+
terryutils-1.0.6.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
terryutils/__init__.py,sha256=lxrRwy_RkvG1mcSs6DOv4SLRqZmwHVERKnT8CzgQ6t8,64
|
|
2
|
-
terryutils/log.py,sha256=ul9DF7-6AwjH-vE7aPB96hFM0OKrYUjjUFmd4xP4678,1063
|
|
3
|
-
terryutils/mysql_util.py,sha256=UwnF8z-KNJlMuOnH03iHVPKkpSNj073YSsrvI3x8pkM,3292
|
|
4
|
-
terryutils-1.0.5.dist-info/METADATA,sha256=ZPiFz_6ifTjLHjTs-2TVERdpXkktNn-mRBqk1FRggMo,344
|
|
5
|
-
terryutils-1.0.5.dist-info/WHEEL,sha256=WnJ8fYhv8N4SYVK2lLYNI6N0kVATA7b0piVUNvqIIJE,91
|
|
6
|
-
terryutils-1.0.5.dist-info/top_level.txt,sha256=LQtzsZsXViaVLHb_rJ4SJiT9ScUfK6dUoy89dP1mLIo,11
|
|
7
|
-
terryutils-1.0.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|