Skip to content

Commit 465b68d

Browse files
authored
Add datetime util (#149)
* Add the datetime utils * lint
1 parent 59f1dab commit 465b68d

File tree

7 files changed

+246
-6
lines changed

7 files changed

+246
-6
lines changed

backend/app/api/v1/mixed/task_demo.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf-8 -*-
33
import datetime
44

5-
from fastapi import APIRouter, Query
5+
from fastapi import APIRouter, Query, File, UploadFile, Form
66
from typing_extensions import Annotated
77

88
from backend.app.common.response.response_schema import response_base
@@ -69,3 +69,12 @@ async def task_demo_delete(job_id: Annotated[str, Query(..., description='任务
6969
scheduler.remove_job(job_id=job_id)
7070

7171
return await response_base.success({'msg': 'success'})
72+
73+
74+
@router.post('/files', summary='文件上传')
75+
async def create_file(file: bytes = File(), fileb: UploadFile = File(), token: str = Form()):
76+
return {
77+
'file_size': len(file),
78+
'token': token,
79+
'fileb_content_type': fileb.content_type,
80+
}

backend/app/common/response/response_schema.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from asgiref.sync import sync_to_async
77
from pydantic import validate_arguments, BaseModel
88

9+
from backend.app.core.conf import settings
910
from backend.app.utils.encoders import jsonable_encoder
1011

1112
_ExcludeData = set[int | str] | dict[int | str, Any]
@@ -37,7 +38,7 @@ def test() -> ResponseModel:
3738
data: Any | None = None
3839

3940
class Config:
40-
json_encoders = {datetime: lambda x: x.strftime('%Y-%m-%d %H:%M:%S')}
41+
json_encoders = {datetime: lambda x: x.strftime(settings.DATETIME_FORMAT)}
4142

4243

4344
class ResponseBase:
@@ -58,7 +59,7 @@ def test():
5859
@staticmethod
5960
@sync_to_async
6061
def __json_encoder(data: Any, exclude: _ExcludeData | None = None, **kwargs):
61-
custom_encoder = {datetime: lambda x: x.strftime('%Y-%m-%d %H:%M:%S')}
62+
custom_encoder = {datetime: lambda x: x.strftime(settings.DATETIME_FORMAT)}
6263
kwargs.update({'custom_encoder': custom_encoder})
6364
result = jsonable_encoder(data, exclude=exclude, **kwargs)
6465
return result

backend/app/core/conf.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ def validator_api_url(cls, values):
6363
# Limiter
6464
LIMITER_REDIS_PREFIX: str = 'fba_limiter'
6565

66+
# DateTime
67+
DATETIME_TIMEZONE: str = 'Asia/Shanghai'
68+
DATETIME_FORMAT: str = '%Y-%m-%d %H:%M:%S'
69+
6670
# MySQL
6771
DB_ECHO: bool = False
6872
DB_DATABASE: str = 'fba'

backend/app/schemas/base.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
'type_error.subclass': '预期 {expected_class} 的子类',
3333
'type_error.tuple': '值不是有效的元组',
3434
'type_error.uuid': '值不是有效的 UUID',
35-
3635
# Value Errors
3736
'value_error.any_str.max_length': '确保此值最多包含 {limit_value} 个字符',
3837
'value_error.any_str.min_length': '确保此值至少包含 {limit_value} 个字符',
@@ -44,7 +43,7 @@
4443
'value_error.decimal.max_places': '确保小数位数不超过 {decimal_places} 位',
4544
'value_error.decimal.not_finite': '值不是有效的小数(Decimal)',
4645
'value_error.decimal.whole_digits': '确保小数点前不超过 {whole_digits} 位',
47-
'value_error.discriminated_union.invalid_discriminator': '不匹配鉴别器 {discriminator_key!r} 和值 {discriminator_value!r}(允许的值:{allowed_values})',
46+
'value_error.discriminated_union.invalid_discriminator': '不匹配鉴别器 {discriminator_key!r} 和值 {discriminator_value!r}(允许的值:{allowed_values})', # noqa: E501
4847
'value_error.discriminated_union.missing_discriminator': '鉴别器 {discriminator_key!r} 的值缺失',
4948
'value_error.extra': '不允许使用额外字段',
5049
'value_error.frozenset.max_items': '确保此值最多包含 {limit_value} 个项目',

backend/app/utils/datetime.py

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
import datetime
4+
import pytz
5+
6+
from backend.app.core.conf import settings
7+
8+
9+
class DateTimeUtils:
10+
def __init__(self, timezone_str=settings.DATETIME_TIMEZONE):
11+
"""
12+
初始化函数,设置时区
13+
14+
:param timezone_str: 时区字符串,默认为 UTC
15+
"""
16+
self.timezone_str = timezone_str
17+
self.timezone = pytz.timezone(self.timezone_str)
18+
19+
def get_current_time(self) -> datetime.datetime:
20+
"""
21+
获取当前时间
22+
23+
:return: 当前时间的 datetime 对象
24+
"""
25+
return datetime.datetime.now(self.timezone)
26+
27+
@staticmethod
28+
def get_current_timestamp() -> int:
29+
"""
30+
获取当前时间戳 (秒)
31+
32+
:return: 当前时间戳 (秒)
33+
"""
34+
return int(datetime.datetime.now().timestamp())
35+
36+
@staticmethod
37+
def get_current_milliseconds() -> int:
38+
"""
39+
获取当前时间戳 (毫秒)
40+
41+
:return: 当前时间戳 (毫秒)
42+
"""
43+
return int(datetime.datetime.now().timestamp() * 1000)
44+
45+
def timestamp_to_datetime(self, timestamp: int) -> datetime.datetime:
46+
"""
47+
时间戳转 datetime 对象
48+
49+
:param timestamp: 时间戳 (秒)
50+
:return: datetime 对象
51+
"""
52+
return datetime.datetime.utcfromtimestamp(timestamp).replace(tzinfo=self.timezone)
53+
54+
def datetime_to_timestamp(self, dt: datetime.datetime) -> int:
55+
"""
56+
datetime 对象转时间戳(秒)
57+
58+
:param dt: datetime 对象
59+
:return: 时间戳 (秒)
60+
"""
61+
return int(dt.astimezone(self.timezone).timestamp())
62+
63+
def datetime_to_milliseconds(self, dt: datetime.datetime) -> int:
64+
"""
65+
datetime 对象转时间戳(毫秒)
66+
67+
:param dt: datetime 对象
68+
:return: 时间戳 (毫秒)
69+
"""
70+
return int(dt.astimezone(self.timezone).timestamp() * 1000)
71+
72+
def str_to_datetime(self, time_str: str, format_str: str = settings.DATETIME_FORMAT) -> datetime.datetime:
73+
"""
74+
时间字符串转 datetime 对象
75+
76+
:param time_str: 时间字符串
77+
:param format_str: 时间字符串的格式,默认为 '%Y-%m-%d %H:%M:%S'
78+
:return: datetime 对象
79+
"""
80+
return datetime.datetime.strptime(time_str, format_str).replace(tzinfo=self.timezone)
81+
82+
def datetime_to_str(self, dt: datetime.datetime, format_str: str = settings.DATETIME_FORMAT) -> str:
83+
"""
84+
datetime 对象转时间字符串
85+
86+
:param dt: datetime 对象
87+
:param format_str: 时间字符串的格式,默认为 '%Y-%m-%d %H:%M:%S'
88+
:return: 时间字符串
89+
"""
90+
return dt.astimezone(self.timezone).strftime(format_str)
91+
92+
@staticmethod
93+
def get_timezone(timezone_str: str) -> pytz.timezone:
94+
"""
95+
获取指定时区的 pytz.timezone 对象
96+
97+
:param timezone_str: 时区字符串
98+
:return: pytz.timezone 对象
99+
"""
100+
return pytz.timezone(timezone_str)
101+
102+
def get_timezone_time(self, timezone_str: str) -> datetime.datetime:
103+
"""
104+
获取指定时区的当前时间
105+
106+
:param timezone_str: 时区字符串
107+
:return: 当前时间的 datetime 对象
108+
"""
109+
timezone = self.get_timezone(timezone_str)
110+
return datetime.datetime.now(timezone)
111+
112+
def datetime_to_timezone(self, dt: datetime.datetime, timezone_str: str) -> datetime.datetime:
113+
"""
114+
将 datetime 对象转换为指定时区的 datetime 对象
115+
116+
:param dt: datetime 对象
117+
:param timezone_str: 目标时区字符串
118+
:return: 目标时区的 datetime 对象
119+
"""
120+
timezone = self.get_timezone(timezone_str)
121+
return dt.astimezone(timezone)
122+
123+
def datetime_to_timezone_str(
124+
self, dt: datetime.datetime, timezone_str: str, format_str: str = settings.DATETIME_FORMAT
125+
) -> str:
126+
"""
127+
将 datetime 对象转换为指定时区的时间字符串
128+
129+
:param dt: datetime 对象
130+
:param timezone_str: 目标时区字符串
131+
:param format_str: 时间字符串的格式,默认为 '%Y-%m-%d %H:%M:%S'
132+
:return: 目标时区的时间字符串
133+
"""
134+
dt_timezone = self.datetime_to_timezone(dt, timezone_str)
135+
return dt_timezone.strftime(format_str)
136+
137+
def str_to_timezone(
138+
self, time_str: str, timezone_str: str, format_str: str = settings.DATETIME_FORMAT
139+
) -> datetime.datetime:
140+
"""
141+
将指定时区的时间字符串转换为 datetime 对象
142+
143+
:param time_str: 指定时区的时间字符串
144+
:param timezone_str: 指定时区字符串
145+
:param format_str: 时间字符串的格式,默认为 '%Y-%m-%d %H:%M:%S'
146+
:return: datetime 对象
147+
"""
148+
dt = datetime.datetime.strptime(time_str, format_str).replace(tzinfo=self.timezone)
149+
return self.datetime_to_timezone(dt, timezone_str)
150+
151+
@staticmethod
152+
def datetime_to_utc(dt: datetime.datetime) -> datetime.datetime:
153+
"""
154+
将 datetime 对象转换为 UTC 时间
155+
156+
:param dt: datetime 对象
157+
:return: UTC 时间的 datetime 对象
158+
"""
159+
return dt.astimezone(pytz.utc)
160+
161+
def str_to_utc(self, time_str: str, format_str: str = settings.DATETIME_FORMAT) -> datetime.datetime:
162+
"""
163+
将时间字符串转换为 UTC 时间的 datetime 对象
164+
165+
:param time_str: 时间字符串
166+
:param format_str: 时间字符串的格式,默认为 '%Y-%m-%d %H:%M:%S'
167+
:return: UTC 时间的 datetime 对象
168+
"""
169+
dt = datetime.datetime.strptime(time_str, format_str).replace(tzinfo=self.timezone)
170+
return self.datetime_to_utc(dt)
171+
172+
def utc_to_datetime(self, utc_time: datetime.datetime) -> datetime.datetime:
173+
"""
174+
将 UTC 时间的 datetime 对象转换为指定时区的 datetime 对象
175+
176+
:param utc_time: UTC 时间的 datetime 对象
177+
:return: 目标时区的 datetime 对象
178+
"""
179+
return utc_time.replace(tzinfo=pytz.utc).astimezone(self.timezone).replace(tzinfo=None)
180+
181+
def get_expire_time(self, expires_delta: datetime.timedelta) -> datetime:
182+
"""
183+
获取过期时间
184+
185+
:param expires_delta: 时间间隔对象
186+
:return: 过期时间的 datetime 对象
187+
"""
188+
return self.get_current_time() + expires_delta
189+
190+
@staticmethod
191+
def get_expire_time_from_datetime(expire_time: datetime, seconds: int) -> datetime:
192+
"""
193+
获取从指定时间开始一定时间后的过期时间
194+
195+
:param expire_time: 指定时间的 datetime 对象
196+
:param seconds: 时间间隔(秒)
197+
:return: 过期时间的 datetime 对象
198+
"""
199+
return expire_time + datetime.timedelta(seconds=seconds)
200+
201+
@staticmethod
202+
def get_expire_seconds(expires_delta: datetime.timedelta) -> int:
203+
"""
204+
获取过期时间(秒)
205+
206+
:param expires_delta: 时间间隔对象
207+
:return: 过期时间(秒)
208+
"""
209+
return int(expires_delta.total_seconds())
210+
211+
def get_expire_seconds_from_datetime(self, expire_datetime: datetime) -> int:
212+
"""
213+
获取从指定时间开始到当前时间的时间间隔(秒)
214+
215+
:param expire_datetime: 指定时间的 datetime 对象
216+
:return: 时间间隔(秒)
217+
"""
218+
current_time = self.get_current_time()
219+
if expire_datetime < current_time:
220+
return 0
221+
return int((expire_datetime - current_time).total_seconds())
222+
223+
224+
datetime_utils = DateTimeUtils()

backend/app/utils/server_info.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import psutil
99

10+
from backend.app.core.conf import settings
11+
1012

1113
class ServerInfo:
1214
@staticmethod
@@ -103,6 +105,6 @@ def get_service_info():
103105
'mem_vms': ServerInfo.format_bytes(mem_info.vms), # 虚拟内存, 即当前进程申请的虚拟内存
104106
'mem_rss': ServerInfo.format_bytes(mem_info.rss), # 常驻内存, 即当前进程实际使用的物理内存
105107
'mem_free': ServerInfo.format_bytes(mem_info.vms - mem_info.rss), # 空闲内存
106-
'startup': start_time.strftime('%Y-%m-%d %H:%M:%S'),
108+
'startup': start_time.strftime(settings.DATETIME_FORMAT),
107109
'elapsed': f'{ServerInfo.fmt_timedelta(datetime.now() - start_time)}',
108110
}

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pytest==7.2.2
2727
pytest-pretty==1.2.0
2828
python-jose==3.3.0
2929
python-multipart==0.0.5
30+
pytz==2023.3
3031
redis[hiredis]==4.5.5
3132
ruff==0.0.262
3233
SQLAlchemy==2.0.8

0 commit comments

Comments
 (0)