Skip to content

Commit 7784d4f

Browse files
authored
Add warning when using a textual date field specifier with the COMPAT locale provider (#112548)
Some textual field specifiers change between COMPAT and CLDR, depending on locale
1 parent 66303ab commit 7784d4f

File tree

4 files changed

+31
-4
lines changed

4 files changed

+31
-4
lines changed

server/src/main/java/org/elasticsearch/common/time/DateFormatters.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,6 +2323,8 @@ static DateFormatter forPattern(String input) {
23232323
} else if (FormatNames.STRICT_YEAR_MONTH_DAY.matches(input)) {
23242324
return STRICT_YEAR_MONTH_DAY;
23252325
} else {
2326+
DateUtils.checkTextualDateFormats(input);
2327+
23262328
try {
23272329
return newDateFormatter(
23282330
input,

server/src/main/java/org/elasticsearch/common/time/DateUtils.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
import org.elasticsearch.common.logging.DeprecationCategory;
1212
import org.elasticsearch.common.logging.DeprecationLogger;
13+
import org.elasticsearch.core.Predicates;
14+
import org.elasticsearch.core.UpdateForV9;
15+
import org.elasticsearch.logging.LogManager;
1316

1417
import java.time.Clock;
1518
import java.time.Duration;
@@ -19,6 +22,8 @@
1922
import java.util.Collections;
2023
import java.util.HashMap;
2124
import java.util.Map;
25+
import java.util.function.Predicate;
26+
import java.util.regex.Pattern;
2227

2328
import static java.util.Map.entry;
2429
import static org.elasticsearch.common.time.DateUtilsRounding.getMonthOfYear;
@@ -382,4 +387,16 @@ public static ZonedDateTime nowWithMillisResolution(Clock clock) {
382387
Clock millisResolutionClock = Clock.tick(clock, Duration.ofMillis(1));
383388
return ZonedDateTime.now(millisResolutionClock);
384389
}
390+
391+
// check for all textual fields, and localized zone offset
392+
private static final Predicate<String> CONTAINS_CHANGING_TEXT_SPECIFIERS = System.getProperty("java.locale.providers", "")
393+
.contains("COMPAT") ? Pattern.compile("[EcGaO]|MMM|LLL|eee|ccc|QQQ|ZZZZ").asPredicate() : Predicates.never();
394+
395+
@UpdateForV9 // this can be removed, we will only use CLDR on v9
396+
static void checkTextualDateFormats(String format) {
397+
if (CONTAINS_CHANGING_TEXT_SPECIFIERS.test(format)) {
398+
LogManager.getLogger(DateFormatter.class)
399+
.warn("Date format [{}] contains textual field specifiers that could change in JDK 23", format);
400+
}
401+
}
385402
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/ClusterStateLicenseService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ CharSequence buildExpirationMessage(long expirationMillis, boolean expired) {
140140
License [{}] on [{}].
141141
# If you have a new license, please update it. Otherwise, please reach out to
142142
# your support contact.
143-
#\s""", expiredMsg, LicenseUtils.DATE_FORMATTER.formatMillis(expirationMillis));
143+
#\s""", expiredMsg, LicenseUtils.formatMillis(expirationMillis));
144144
if (expired) {
145145
general = general.toUpperCase(Locale.ROOT);
146146
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseUtils.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88

99
import org.elasticsearch.ElasticsearchSecurityException;
1010
import org.elasticsearch.common.hash.MessageDigests;
11-
import org.elasticsearch.common.time.DateFormatter;
1211
import org.elasticsearch.license.License.LicenseType;
1312
import org.elasticsearch.license.internal.XPackLicenseStatus;
1413
import org.elasticsearch.protocol.xpack.license.LicenseStatus;
1514
import org.elasticsearch.rest.RestStatus;
1615

1716
import java.nio.charset.StandardCharsets;
1817
import java.time.Clock;
18+
import java.time.Instant;
19+
import java.time.ZoneOffset;
20+
import java.time.format.DateTimeFormatter;
1921
import java.util.HashMap;
2022
import java.util.Locale;
2123
import java.util.Map;
@@ -25,7 +27,13 @@
2527
public class LicenseUtils {
2628

2729
public static final String EXPIRED_FEATURE_METADATA = "es.license.expired.feature";
28-
public static final DateFormatter DATE_FORMATTER = DateFormatter.forPattern("EEEE, MMMM dd, yyyy").withLocale(Locale.ENGLISH);
30+
31+
public static String formatMillis(long millis) {
32+
// DateFormatters logs a warning about the pattern on COMPAT
33+
// this will be confusing to users, so call DateTimeFormatter directly instead
34+
return DateTimeFormatter.ofPattern("EEEE, MMMM dd, yyyy", Locale.ENGLISH)
35+
.format(Instant.ofEpochMilli(millis).atOffset(ZoneOffset.UTC));
36+
}
2937

3038
/**
3139
* Exception to be thrown when a feature action requires a valid license, but license
@@ -155,7 +163,7 @@ public static String getExpiryWarning(long licenseExpiryDate, long currentTime)
155163
? "expires today"
156164
: (diff > 0
157165
? String.format(Locale.ROOT, "will expire in [%d] days", days)
158-
: String.format(Locale.ROOT, "expired on [%s]", LicenseUtils.DATE_FORMATTER.formatMillis(licenseExpiryDate)));
166+
: String.format(Locale.ROOT, "expired on [%s]", formatMillis(licenseExpiryDate)));
159167
return "Your license "
160168
+ expiryMessage
161169
+ ". "

0 commit comments

Comments
 (0)