距离上次更新本文已经过去了 696 天,文章部分内容可能已经过时,请注意甄别

用配置文件,修改 linux 系统下的时区(所用系统 CentOS8)

1. 问题来源

在 linux 上使用 python 的 logging 模块的时候,发现了一个问题,那就是模块里面的 %(asctime)s 打印的时间并非东八区的时间,而是格林尼治时间,比东八区的时间少了 8 小时

对于日志来说,这怎么行?总不能每次看日志的时候,自己手动给时间加 8 小时吧!那样对 debug 来说可不是什么方便事!

2. 解决

百度了一下后,发现是我系统的时区问题。这个 Centos 系统是用 docker 安装的,内部的时区没有正常设置

plaintext
1
date

通过 date 命令可以查看系统当前的时间

plaintext
1
Sat Mar  4 01:29:59 UTC 2023

会发现是 3 月 4 日的凌晨 1 点,但实际上我是在上午 9 点写下的这篇博客

image-20230304093451196

这就需要我们修改时区了!


刚开始,我尝试用 tzselect 命令进行时区的选择,再选择了上海时区后,系统的时间还是有问题。然后发现,需要修改文件配置,才能让时间生效

bash
1
2
3
/etc/timezone # 时区的配置
/etc/localtime # 时间
/usr/share/zoneinfo/Asia #这里边放着亚洲主要城市的时间

而我的系统中,第一个文件压根不存在!

要用下面的两个命令来修改配置文件(root 下执行)

bash
1
echo "Asia/Shanghai" > /etc/timezone
bash
1
2
rm -rf /etc/localtime
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

修改完毕,再次执行 date 命令,可以看到时间已经正常了

plaintext
1
Sat Mar  4 09:31:33 CST 2023

python 的 logging 模块中打印的时间也正常了

plaintext
1
2
3
4
5
6
[23-03-04 09:31:39] DEBUG:log.py:debug:9 | test in main
[23-03-04 09:31:39] ERROR:log.py:exception:24 | Exception occurred
Traceback (most recent call last):
File "/home/kook/code/py-test/log.py", line 32, in test
a = 10/0
ZeroDivisionError: division by zero

3.python-logging 优化

虽然如此,但 logging 本身其实是可以设置时区的,提供一份简单的模板代码;

在下面的代码中,用了 beijing 这个函数来获取东八区的时间,这样这份代码在任何系统上执行,都将会打印东八区的时间了。

python
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
import logging
from datetime import datetime,timezone,timedelta

LOGGER_NAME = "bot-log" # 日志对象名字,这个没啥用
LOGGER_FILE = "bot.log" # 如果想修改log文件的名字和路径,修改此变量
"""日志文件路径"""


def beijing(sec, what):
"""日志返回北京时间的处理"""
utc_dt = datetime.now(timezone.utc) # 获取当前时间
beijing_time = utc_dt.astimezone(timezone(timedelta(hours=8))) # 转换为北京时间
return beijing_time.timetuple()
# 日志时间改为北京时间
logging.Formatter.converter = beijing # type:ignore

# 只打印info以上的日志(debug低于info)
logging.basicConfig(level=logging.INFO,
format="[%(asctime)s] %(levelname)s:%(filename)s:%(funcName)s:%(lineno)d | %(message)s",
datefmt="%y-%m-%d %H:%M:%S")
# 获取一个logger对象
_log = logging.getLogger(LOGGER_NAME)
"""自定义的logger对象"""
# 实例化控制台handler和文件handler,同时输出到控制台和文件
# cmd_handler = logging.StreamHandler() # 默认设置里面,就会往控制台打印信息;自己又加一个,导致打印俩次
file_handler = logging.FileHandler(LOGGER_FILE, mode="a", encoding="utf-8")
fmt = logging.Formatter(fmt="[%(asctime)s] %(levelname)s:%(filename)s:%(funcName)s:%(lineno)d | %(message)s",
datefmt="%y-%m-%d %H:%M:%S")
file_handler.setFormatter(fmt)
_log.addHandler(file_handler)

针对 replit 这类不支持自定义时区的平台,这样也更方便。