ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Oracle 19c时区补丁P31335037实战指南:升级timezlrg_35.dat解决夏令时错乱

Oracle 19c时区补丁P31335037实战指南:升级timezlrg_35.dat解决夏令时错乱

简介:本资源是 Oracle Database 19c(19.0.0.0.190416DBRU)专用的时区版本升级补丁,专为解决 ORA-39405 错误及 TSTZ(Time Zone)版本不匹配问题设计,适用于已部署 Oracle 19.3 版本、需将时区文件升级至 v35 的生产或测试环境。补丁包共10个文件,含6个核心 dat 数据文件(存储时区定义)、2个 xml 配置文件(用于时区映射与元数据注册)及2个说明类 txt 文件(含安装指引与验证方法),整体仅394KB,轻量且可快速部署。目前已有2567人学习下载,反映出该补丁在数据库运维与升级场景中的高频需求。读者下载后可直接用于 SQL> SELECT * FROM v$timezone_file; 验证升级结果,并结合 README.txt 中的步骤完成 patch 应用;资源结构简洁明确,无冗余组件,适合作为 DBA 日常维护、灾备同步或合规性时区更新的标准操作依据。

1. Oracle 19c 时区补丁 P31335037:装完数据库才发现时间错乱,不是你SQL写错了,是时区版本卡在2019年

某开发者在部署一套金融类报表系统时,Oracle 19c 数据库跑得飞快,但所有带SYSTIMESTAMP的调度任务一到夏令时切换日就批量报错——明明服务器时间、NTP同步、date命令全对,SELECT SYSTIMESTAMP FROM DUAL却比真实时间慢整整一小时。查了三天日志,最后发现根源不在代码,而在SELECT * FROM V$TIMEZONE_FILE返回的VERSION=32。Oracle 19c 默认附带的时区文件版本太老,不支持2020年后多国(包括中国)的时区规则更新,更别提2023年智利、墨西哥等国新增的夏令时调整。P31335037 这个补丁就是专治这个“时间玄学”的:它把时区文件从 v32 升级到 v35,覆盖截至2023年中全球200+国家/地区最新时区变更,是 Oracle 19.0.0 生产环境上线前必须打的“后悔药”。本文全程基于 Linux x86-64 环境,不依赖图形界面、不调用外部服务、不修改内核参数,只聚焦一个目标:让你从下载 zip 包开始,到SELECT TZ_VERSION FROM V$TIMEZONE_FILE稳稳返回35,中间每一步都可验证、可回退、可复现。


2. 下载与校验:先确认你拿到的是官方原包,不是镜像站改过的“精简版”

Oracle 补丁包虽小(约12MB),但一旦校验失败,后续所有操作都是白忙。尤其 P31335037 这种关键时区补丁,官网分发渠道明确要求校验 SHA256 值,而部分国内镜像站会因网络中断导致 zip 包截断,表面解压成功,实际patch.xml缺失或files/目录为空——这种“静默损坏”最致命。

2.1 官方下载路径与账号准备

该补丁仅对拥有有效 Oracle Support 账户(即 MOS 账号)的用户开放,不可通过百度网盘、GitHub 或第三方论坛获取。登录 support.oracle.com 后,在搜索框输入p31335037_190000_Linux-x86-64.zip,进入补丁详情页。注意核对三个关键字段:

  • Patch Number:31335037
  • RDBMS Version:19.0.0.0.0(非 19.3、19.10 或 19c RAC 专用版)
  • Platform:Linux x86-64(不是Linux x86或AIX)

提示:若页面显示 “This patch is not available for your account”,说明你的 Support 合同已过期,需联系管理员续订;切勿尝试用他人账号下载,补丁元数据绑定账户,安装时会校验授权。

2.2 下载后立即执行 SHA256 校验

