问题:

使用start.sh脚本启动服务时(即java -jar)服务一切正常,但是把程序部署为系统服务(使用systemctl 启动)后,出现了时间格式化后与实际时间相差8小时的问题,那肯定是时区问题了。

先上结论:

  1. 使用timedatectl来确认时区,使用timedatectl set-timezone Asia/Shanghai来修改时区。

  2. date -R以及其他命令、脚本会使用profile文件中定义的系统变量,但是systemctl不会,这可能导致系统参数不一致。

排查过程:

1
2
$ date -R
Tue, 01 Aug 2022 18:04:33 +0800

看起来系统时区是正确的…于是陷入了沉思😔(实际这里不应该使用该命令确认时区)

由于问题只出现在systemctl模式下,其实基本可以排除java服务本身配置问题,但为了保险起见,还是确认一下。java服务开启了actuator端点监控,可以直接通过浏览器查看当前生效的参数:

http://10.0.24.125:9004/actuator/env/user.timezone

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "property": {
    "source": "systemProperties",
    "value": "Etc/UTC"
  },
  "activeProfiles": [],
  "propertySources": [
    ...
    {
      "name": "systemProperties",
      "property": {
        "value": "Etc/UTC"
      }
    },
    ...
  ]
}

可以看到,当前user.timezone参数的值确实是错误的(Etc/UTC),参数配置来源于系统配置(systemProperties)。切换成脚本启动后,值就变成正确的"Asia/Shanghai"了。

看来配置没问题,问题还是出在系统上。

于是再查看一下profile文件

1
2
3
4
$ cat /etc/profile
...
export TZ='Asia/Shanghai'
...

发现这里有一个系统变量设置,但也没有感觉异常(实际这里就是罪魁祸首)

辗转查询资料,发现linux有一个新命令timedatectl可以用于方便的设置时区,于是:

1
2
3
4
5
6
7
8
$ timedatectl
                      Local time: Tue 2022-08-01 18:13:33 CST
                  Universal time: Tue 2022-08-01 10:13:33 UTC
                        RTC time: Tue 2022-08-01 10:13:33
                       Time zone: UTC
       System clock synchronized: yes
systemd-timesyncd.service active: yes
                 RTC in local TZ: no

该命令返回的时区是UTC,跟上面date -R并不一致。🤯

实际上最准确的系统的时区应该这样看:

1
2
$ ls -lrt /etc/localtime
lrwxrwxrwx 1 root root 33 Aug  1 09:55 /etc/localtime -> /usr/share/zoneinfo/UTC

但是date命令或者直接执行java -jar 等命令时,会读取profile文件中的系统变量,此时export TZ='Asia/Shanghai'会生效,但是systemctl不会读取profile文件内容,所以会取到系统实际时区(UTC),导致上述问题。

所以修改时区时,最好不要使用profile文件,而是将/etc/localtime软连接到/usr/share/zoneinfo下的一个时区,或者使用timedatectl set-timezone Asia/Shanghai来修改更方便。