10000 Rebuild default date formats used to parse string as dates when the d… · assertj/assertj@97b642a · GitHub
[go: up one dir, main page]

Skip to content

Commit 97b642a

Browse files
Rebuild default date formats used to parse string as dates when the default timezone or the lenient flag changes.
Fix #3382
1 parent 9eeb352 commit 97b642a

File tree

4 files changed

+122
-29
lines changed

4 files changed

+122
-29
lines changed

assertj-core/src/main/java/org/assertj/core/api/AbstractDateAssert.java

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@
3838
import java.util.Date;
3939
import java.util.LinkedHashSet;
4040
import java.util.List;
41+
import java.util.TimeZone;
4142
import java.util.concurrent.TimeUnit;
4243
import java.util.function.Function;
4344

45+
import org.assertj.core.configuration.Configuration;
4446
import org.assertj.core.configuration.ConfigurationProvider;
4547
import org.assertj.core.internal.ComparatorBasedComparisonStrategy;
4648
import org.assertj.core.internal.Dates;
@@ -70,19 +72,33 @@
7072
*/
7173
public abstract class AbstractDateAssert<SELF extends AbstractDateAssert<SELF>> extends AbstractAssert<SELF, Date> {
7274

75+
private static final String DATE_FORMAT_PATTERN_SHOULD_NOT_BE_NULL = "Given date format pattern should not be null";
76+
private static final String DATE_FORMAT_SHOULD_NOT_BE_NULL = "Given date format should not be null";
77+
7378
/**
7479
* the default DateFormat used to parse any String date representation.
7580
*/
81+
private static List<DateFormat> DEFAULT_DATE_FORMATS = defaultDateFormats();
82+
private static boolean lenientParsing = Configuration.LENIENT_DATE_PARSING;
83+
7684
@VisibleForTesting
77-
static final List<DateFormat> DEFAULT_DATE_FORMATS = list(newIsoDateTimeWithMsAndIsoTimeZoneFormat(),
78-
newIsoDateTimeWithMsFormat(),
79-
newTimestampDateFormat(),
80-
newIsoDateTimeWithIsoTimeZoneFormat(),
81-
newIsoDateTimeFormat(),
82-
newIsoDateFormat());
85+
static List<DateFormat> defaultDateFormats() {
86+
if (DEFAULT_DATE_FORMATS == null || defaultDateFormatMustBeRecreated()) {
87+
DEFAULT_DATE_FORMATS = list(newIsoDateTimeWithMsAndIsoTimeZoneFormat(lenientParsing),
88+
newIsoDateTimeWithMsFormat(lenientParsing),
89+
newTimestampDateFormat(lenientParsing),
90+
newIsoDateTimeWithIsoTimeZoneFormat(lenientParsing),
91+
newIsoDateTimeFormat(lenientParsing),
92+
newIsoDateFormat(lenientParsing));
93+
}
94+
return DEFAULT_DATE_FORMATS;
95+
}
8396

84-
private static final String DATE_FORMAT_PATTERN_SHOULD_NOT_BE_NULL = "Given date format pattern should not be null";
85-
private static final String DATE_FORMAT_SHOULD_NOT_BE_NULL = "Given date format should not be null";
97+
private static boolean defaultDateFormatMustBeRecreated() {
98+
// check default timezone or lenient flag changes, only check one date format since all are configured the same way
99+
DateFormat dateFormat = DEFAULT_DATE_FORMATS.get(0);
100+
return !dateFormat.getTimeZone().getID().equals(TimeZone.getDefault().getID()) || dateFormat.isLenient() != lenientParsing;
101+
}
86102

87103
/**
88104
* Used in String based Date assertions - like {@link #isAfter(String)} - to convert input date represented as string
@@ -3392,13 +3408,10 @@ public SELF withDateFormat(String userCustomDateFormatPattern) {
33923408
*
33933409
* To revert to default strict date parsing, call {@code setLenientDateParsing(false)}.
33943410
*
3395-
* @param value whether lenient parsing mode should be enabled or not
3411+
* @param lenientDateParsing whether lenient parsing mode should be enabled or not
33963412
*/
3397-
public static void setLenientDateParsing(boolean value) {
3398-
ConfigurationProvider.loadRegisteredConfiguration();
3399-
for (DateFormat defaultDateFormat : DEFAULT_DATE_FORMATS) {
3400-
defaultDateFormat.setLenient(value);
3401-
}
3413+
public static void setLenientDateParsing(boolean lenientDateParsing) {
3414+
lenientParsing = lenientDateParsing;
34023415
}
34033416

34043417
/**
@@ -3564,7 +3577,7 @@ public SELF withDefaultDateFormatsOnly() {
35643577
}
35653578

35663579
/**
3567-
* Thread safe utility method to parse a Date with {@link #userDateFormats} first, then {@link #DEFAULT_DATE_FORMATS}.
3580+
* Thread safe utility method to parse a Date with {@link #userDateFormats} first, then {@link #defaultDateFormats()}.
35683581
* <p>
35693582
* Returns <code>null</code> if dateAsString parameter is <code>null</code>.
35703583
*
@@ -3587,15 +3600,13 @@ Date parse(String dateAsString) {
35873600
info.representation().toStringOf(dateFormatsInOrderOfUsage())));
35883601
}
35893602

3590-
private Date parseDateWithDefaultDateFormats(final String dateAsString) {
3591-
synchronized (DEFAULT_DATE_FORMATS) {
3592-
return parseDateWith(dateAsString, DEFAULT_DATE_FORMATS);
3593-
}
3603+
private synchronized Date parseDateWithDefaultDateFormats(final String dateAsString) {
3604+
return parseDateWith(dateAsString, defaultDateFormats());
35943605
}
35953606

35963607
private List<DateFormat> dateFormatsInOrderOfUsage() {
35973608
List<DateFormat> allDateFormatsInOrderOfUsage = newArrayList(userDateFormats.get());
3598-
allDateFormatsInOrderOfUsage.addAll(DEFAULT_DATE_FORMATS);
3609+
allDateFormatsInOrderOfUsage.addAll(defaultDateFormats());
35993610
return allDateFormatsInOrderOfUsage;
36003611
}
36013612

assertj-core/src/main/java/org/assertj/core/util/DateUtil.java

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class DateUtil {
5252
* @return a {@code yyyy-MM-dd} {@link DateFormat}
5353
*/
5454
public static DateFormat newIsoDateFormat() {
55-
return strictDateFormatForPattern("yyyy-MM-dd");
55+
return newIsoDateFormat(false);
5656
}
5757

5858
/**
@@ -61,15 +61,15 @@ public static DateFormat newIsoDateFormat() {
6161
* @return a {@code yyyy-MM-dd'T'HH:mm:ssX} {@link DateFormat}
6262
*/
6363
public static DateFormat newIsoDateTimeWithIsoTimeZoneFormat() {
64-
return strictDateFormatForPattern("yyyy-MM-dd'T'HH:mm:ssX");
64+
return newIsoDateTimeWithIsoTimeZoneFormat(false);
6565
}
6666

6767
/**
6868
* ISO 8601 date-time format (yyyy-MM-dd'T'HH:mm:ss), example : <code>2003-04-26T13:01:02</code>
6969
* @return a {@code yyyy-MM-dd'T'HH:mm:ss} {@link DateFormat}
7070
*/
7171
public static DateFormat newIsoDateTimeFormat() {
72-
return strictDateFormatForPattern("yyyy-MM-dd'T'HH:mm:ss");
72+
return newIsoDateTimeFormat(false);
7373
}
7474

7575
/**
@@ -78,7 +78,7 @@ public static DateFormat newIsoDateTimeFormat() {
7878
* @return a {@code yyyy-MM-dd'T'HH:mm:ss.SSS} {@link DateFormat}
7979
*/
8080
public static DateFormat newIsoDateTimeWithMsFormat() {
81-
return strictDateFormatForPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
81+
return newIsoDateTimeWithMsFormat(false);
8282
}
8383

8484
/**
@@ -87,7 +87,7 @@ public static DateFormat newIsoDateTimeWithMsFormat() {
8787
* @return a {@code yyyy-MM-dd'T'HH:mm:ss.SSSX} {@link DateFormat}
8888
*/
8989
public static DateFormat newIsoDateTimeWithMsAndIsoTimeZoneFormat() {
90-
return strictDateFormatForPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
90+
return newIsoDateTimeWithMsAndIsoTimeZoneFormat(false);
9191
}
9292

9393
/**
@@ -96,12 +96,70 @@ public static DateFormat newIsoDateTimeWithMsAndIsoTimeZoneFormat() {
9696
* @return a {@code yyyy-MM-dd HH:mm:ss.SSS} {@link DateFormat}
9797
*/
9898
public static DateFormat newTimestampDateFormat() {
99-
return strictDateFormatForPattern("yyyy-MM-dd HH:mm:ss.SSS");
99+
return newTimestampDateFormat(false);
100100
}
101101

102-
private static DateFormat strictDateFormatForPattern(String pattern) {
102+
/**
103+
* ISO 8601 date format (yyyy-MM-dd), example : <code>2003-04-23</code>
104+
* @param lenientParsing whether or not parsing the date is lenient
105+
* @return a {@code yyyy-MM-dd} {@link DateFormat}
106+
*/
107+
public static DateFormat newIsoDateFormat(boolean lenientParsing) {
108+
return dateFormatForPattern("yyyy-MM-dd", lenientParsing);
109+
}
110+
111+
/**
112+
* ISO 8601 date-time format with ISO time zone (yyyy-MM-dd'T'HH:mm:ssX), example :
113+
* <code>2003-04-26T03:01:02+00:00</code>
114+
* @param lenientParsing whether or not parsing the date is lenient
115+
* @return a {@code yyyy-MM-dd'T'HH:mm:ssX} {@link DateFormat}
116+
*/
117+
public static DateFormat newIsoDateTimeWithIsoTimeZoneFormat(boolean lenientParsing) {
118+
return dateFormatForPattern("yyyy-MM-dd'T'HH:mm:ssX", lenientParsing);
119+
}
120+
121+
/**
122+
* ISO 8601 date-time format (yyyy-MM-dd'T'HH:mm:ss), example : <code>2003-04-26T13:01:02</code>
123+
* @param lenientParsing whether or not parsing the date is lenient
124+
* @return a {@code yyyy-MM-dd'T'HH:mm:ss} {@link DateFormat}
125+
*/
126+
public static DateFormat newIsoDateTimeFormat(boolean lenientParsing) {
127+
return dateFormatForPattern("yyyy-MM-dd'T'HH:mm:ss", lenientParsing);
128+
}
129+
130+
/**
131+
* ISO 8601 date-time format with millisecond (yyyy-MM-dd'T'HH:mm:ss.SSS), example :
132+
* <code>2003-04-26T03:01:02.999</code>
133+
* @param lenientParsing whether or not parsing the date is lenient
134+
* @return a {@code yyyy-MM-dd'T'HH:mm:ss.SSS} {@link DateFormat}
135+
*/
136+
public static DateFormat newIsoDateTimeWithMsFormat(boolean lenientParsing) {
137+
return dateFormatForPattern("yyyy-MM-dd'T'HH:mm:ss.SSS", lenientParsing);
138+
}
139+
140+
/**
141+
* ISO 8601 date-time format with millisecond and ISO time zone (yyyy-MM-dd'T'HH:mm:ss.SSSX), example :
142+
* <code>2003-04-26T03:01:02.758+00:00</code>
143+
* @param lenientParsing whether or not parsing the date is lenient
144+
* @return a {@code yyyy-MM-dd'T'HH:mm:ss.SSSX} {@link DateFormat}
145+
*/
146+
public static DateFormat newIsoDateTimeWithMsAndIsoTimeZoneFormat(boolean lenientParsing) {
147+
return dateFormatForPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX", lenientParsing);
148+
}
149+
150+
/**
151+
* {@link java.sql.Timestamp} date-time format with millisecond (yyyy-MM-dd HH:mm:ss.SSS), example :
152+
* <code>2003-04-26 03:01:02.999</code>
153+
* @param lenientParsing whether or not parsing the date is lenient
154+
* @return a {@code yyyy-MM-dd HH:mm:ss.SSS} {@link DateFormat}
155+
*/
156+
public static DateFormat newTimestampDateFormat(boolean lenientParsing) {
157+
return dateFormatForPattern("yyyy-MM-dd HH:mm:ss.SSS", lenientParsing);
158+
}
159+
160+
private static DateFormat dateFormatForPattern(String pattern, boolean lenient) {
103161
DateFormat dateFormat = new SimpleDateFormat(pattern);
104-
dateFormat.setLenient(false);
162+
dateFormat.setLenient(lenient);
105163
return dateFormat;
106164
}
107165

assertj-core/src/test/java/org/assertj/core/api/EntryPointAssertions_setLenientDateParsing_Test.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void should_setLenientDateParsing(Consumer<Boolean> setLenientDateParsingFunctio
3838
// WHEN
3939
setLenientDateParsingFunction.accept(true);
4040
// THEN
41-
then(AbstractDateAssert.DEFAULT_DATE_FORMATS).allMatch(DateFormat::isLenient);
41+
then(AbstractDateAssert.defaultDateFormats()).allMatch(DateFormat::isLenient);
4242
}
4343

4444
private static Stream<Consumer<Boolean>> setLenientDateParsingFunctions() {

assertj-core/src/test/java/org/assertj/core/api/date/DateAssert_with_string_based_date_representation_Test.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,20 @@
2424

2525
import java.sql.Timestamp;
2626
import java.text.SimpleDateFormat;
27+
import java.time.Instant;
2728
import java.time.LocalDate;
2829
import java.time.LocalDateTime;
2930
import java.time.OffsetDateTime;
3031
import java.time.ZoneId;
3132
import java.time.ZoneOffset;
3233
import java.util.Date;
34+
import java.util.TimeZone;
3335
import java.util.concurrent.TimeUnit;
3436

3537
import org.assertj.core.api.DateAssertBaseTest;
3638
import org.assertj.core.util.DateUtil;
3739
import org.junit.jupiter.api.AfterEach;
40+
import org.junit.jupiter.api.BeforeEach;
3841
import org.junit.jupiter.api.Test;
3942

4043
/**
@@ -44,10 +47,20 @@
4447
*/
4548
class DateAssert_with_string_based_date_representation_Test extends DateAssertBaseTest {
4649

50+
private TimeZone defaultTimeZone;
51+
52+
@Override
53+
@BeforeEach
54+
public void setUp() {
55+
super.setUp();
56+
defaultTimeZone = TimeZone.getDefault();
57+
}
58+
4759
@Override
4860
@AfterEach
4961
public void tearDown() {
5062
useDefaultDateFormatsOnly();
63+
TimeZone.setDefault(defaultTimeZone);
5164
}
5265

5366
@Test
@@ -256,4 +269,15 @@ void use_custom_date_formats_first_then_defaults_to_parse_a_date() {
256269
assertThat(date).isEqualTo("2003 04 26");
257270
}
258271

272+
@Test
273+
void default_date_formats_should_support_default_timezone_change() {
274+
// GIVEN
275+
TimeZone.setDefault(TimeZone.getTimeZone("CET"));
276+
// need to call a date assertion to initialize the default date formats before changing the timezone.
277+
assertThat(Date.from(Instant.parse("2024-03-01T00:00:00.000+01:00"))).as("In CET time zone").isEqualTo("2024-03-01");
278+
// WHEN
279+
TimeZone.setDefault(TimeZone.getTimeZone("WET"));
280+
// THEN
281+
then(Date.from(Instant.parse("2024-03-01T00:00:00.000+00:00"))).as("In WET time zone").isEqualTo("2024-03-01");
282+
}
259283
}

0 commit comments

Comments
 (0)
0