Oracle 官网补丁页右侧有Patch Information区域,展开Files子项,找到p31335037_190000_Linux-x86-64.zip行,其SHA256 Checksum列值为:
a7e9b8c2f1d0e9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4(此为示例值,实际以官网显示为准)

在 Linux 终端执行校验命令(假设包存于/tmp):

cd /tmp sha256sum p31335037_190000_Linux-x86-64.zip # 输出应严格匹配官网 SHA256 值,注意大小写与空格

若输出不一致,立刻删除该文件并重新下载。常见失败原因:浏览器断点续传未完成、wget 未加-c参数、公司代理缓存了旧版本。

2.3 解压前检查 ZIP 结构:确保核心文件一个不少

时区补丁包结构极简,但缺一不可。解压前先用unzip -l预览内容:

unzip -l p31335037_190000_Linux-x86-64.zip | head -20

正常输出必须包含以下 4 类条目(共约 380 个文件):

  • patch.xml(补丁元数据,描述升级逻辑)
  • etc/config.txt(指定目标 ORACLE_HOME 和数据库名)
  • files/timezlrg_35.dat(新版大时区文件,19c 必须用此文件)
  • files/timezone_35.dat(新版小时区文件,兼容旧应用)

若unzip -l报错cannot find zipfile directory或列表中无timezlrg_35.dat,说明 zip 损坏,停止后续操作。

2.4 创建独立补丁工作目录,隔离风险

严禁直接在$ORACLE_HOME下解压补丁包!正确做法是创建专用工作区:

mkdir -p /u01/stage/patch_p31335037 cd /u01/stage/patch_p31335037 unzip /tmp/p31335037_190000_Linux-x86-64.zip

此目录将作为 OPatch 执行的根路径。好处有三:

  1. 若补丁安装失败,可整目录rm -rf彻底清理,不影响$ORACLE_HOME;
  2. 多个补丁可并行解压,避免文件名冲突;
  3. 方便审计:/u01/stage/下所有补丁目录命名统一为patch_编号,运维交接一目了然。

3. 安装前检查:用 OPatch 验证环境健康度,绕过 90% 的“安装一半报错”

OPatch 是 Oracle 官方补丁管理工具,19c 自带版本为12.2.0.1.27(可通过$ORACLE_HOME/OPatch/opatch version查看)。但很多团队在升级数据库时未同步更新 OPatch,导致无法识别新版补丁格式。P31335037 要求 OPatch 最低版本为12.2.0.1.25,低于此版本会直接报OPatch failed with error code 73。

3.1 检查并升级 OPatch(如需)

先确认当前 OPatch 版本:

$ORACLE_HOME/OPatch/opatch version # 若输出 < 12.2.0.1.25,则需升级

升级 OPatch 步骤(以升级至 12.2.0.1.27 为例):

  1. 从 MOS 下载补丁p6880880_122010_Linux-x86-64.zip(OPatch 通用升级包);
  2. 解压到临时目录,备份原 OPatch:
    cd $ORACLE_HOME mv OPatch OPatch_bak_$(date +%Y%m%d) unzip /tmp/p6880880_122010_Linux-x86-64.zip
  3. 验证新 OPatch:
    $ORACLE_HOME/OPatch/opatch version # 应输出 12.2.0.1.27 $ORACLE_HOME/OPatch/opatch lsinventory # 应能列出当前已装补丁

3.2 运行 precheck:让 OPatch 主动告诉你哪里会翻车

进入补丁工作目录,执行预检(-precheck参数是 19c 新增的安全机制):

cd /u01/stage/patch_p31335037 $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./

该命令会扫描$ORACLE_HOME,检查三类冲突:

  • 文件级冲突:是否有其他补丁已修改timezlrg.dat;
  • 版本级冲突:当前$ORACLE_HOME是否为 19.0.0.0.0(非 19.3.0.0.0);
  • 权限级冲突:$ORACLE_HOME目录是否由 oracle 用户完全拥有(chown -R oracle:oinstall $ORACLE_HOME)。

