背景
在数据库中存储时间,不会自动对时区进行处理,要想针对不同时区作时间显示的适配,需要在程序中做适配,本文即为解决这一问题的实践案例。
数据库存 UTC 时间
插入记录时,使用 datetime.utcnow()
获取当前 utc 时间,并记录到数据库
class File(Base):
__tablename__ = "tb_file"
id = Column(Integer, primary_key=True)
bucket = Column(String)
uuid = Column(String, unique=True)
name = Column(String, index=True)
type = Column(String, index=True)
url = Column(String)
# 在 Python 中,int类型理论上可以表示任意大小的整数,仅受限于可用的内存大小
# 在 MySQL 中,int 数值范围是32 位整数 0~42 9496 7295,而文件大小限制了 4M,够用了
size = Column(Integer)
create_stamp = Column(TIMESTAMP, nullable=False, default=datetime.utcnow())
update_stamp = Column(TIMESTAMP, nullable=False, default=datetime.utcnow())
在数据库中,使用 timestamp 对创建时间和更新时间进行存储
CREATE TABLE `tb_file`
(
`id` int NOT NULL AUTO_INCREMENT COMMENT '文件编码',
`bucket` varchar(20) NOT NULL COMMENT 'minio中的存储桶',
`uuid` varchar(64) NOT NULL COMMENT 'uuid替换',
`name` varchar(64) NOT NULL COMMENT '文件名(包含文件名)',
`type` varchar(10) NOT NULL COMMENT '文件类型',
`url` varchar(350) NOT NULL COMMENT '文件下载地址',
`size` int NOT NULL COMMENT '文件大小',
`create_stamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间戳(UTC时间)',
`update_stamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间戳(UTC时间)',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_uuid` (`uuid`),
KEY `index_overlay_search` (`update_stamp` DESC, `type`, `name`, `url`, `size`, `uuid`) COMMENT '覆盖索引-查询',
KEY `index_overlay_modify` (`uuid`, `name`) COMMENT '覆盖索引-删改'
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci;
服务层对 UTC 时间进行转换
def stamp_to_int(stamp) -> int:
# 将时间对象转换为时间元组(time.struct_time 对象)。时间元组是一个包含了年、月、日、小时、分钟、秒等时间信息的对象
timetuple = stamp.timetuple()
# 将 TIMESTAMP 转换为时间戳
float_stamp = time.mktime(timetuple)
return int(float_stamp)
def switch_timezone(stamp):
# 接收 UTC 时间
utc_time = stamp
# 获取当前系统时间
now = datetime.now()
# 获取当前系统时区
local_timezone = now.astimezone().tzinfo
local_time = utc_time.replace(tzinfo=pytz.utc).astimezone(local_timezone)
return local_time
def trans_time(stamp):
"""
将 utc 时间转换成当前时区的时间戳,并返回时间戳整数值
:param stamp:
:return:
"""
return stamp_to_int(switch_timezone(stamp))
UI 对时间戳进行转换
效果
注意,这里要对时间戳 * 1000,否则会如下图所示,回到 1970 年哦