Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
<commons.distSvnStagingUrl>scm:svn:https://dist.apache.org/repos/dist/dev/commons/lang</commons.distSvnStagingUrl>
<!-- JaCoCo: Don't make code coverage worse than: -->
<commons.jacoco.haltOnFailure>true</commons.jacoco.haltOnFailure>
<commons.jacoco.classRatio>0.99</commons.jacoco.classRatio>
<commons.jacoco.classRatio>0.98</commons.jacoco.classRatio>
<commons.jacoco.instructionRatio>0.96</commons.jacoco.instructionRatio>
<commons.jacoco.methodRatio>0.96</commons.jacoco.methodRatio>
<commons.jacoco.branchRatio>0.92</commons.jacoco.branchRatio>
Expand Down Expand Up @@ -482,6 +482,17 @@
</plugins>
</build>
</profile>
<profile>
<!-- Java 25 and up -->
<id>java-25-up</id>
<activation>
<jdk>[25,)</jdk>
</activation>
<properties>
<!-- JaCoCo: Don't make code coverage worse than: -->
<commons.jacoco.classRatio>0.99</commons.jacoco.classRatio>
</properties>
</profile>
<profile>
<id>benchmark</id>
<properties>
Expand Down
36 changes: 32 additions & 4 deletions src/main/java/org/apache/commons/lang3/time/FastDateParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
Expand All @@ -46,7 +47,10 @@

import org.apache.commons.lang3.ArraySorter;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.SystemProperties;
import org.apache.commons.lang3.SystemUtils;

/**
* FastDateParser is a fast and thread-safe version of {@link java.text.SimpleDateFormat}.
Expand Down Expand Up @@ -482,6 +486,7 @@ private StrategyAndWidth literal() {
* A strategy that handles a time zone field in the parsing pattern
*/
static class TimeZoneStrategy extends PatternStrategy {

private static final class TzInfo {
final TimeZone zone;
final int dstOffset;
Expand All @@ -496,6 +501,9 @@ public String toString() {
return "TzInfo [zone=" + zone + ", dstOffset=" + dstOffset + "]";
}
}

private static final boolean AT_LEAST_JAVA_25 = SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_25);

private static final String RFC_822_TIME_ZONE = "[+-]\\d{4}";

