作者:来自 Elastic Simon Cooper
随着 JDK 23 即将发布,语言环境信息中有一些重大变化,这将影响 Elasticsearch 以及你提取和格式化日期时间数据的方式。首先,介绍一些背景知识。
什么是语言环境?
每次 Java 程序需要解析或格式化使用文本字符串的日期格式(例如,“Tuesday 16th July”)时,它都需要查阅一组内部表格,其中包含有关应该使用哪些字符串的信息,用于星期几和月份字段等。此信息取决于所使用的语言(英语、法语、阿拉伯语等),在某些情况下还取决于所使用的特定国家或地区。
受影响的不仅仅是日期 — 从数字格式、日历和时间格式到每个时区的名称和每个其他语言环境的所有内容都在这些表中。特别是,这还包括用于计算星期日期的信息 - 从年初开始计算的周数,而不是日历月。所有这些信息都打包到该语言的语言环境中。
Elasticsearch 如何使用语言环境信息?
Elasticsearch 在 JDK 上运行。这意味着我们使用 JDK 提供的语言环境信息。每次你有一个解析文本日期或星期日期的日期映射器时,内部 JDK 语言环境表都会用于将这些格式映射到表示你指定的语言环境(或默认根语言环境,如果没有另行指定)的相应日期信息的数据结构。
在 Java 版本 7 及之前版本中,JDK 使用由 Sun 和 Oracle 创建的自己的内部语言环境表来存储 JDK 使用的所有语言环境信息。在 2014 年发布的 JDK 8 中,Oracle 在内部 JDK 数据库旁边添加了 Unicode Consortium 提供的 CLDR 语言环境数据库,并在 JDK 9 中将其设为默认语言环境(locale)数据库。CLDR 数据库和原始 JDK 数据库(以下称为 COMPAT 数据库)之间存在大量变化,因此当时 Elasticsearch 继续使用 COMPAT 数据库来维护数据和索引兼容性。
那么,到底有什么变化呢?
最近的 JDK 版本 JDK 23 完全删除了 Elasticseach 当前使用的 COMPAT 数据库,只留下 CLDR 作为语言环境设置数据的唯一选项。这意味着当我们升级到使用 JDK 23 时,我们被迫更改在 JDK 23 及更高版本上运行的 Elasticsearch 所使用的语言环境设置数据库。
CLDR 中的语言环境设置数据库有两个方面正在发生变化 - 文本字段值和星期日期计算。
首先,用于表示日期中各种文本字段的字符串在许多区域设置中都在发生变化 - 差异很小,但范围很广。以下是一些示例:
COMPAT | CLDR | |
---|---|---|
English period-of-day | AM, PM | in the morning, in the afternoon, in the evening |
English quarter names | 1, 2, 3, 4 | Q1, Q2, Q3, Q4 |
German short day-of-week names | So, Mo, Di, Mi… | So., Mo., Di., Mi. … |
French narrow era names | B, A | av. J.-C., ap. J.-C. |
Portuguese long day-of-week names | Domingo, Segunda-feira, Terça-feira… | domingo, segunda-feira, terça-feira… |
这意味着,如果你在 de 语言环境中使用日期格式字符串 EEE d MMM yyyy,则在 JDK 22 上,它将接受文本 Mi 4 Dez 2024;在 JDK 23 上,它只接受 Mi. 4 Dez. 2024(请注意额外的点)。
其次,用于计算星期日期的基础数据正在发生变化。这些日期通常采用 2024-W34-2 格式,计算自一年开始以来的周数,而不是日历月份和天数。但年份通常不从一周的第一天开始;如果 1 月 1 日是星期五,那是该年的第一周,还是前一年最后一周的一部分?为了了解这一点,语言环境提供了有关一周需要多少天才能算作一周以及哪一天是新一周开始的信息。
在 COMPAT 中,这些值采用各种值,具体取决于语言环境。通常,以星期日或星期一作为第一天,一周至少有 1 天或 4 天。在 CLDR 中,对于每个语言环境,这都会更改为一周的第一天为星期日,一周至少有 1 天。这适用于使用 Y、W 或 w 说明符的所有自定义日期格式。
内置的星期格式(week_date、weekyear_week_date 等)始终并将继续使用 ISO 星期日期定义,即一周的第一天为星期一,一周至少有 4 天,无论底层语言环境数据库和 JDK 版本如何。
作为 Elasticsearch 用户,这对我意味着什么?
如果你使用带有文本或星期日期字段说明符的自定义日期格式化程序,则会影响你。否则,你不会受到影响。如果你使用的日期格式说明符在升级到 JDK 23 时可能会发生变化,那么从 v8.15.2 开始的 Elasticsearch 将记录弃用警告(可在 Kibana 中看到)。
Elasticsearch 将继续随 JDK 22 一起提供给所有剩余的 v7.17.x 和 v8.15.x 版本,并将使用 COMPAT 语言环境数据库。从 v7.17.25 和 v8.15.2 开始的 Elasticsearch 版本将支持在 JDK 23 上作为自定义 JDK 运行,并且将使用 CLDR 数据库(如果是)。
如果你在 JDK 23 或更高版本上运行 Elasticsearch 版本 v7.17.24 或 v8.15.1 或更早版本,它将根本没有语言环境信息。 Elasticseach 将尝试加载 JDK 23 上不存在的 COMPAT 数据库,然后它将默认仅使用根语言环境(即基本英语)。这可能会导致一些奇怪的行为,特别是如果你使用非英语语言环境。
从 Elasticsearch 版本 8.16.0 开始,Elasticsearch 将随 JDK 23 一起提供,并默认使用 CLDR 语言环境数据库。这意味着,如果你使用文本字符串提取或输出日期,Elasticsearch 使用和接受的确切字符串可能会发生变化。如果你使用自定义星期日期提取或输出数据,星期日期可能会发生变化。这不仅会影响现在提取的数据,还会影响已在以前的 JDK 版本上提取到 Elasticsearch 中的数据。
为了减少对根语言环境进行最广泛更改的影响,在 v8.16.0 中,日期字段和日期处理器的默认语言环境将从根语言环境更改为 en,除了长时代(long era)名称和季度(quarter)名称外,COMPAT 和 CLDR 之间的语言环境相同。
如果你现在不想适应此更改,你可以继续在 JDK 22 或更低版本上运行任何版本的 Elasticsearch v7 或 v8,Elasticsearch 将使用这些版本中存在的 COMPAT 语言环境数据库。
从 Elasticsearch v9 开始,Elasticsearch 将使用 CLDR 语言环境数据库,无论它运行在哪个 JDK 版本上。
请注意,一旦 JDK 23 发布,Oracle 将不再支持 JDK 22,并且该版本上的任何未来错误和 CVE 都不会得到修复。 JDK 21 是 Java 当前的长期支持版本,如果在 JDK 21 上运行,Elasticsearch 的所有 v7 和 v8 版本都将使用 COMPAT 数据库。
要将自定义 JDK 与 Elasticsearch 一起使用,请按照以下说明操作。请注意,从预构建的 docker 映像或在 Elastic Cloud 上运行时无法做到这一点。
如何处理字符串更改?
首先,使用输入数据在 JDK 23 上测试 Elasticsearch,以检查你是否真的受到此影响。此更改将导致 Elasticsearch 将之前有效的日期字段拒绝为无效数据。如果你使用 B、G、E、O、L、M、Q、Z、a、c、e、q、v 或 z 字段说明符自定义日期格式,则最有可能出现这种情况。如果其中一个说明符与 COMPAT 语言环境数据库一起使用,Elasticsearch v8.15.2 及更高版本会将 Date format [<format>] contains textual field specifiers that could change in JDK 23 记录到 Elasticsearch 日志中,并作为相关查询的警告标头。
如果你受到影响,你可以选择在 JDK 22 及以下版本上运行 Elasticsearch,以完成其余的 v8 版本。或者,你可以修改输入数据以解决字符串的差异 - 这可以作为摄取管道的一部分来完成,也可以通过在数据到达 Elasticsearch 之前在源处修改数据来完成。
要确定特定日期格式化程序所接受的新字符串,你可以在运行 JDK 23 的独立 Java 项目中使用自定义日期格式创建 DateTimeFormatter,并测试它对各种 ZonedDateTime 对象的输出,或者使用 Calendar.getDisplayNames 方法获取特定语言环境的所有可接受字符串。
如果你受到字符串格式更改的影响,你还需要使用旧字符串处理现有数据的重新索引 — 你需要在重新索引期间指定一个脚本以将旧字符串更改为新字符串,如下所示:
String updateDate(String date) {
return date
.replace("Monday", "Mon")
.replace("Tuesday", "Tue")
.replace("Wednesday", "Wed")
.replace("Thursday", "Thu")
.replace("Friday", "Fri")
}
ctx._source.my_date_field = updateDate(ctx._source.my_date_field);
不幸的是,如何处理这种变化取决于你的具体情况以及你使用的日期格式。
如何处理星期日期的更改?
如果你使用带有 Y、W 或 w 说明符的自定义星期格式,这些格式生成的日期可能会发生变化。你需要更改为使用使用 ISO 星期日期定义的内置格式之一,使用上述自定义脚本修改摄取、输出和重新索引时的日期,或调整集成代码以使用与 CLDR 数据库相同的方式计算星期日期(星期日是一周的第一天,一周至少 1 天)。
特别是,如果你使用 Y 说明符作为日历日期格式的一部分,则你可能使用错误;Joda 时间使用 Y 表示纪年(year-of-era),但 JDK 使用 Y 表示星期年(week-year)。你需要修改格式以使用 y,或更改为内置格式。
重申:
- 如果在 JDK 23 及更高版本上运行,Elasticsearch 版本 7.17.24 和 8.15.1 及更早版本将无法访问任何语言环境数据。
- 所有剩余的 7.17.x 和 8.15.x 补丁版本将继续随 JDK 22 一起发布,默认情况下均使用 COMPAT 语言环境数据库。它们将支持在 JDK 23 上运行,并在这种情况下使用 CLDR 语言环境数据库。
- 8.16.0 及更高版本将随 JDK 23 一起发布,默认情况下将使用 CLDR 语言环境数据库。如果在 JDK 22 或更低版本上运行 Elasticsearch 版本 8.16.0 及更高版本,它们将改用 COMPAT 语言环境数据库。
- 8.16.0 将把日期字段和日期处理器的默认语言环境更改为 en。
- Elasticsearch v9 发布时将使用 CLDR 语言环境数据库,无论它运行在哪个 JDK 版本上。
本文中描述的任何特性或功能的发布和时间均由 Elastic 自行决定。任何当前不可用的特性或功能可能无法按时交付或根本无法交付。
原文:Locale changes in Elasticsearch 8.16 and JDK 23 | Elastic Blog