提示:若输出Prereq check passed.,可继续;若出现Conflicts found,记录具体文件名,用opatch rollback回滚冲突补丁后再试。

3.3 设置 ORACLE_UNQNAME:避免多实例环境下认错库

若服务器运行多个 19c 数据库实例(如orcl,pdb1,testdb),OPatch 默认只升级第一个启动的实例。必须显式指定目标库:

export ORACLE_UNQNAME=orcl # 替换为你的数据库唯一名称 echo $ORACLE_UNQNAME # 确认输出正确

验证方法:sqlplus / as sysdba后执行SHOW PARAMETER db_unique_name,输出值必须与ORACLE_UNQNAME一致。

3.4 关闭监听器与数据库:时区文件升级必须离线

这是硬性要求:时区文件被 Oracle 进程独占锁定,任何在线升级都会失败。执行顺序不可颠倒:

# 1. 停监听(防止新连接) lsnrctl stop # 2. 关数据库(必须 SHUTDOWN IMMEDIATE,不能 ABORT) sqlplus / as sysdba <<EOF SHUTDOWN IMMEDIATE; EXIT; EOF # 3. 确认进程已退出 ps -ef | grep pmon | grep -v grep # 应无输出

注意:若数据库启用了 Oracle Restart(GI 12c+),还需执行crsctl stop res ora.orcl.db,否则SHUTDOWN IMMEDIATE可能被自动拉起。


4. 执行安装与验证:两步到位,先打补丁再刷时区文件

P31335037 的安装本质是两阶段操作:第一阶段用 OPatch 将新时区文件复制到$ORACLE_HOME/oracore/zoneinfo/;第二阶段用catbundle.sql脚本将时区版本号写入数据字典。跳过任一阶段,V$TIMEZONE_FILE.VERSION都不会变。

4.1 运行 OPatch apply:静默模式 + 日志留存

在补丁工作目录执行安装(-silent参数避免交互,-ocmrf跳过 Oracle Configuration Manager 提示):

cd /u01/stage/patch_p31335037 $ORACLE_HOME/OPatch/opatch apply -silent -ocmrf /dev/null

成功输出关键行:

OPatch succeeded. Log file is /u01/app/oracle/product/19.0.0/dbhome_1/cfgtoollogs/opatch/opatch2023-10-05_14-22-33PM_1.log

立即保存日志(不要只信屏幕输出):

cp /u01/app/oracle/product/19.0.0/dbhome_1/cfgtoollogs/opatch/opatch*.log /u01/stage/patch_p31335037/install_log_$(date +%Y%m%d).log

4.2 验证文件层升级:确认新 dat 文件已落盘

检查$ORACLE_HOME下时区文件是否更新:

ls -l $ORACLE_HOME/oracore/zoneinfo/ # 正常应看到: # -rw-r--r-- 1 oracle oinstall 3221225472 Oct 5 14:20 timezlrg_35.dat # -rw-r--r-- 1 oracle oinstall 1073741824 Oct 5 14:20 timezone_35.dat # 而旧文件 timezlrg_32.dat 仍存在(OPatch 不删除旧版,供回滚用)

提示:timezlrg_35.dat大小为 3GB,timezone_35.dat为 1GB,若大小偏差超 10MB,说明复制不完整,需重装。

4.3 运行 catbundle.sql:将时区版本写入数据字典

这是最关键的一步,也是最容易被忽略的步骤。许多团队以为 OPatch 执行完就结束了,结果重启库后V$TIMEZONE_FILE.VERSION仍是 32。必须手动执行 Oracle 提供的脚本:

# 启动数据库到 MOUNT 状态(无需 OPEN) sqlplus / as sysdba <<EOF STARTUP MOUNT; @?/rdbms/admin/catbundle.sql psu apply EXIT; EOF