private static final String GMT_OPTION = TimeZones.GMT_ID + "[+-]\\d{1,2}:\\d{2}";
Expand All @@ -505,6 +513,22 @@ public String toString() {
*/
private static final int ID = 0;

/**
* Tests whether to skip the given time zone, true if TimeZone.getTimeZone().
* <p>
* On Java 25 and up, skips short IDs if {@code ignoreTimeZoneShortIDs} is true.
* </p>
* <p>
* This method is package private only for testing.
* </p>
*
* @param tzId the ID to test.
* @return Whether to skip the given time zone ID.
*/
static boolean skipTimeZone(final String tzId, final boolean ignoreTimeZoneShortIDs) {
return tzId.equalsIgnoreCase(TimeZones.GMT_ID) || AT_LEAST_JAVA_25 && ignoreTimeZoneShortIDs && ZoneId.SHORT_IDS.containsKey(tzId);
}

private final Locale locale;

/**
Expand All @@ -515,6 +539,9 @@ public String toString() {

/**
* Constructs a Strategy that parses a TimeZone
* <p>
* On Java 25 and up, skips short IDs if the property {@code "FastDateParser.ignoreTimeZoneShortIDs"} is true.
* </p>
*
* @param locale The Locale
*/
Expand All @@ -529,13 +556,14 @@ public String toString() {
// Order is undefined.
// TODO Use of getZoneStrings() is discouraged per its Javadoc.
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
final boolean ignoreTimeZoneShortIDs = SystemProperties.getBoolean(FastDateParser.class, "ignoreTimeZoneShortIDs", () -> false);
for (final String[] zoneNames : zones) {
// offset 0 is the time zone ID and is not localized
final String tzId = zoneNames[ID];
if (tzId.equalsIgnoreCase(TimeZones.GMT_ID)) {
if (skipTimeZone(tzId, ignoreTimeZoneShortIDs)) {
continue;
}
final TimeZone tz = TimeZone.getTimeZone(tzId);
final TimeZone tz = TimeZones.getTimeZone(tzId);
// offset 1 is long standard name
// offset 2 is short standard name
final TzInfo standard = new TzInfo(tz, false);
Expand All @@ -561,10 +589,10 @@ public String toString() {
}
// Order is undefined.
for (final String tzId : ArraySorter.sort(TimeZone.getAvailableIDs())) {
if (tzId.equalsIgnoreCase(TimeZones.GMT_ID)) {
if (skipTimeZone(tzId, ignoreTimeZoneShortIDs)) {
continue;
}
final TimeZone tz = TimeZone.getTimeZone(tzId);
final TimeZone tz = TimeZones.getTimeZone(tzId);
final String zoneName = tz.getDisplayName(locale);
if (sorted.add(zoneName)) {
tzNames.put(zoneName, new TzInfo(tz, tz.observesDaylightTime()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public static TimeZone getTimeZone(final String id) {
if (tz != null) {
return tz;
}
return TimeZone.getTimeZone(id);
return TimeZones.getTimeZone(id);
}

private static int parseInt(final String group) {
Expand Down
33 changes: 31 additions & 2 deletions src/main/java/org/apache/commons/lang3/time/TimeZones.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@

package org.apache.commons.lang3.time;

import java.time.ZoneId;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.SystemUtils;

/**
* Helps dealing with {@link java.util.TimeZone}s.
Expand All @@ -28,6 +33,10 @@
*/
public class TimeZones {

private static final boolean AT_LEAST_JAVA_25 = SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_25);

private static final Map<String, TimeZone> SHORT_IDS = new ConcurrentHashMap<>();

/**
* A public version of {@link java.util.TimeZone}'s package private {@code GMT_ID} field.
*/
Expand All @@ -38,7 +47,28 @@ public class TimeZones {
*
* @since 3.13.0
*/
public static final TimeZone GMT = TimeZone.getTimeZone(GMT_ID);
public static final TimeZone GMT = TimeZones.getTimeZone(GMT_ID);

/**
* Delegates to {@link TimeZone#getTimeZone(String)} with special behavior on Java 25.
* <p>
* On Java 25, this methods delegates once to {@link TimeZone#getTimeZone(String)} for each short ID to avoid logging deprecation warning to system error.
* </p>
* <p>
* On Java 25, this message is of the form:
* </p>
*
* <pre>
* WARNING: Use of the three-letter time zone ID "the-short-id" is deprecated and it will be removed in a future release
* </pre>
*
* @param id Same as {@link TimeZone#getTimeZone(String)}.
* @return Same as {@link TimeZone#getTimeZone(String)}.
* @since 3.20.0
*/
public static TimeZone getTimeZone(final String id) {
return AT_LEAST_JAVA_25 && ZoneId.SHORT_IDS.containsKey(id) ? SHORT_IDS.computeIfAbsent(id, TimeZone::getTimeZone) : TimeZone.getTimeZone(id);
}

/**
* Returns the given TimeZone if non-{@code null}, otherwise {@link TimeZone#getDefault()}.
Expand All @@ -54,5 +84,4 @@ public static TimeZone toTimeZone(final TimeZone timeZone) {
/** Do not instantiate. */
private TimeZones() {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ void testGetYear() {
*/
@Test
void testToLocalDate() {
final Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone(TimeZones.GMT_ID));
final Calendar calendar = new GregorianCalendar(TimeZones.getTimeZone(TimeZones.GMT_ID));
calendar.setTimeInMillis(-27078001200000L);
assertEquals("1111-12-08T05:00:00Z", calendar.toInstant().toString());
assertEquals(LocalDate.of(1111, Month.DECEMBER, 8), new CalendarUtils(calendar).toLocalDate());
Expand All @@ -109,7 +109,7 @@ void testToLocalDate() {
@ParameterizedTest
@MethodSource(TimeZonesTest.TIME_ZONE_GET_AVAILABLE_IDS)
void testToLocalDateTime(final String timeZoneId) {
final TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
final TimeZone timeZone = TimeZones.getTimeZone(timeZoneId);
final ZoneId zoneId = timeZone.toZoneId();
final Calendar calendar = new GregorianCalendar(timeZone);
calendar.setTimeInMillis(0);
Expand All @@ -122,7 +122,7 @@ void testToLocalDateTime(final String timeZoneId) {
@ParameterizedTest
@MethodSource(TimeZonesTest.TIME_ZONE_GET_AVAILABLE_IDS)
void testToOffsetDateTime(final String timeZoneId) {
final TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
final TimeZone timeZone = TimeZones.getTimeZone(timeZoneId);
final ZoneId zoneId = timeZone.toZoneId();
final Calendar calendar = new GregorianCalendar(timeZone);
calendar.setTimeInMillis(0);
Expand All @@ -135,7 +135,7 @@ void testToOffsetDateTime(final String timeZoneId) {
@ParameterizedTest
@MethodSource(TimeZonesTest.TIME_ZONE_GET_AVAILABLE_IDS)
void testToZonedDateTime(final String timeZoneId) {
final TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
final TimeZone timeZone = TimeZones.getTimeZone(timeZoneId);
final ZoneId zoneId = timeZone.toZoneId();
final Calendar calendar = new GregorianCalendar(timeZone);
calendar.setTimeInMillis(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void testFormatUTC() {
}

private void testGmtMinus3(final String expectedValue, final String pattern) {
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
final TimeZone timeZone = TimeZones.getTimeZone("GMT-3");
assertFormats(expectedValue, pattern, timeZone, createFebruaryTestDate(timeZone));
}

Expand All @@ -153,10 +153,10 @@ void testLANG1000() throws Exception {

@Test
void testLANG1462() {
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
final TimeZone timeZone = TimeZones.getTimeZone("GMT-3");
final Calendar calendar = createJuneTestDate(timeZone);
assertEquals("20030608101112", DateFormatUtils.format(calendar, "yyyyMMddHHmmss"));
calendar.setTimeZone(TimeZone.getTimeZone("JST"));
calendar.setTimeZone(TimeZones.getTimeZone("JST"));
assertEquals("20030608221112", DateFormatUtils.format(calendar, "yyyyMMddHHmmss"));
}

Expand All @@ -179,43 +179,43 @@ void testLang530() throws ParseException {
@Test
void testLang916() {

final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Paris"));
final Calendar cal = Calendar.getInstance(TimeZones.getTimeZone("Europe/Paris"));
cal.clear();
cal.set(2009, 9, 16, 8, 42, 16);

// Long.
{
final String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Europe/Paris"));
final String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZones.getTimeZone("Europe/Paris"));
assertEquals("2009-10-16T08:42:16+02:00", value, "long");
}
{
final String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Asia/Kolkata"));
final String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZones.getTimeZone("Asia/Kolkata"));
assertEquals("2009-10-16T12:12:16+05:30", value, "long");
}
{
final String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Europe/London"));
final String value = DateFormatUtils.format(cal.getTimeInMillis(), DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZones.getTimeZone("Europe/London"));
assertEquals("2009-10-16T07:42:16+01:00", value, "long");
}

// Calendar.
{
final String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Europe/Paris"));
final String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZones.getTimeZone("Europe/Paris"));
assertEquals("2009-10-16T08:42:16+02:00", value, "calendar");
}
{
final String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Asia/Kolkata"));
final String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZones.getTimeZone("Asia/Kolkata"));
assertEquals("2009-10-16T12:12:16+05:30", value, "calendar");
}
{
final String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZone.getTimeZone("Europe/London"));
final String value = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), TimeZones.getTimeZone("Europe/London"));
assertEquals("2009-10-16T07:42:16+01:00", value, "calendar");
}
}

@DefaultLocale(language = "en")
@Test
void testSMTP() {
TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
TimeZone timeZone = TimeZones.getTimeZone("GMT-3");
Calendar june = createJuneTestDate(timeZone);

assertFormats("Sun, 08 Jun 2003 10:11:12 -0300", DateFormatUtils.SMTP_DATETIME_FORMAT.getPattern(),
Expand Down
16 changes: 8 additions & 8 deletions src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@
@WritesDefaultLocale
class DateUtilsTest extends AbstractLangTest {

private static final TimeZone TIME_ZONE_NY = TimeZone.getTimeZone("America/New_York");
private static final TimeZone TIME_ZONE_NY = TimeZones.getTimeZone("America/New_York");
private static final TimeZone TIME_ZONE_DEFAULT = TimeZone.getDefault();
private static final TimeZone TIME_ZONE_MET = TimeZone.getTimeZone("MET");
private static final TimeZone TIME_ZONE_MET = TimeZones.getTimeZone("MET");
private static Date BASE_DATE;

/**
Expand Down Expand Up @@ -209,7 +209,7 @@ private static Stream<Arguments> testToLocalDateTimeTimeZone() {
Arguments.of(
LocalDateTime.of(2023, 1, 1, 14, 0),
Date.from(LocalDateTime.of(2023, 1, 1, 0, 0).atOffset(ZoneOffset.UTC).toInstant()),
TimeZone.getTimeZone("Pacific/Kiritimati")
TimeZones.getTimeZone("Pacific/Kiritimati")
)
);
// @formatter:on
Expand Down Expand Up @@ -763,8 +763,8 @@ void testIsSameDay_DateNullNull() {

@Test
void testIsSameInstant_Cal() {
final GregorianCalendar cala = new GregorianCalendar(TimeZone.getTimeZone("GMT+1"));
final GregorianCalendar calb = new GregorianCalendar(TimeZone.getTimeZone("GMT-1"));
final GregorianCalendar cala = new GregorianCalendar(TimeZones.getTimeZone("GMT+1"));
final GregorianCalendar calb = new GregorianCalendar(TimeZones.getTimeZone("GMT-1"));
cala.set(2004, Calendar.JULY, 9, 13, 45, 0);
cala.set(Calendar.MILLISECOND, 0);
calb.set(2004, Calendar.JULY, 9, 13, 45, 0);
Expand Down Expand Up @@ -820,8 +820,8 @@ void testIsSameInstant_DateNullNull() {

@Test
void testIsSameLocalTime_Cal() {
final GregorianCalendar cala = new GregorianCalendar(TimeZone.getTimeZone("GMT+1"));
final GregorianCalendar calb = new GregorianCalendar(TimeZone.getTimeZone("GMT-1"));
final GregorianCalendar cala = new GregorianCalendar(TimeZones.getTimeZone("GMT+1"));
final GregorianCalendar calb = new GregorianCalendar(TimeZones.getTimeZone("GMT-1"));
cala.set(2004, Calendar.JULY, 9, 13, 45, 0);
cala.set(Calendar.MILLISECOND, 0);
calb.set(2004, Calendar.JULY, 9, 13, 45, 0);
Expand Down Expand Up @@ -1577,7 +1577,7 @@ void testTruncate_Bugzilla_31395() throws Exception {
@Test
void testTruncateLang59() {
// Set TimeZone to Mountain Time
final TimeZone denverZone = TimeZone.getTimeZone("America/Denver");
final TimeZone denverZone = TimeZones.getTimeZone("America/Denver");
TimeZone.setDefault(denverZone);
final DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS XXX");
format.setTimeZone(denverZone);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ void testFormatPeriodeStartGreaterEnd() {
@SuppressWarnings("deprecation")
@Test
void testFormatPeriodISO() {
final TimeZone timeZone = TimeZone.getTimeZone("GMT-3");
final TimeZone timeZone = TimeZones.getTimeZone("GMT-3");
final Calendar base = Calendar.getInstance(timeZone);
base.set(1970, Calendar.JANUARY, 1, 0, 0, 0);
base.set(Calendar.MILLISECOND, 0);
Expand Down
Loading
Loading