alpine镜像中仅设tz环境变量无效,须三步固化时区:安装tzdata、复制时区文件到/etc/localtime、写入asia/shanghai到/etc/timezone并删tzdata;同时php.ini必须显式配置date.timezone=asia/shanghai,否则hyperf日志、swoole定时器等仍为utc。

Hyperf容器里date显示UTC,但设置了TZ环境变量也没用
Alpine镜像下只设TZ=Asia/Shanghai是无效的——系统级时区文件缺失,glibc和PHP都读不到真实时区。光靠环境变量只能影响部分命令(如date),但date('Y-m-d H:i:s')、日志时间戳、Swoole定时器等仍走UTC。
必须配合三步操作:
-
RUN apk add --no-cache tzdata:不装这个,后续所有复制时区文件的操作都失败 -
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime:让date命令生效,且被PHP底层调用识别 -
RUN echo "Asia/Shanghai" > /etc/timezone:补全Debian/Alpine系PHP依赖的配置路径,否则date_default_timezone_get()返回空或UTC
做完立刻apk del tzdata,避免镜像多出4MB+体积。
php.ini里没配timezone,Hyperf启动后还是UTC
PHP默认不自动同步系统时区,尤其在Alpine这种精简环境里更不可信。不能只靠TZ环境变量,得双保险。
两种可靠写法任选其一:
- 在
php.ini中显式写:date.timezone = Asia/Shanghai - 或启动容器时加:
-e PHP_INI_SCAN_DIR=/etc/php/conf.d,再挂载一个含该配置的.ini文件
验证方式:进容器执行php -r "echo date_default_timezone_get();",输出必须是Asia/Shanghai,不是UTC或空字符串。改完php.ini要重启PHP进程,opcache启用时reload不生效。
Dockerfile里写ENV TZ=Asia/Shanghai根本不管用
很多Dockerfile盲目照搬Ubuntu写法,在Alpine上直接失效。因为Alpine默认不解析TZ环境变量来更新/etc/localtime,它只认/etc/TZ文件或硬链接。
正确做法是彻底放弃纯ENV TZ=...,改用构建期固化:
- Alpine:
RUN apk add --no-cache tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone && apk del tzdata - Ubuntu/Debian:
RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && dpkg-reconfigure -f noninteractive tzdata
挂载宿主机/etc/localtime在Kubernetes或CI环境中容易因节点时区不一致而翻车,优先选构建时配置。
Hyperf日志时间戳对不上,但date命令显示正常
说明系统层时区已生效,但Hyperf或Swoole没读到PHP时区配置。常见于用了php-fpm或swoole_http_server子进程场景——主进程加载了php.ini,但子进程没继承或缓存了旧配置。
检查点:
- 确认
php -i | grep timezone输出中date.timezone值为Asia/Shanghai - 如果用了
opcache.enable=1,改完php.ini必须重启整个PHP服务,不能只kill -USR2 - Hyperf启动前加
php -d date.timezone=Asia/Shanghai bin/hyperf.php start临时验证是否是配置加载问题
最易忽略的是:Alpine镜像里/etc/timezone文件内容必须是纯文本Asia/Shanghai,不能带BOM、空行或空格,否则PHP读取失败仍回退UTC。