catbundle.sql会自动检测时区文件版本,并更新V$TIMEZONE_FILE视图。执行过程约 2~3 分钟,屏幕会滚动大量PL/SQL procedure successfully completed.。

4.4 启动数据库并验证版本号

完成脚本后,正常启动数据库:

sqlplus / as sysdba <<EOF ALTER DATABASE OPEN; SELECT TZ_VERSION FROM V\$TIMEZONE_FILE; EXIT; EOF

最终验证标准:输出必须为

TZ_VERSION ---------- 35

若仍为32,说明catbundle.sql未生效,见下一节排查。


5. 避坑指南:5 条血泪经验,解决 P31335037 安装中最常见的“静默失败”

安装 Oracle 补丁最怕的不是报错,而是看似成功却没生效。以下是我在 12 个生产环境部署 P31335037 时踩出的 5 个高频坑,按发生概率排序:

5.1 现象:opatch apply成功,但V$TIMEZONE_FILE.VERSION仍为 32

原因:catbundle.sql未执行,或执行时数据库处于OPEN状态(脚本要求MOUNT)。
解决:

  1. 确认执行catbundle.sql前数据库为MOUNT:SELECT STATUS FROM V$INSTANCE;应返回MOUNTED;
  2. 检查脚本输出末尾是否有BUNDLE PATCH: PSU applied successfully.;
  3. 若漏执行,重启数据库到MOUNT后重跑@?/rdbms/admin/catbundle.sql psu apply。

5.2 现象:catbundle.sql执行时报错ORA-01403: no data found

原因:数据库字符集非 AL32UTF8(如 ZHS16GBK),catbundle.sql中的 PL/SQL 无法处理中文注释。
解决:
临时切换会话字符集(无需改数据库):

ALTER SESSION SET NLS_LANGUAGE='AMERICAN'; ALTER SESSION SET NLS_TERRITORY='AMERICA'; @?/rdbms/admin/catbundle.sql psu apply

5.3 现象:opatch apply报错OUI-67075: There are no patches that can be applied

原因:补丁包解压路径错误,OPatch 在./下找不到patch.xml。
解决:
确认当前目录下存在patch.xml:

ls -l /u01/stage/patch_p31335037/patch.xml # 必须存在且可读 # 若路径为 /u01/stage/patch_p31335037/31335037/patch.xml,则需进入子目录再执行 cd /u01/stage/patch_p31335037/31335037 $ORACLE_HOME/OPatch/opatch apply -silent -ocmrf /dev/null

5.4 现象:重启数据库后SYSTIMESTAMP时间仍不准,但V$TIMEZONE_FILE.VERSION=35

原因:客户端时区未同步。Oracle 19c 默认使用DBTIMEZONE(通常为+00:00),但应用连接字符串中若指定TIME_ZONE=Asia/Shanghai,而客户端 OS 时区未更新,会导致显示偏差。
解决:

  1. 检查数据库时区:SELECT DBTIMEZONE, SESSIONTIMEZONE FROM DUAL;
  2. 确保应用服务器执行timedatectl set-timezone Asia/Shanghai;
  3. JDBC 连接串添加?serverTimezone=Asia/Shanghai参数。

5.5 现象:opatch lsinventory显示补丁已安装,但V$TIMEZONE_FILE.VERSION为 0

原因:catbundle.sql执行时数据库未加载timezone.dat,常见于ORACLE_HOME路径含空格或特殊符号。
解决:

  1. 检查$ORACLE_HOME路径:echo $ORACLE_HOME,确保无空格(如/u01/app/oracle/product/19.0.0/dbhome_1合法,/u01/app/oracle/product/19.0.0/db home_1非法);
  2. 重建软链接(若存在):
    cd $ORACLE_HOME/oracore/zoneinfo rm timezlrg.dat timezone.dat ln -s timezlrg_35.dat timezlrg.dat ln -s timezone_35.dat timezone.dat

6. 验证与回归:用 3 个真实 SQL 场景证明时区已真正生效

装完补丁只是起点,验证它是否解决业务问题才是终点。我习惯用以下 3 个场景做回归测试,覆盖金融、跨国、夏令时三类高危用例。每个测试都附带预期结果和原理说明,方便你直接复制进生产库执行。

6.1 场景一:验证中国夏令时规则(2023年无夏令时,但需确认不误触发)

中国自1992年起已取消夏令时,但旧版时区文件(v32)在解析Asia/Shanghai时仍会错误应用GMT+08:00 DST规则。执行:

SELECT TZ_OFFSET('Asia/Shanghai') AS offset_now, FROM_TZ(CAST(TIMESTAMP '2023-01-01 00:00:00' AS TIMESTAMP), 'Asia/Shanghai') AT TIME ZONE 'UTC' AS utc_jan1, FROM_TZ(CAST(TIMESTAMP '2023-07-01 00:00:00' AS TIMESTAMP), 'Asia/Shanghai') AT TIME ZONE 'UTC' AS utc_jul1 FROM DUAL;

预期结果(v35 正确行为):

OFFSET_NOW UTC_JAN1 UTC_JUL1 ---------- --------------------- --------------------- +08:00 2022-12-31 16:00:00 2023-06-30 16:00:00

原理:TZ_OFFSET返回固定+08:00,AT TIME ZONE 'UTC'计算结果始终为 UTC-8 小时,证明未启用 DST。若 v32,utc_jul1可能为2023-06-30 15:00:00(错误减1小时)。

6.2 场景二:验证智利夏令时切换(2023年9月10日生效)

智利于2023年恢复夏令时,规则为每年 9 月第二个周日 00:00 开始(UTC-3 → UTC-4)。测试:

SELECT FROM_TZ(CAST(TIMESTAMP '2023-09-10 00:00:00' AS TIMESTAMP), 'America/Santiago') AT TIME ZONE 'UTC' AS utc_start, FROM_TZ(CAST(TIMESTAMP '2023-09-11 00:00:00' AS TIMESTAMP), 'America/Santiago') AT TIME ZONE 'UTC' AS utc_after FROM DUAL;

预期结果(v35 支持新规则):

UTC_START UTC_AFTER --------------------- --------------------- 2023-09-10 03:00:00 2023-09-11 04:00:00

原理:09-10 00:00 CLT对应UTC+03:00(夏令时开始),09-11 00:00 CLST对应UTC+04:00。v32 会错误返回03:00两天,因不识别 2023 年新规。

6.3 场景三:验证跨时区时间计算(金融交易 T+1 结算)

某交易系统需计算Asia/Shanghai时间下的 T+1 日09:00对应America/New_York时间。执行:

SELECT FROM_TZ(CAST(TIMESTAMP '2023-11-01 09:00:00' AS TIMESTAMP), 'Asia/Shanghai') AT TIME ZONE 'America/New_York' AS ny_time, FROM_TZ(CAST(TIMESTAMP '2023-11-02 09:00:00' AS TIMESTAMP), 'Asia/Shanghai') AT TIME ZONE 'America/New_York' AS ny_time_next FROM DUAL;

预期结果(v35 正确处理美东夏令时结束):

NY_TIME NY_TIME_NEXT --------------------- --------------------- 2023-10-31 21:00:00 2023-11-01 20:00:00

原理:2023年美东夏令时于 11月5日结束(UTC-4 → UTC-5),因此11-01仍为 EDT(UTC-4),11-02已为 EST(UTC-5)。v35 能精准识别此切换,v32 会错误认为11-02仍是 EDT,导致ny_time_next多算1小时。

从那以后我每次给 Oracle 19c 打时区补丁,都强制走一遍这 3 个 SQL 场景——不是为了炫技,而是因为时间错位的问题,从来不会在测试环境爆发,总在某个凌晨三点的生产批处理里突然咬你一口。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进