Skip to content

Commit c1c2f2b

Browse files
committed
Default MailDateFormat pattern might not be compatible with RFC 2822? #820 move from MailDateFormat (not thread safe) to DateTimeFormatter #590
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
1 parent d93e2fd commit c1c2f2b

File tree

5 files changed

+47
-24
lines changed

5 files changed

+47
-24
lines changed

api/src/main/java/jakarta/mail/internet/MailDateFormat.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2025 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -124,7 +124,7 @@
124124
public class MailDateFormat extends SimpleDateFormat {
125125

126126
private static final long serialVersionUID = -8148227605210628779L;
127-
private static final String PATTERN = "EEE, d MMM yyyy HH:mm:ss Z (z)";
127+
private static final String PATTERN = "EEE, d MMM yyyy HH:mm:ss Z";
128128

129129
private static final Logger LOGGER = Logger.getLogger(MailDateFormat.class.getName());
130130

@@ -149,7 +149,7 @@ public MailDateFormat() {
149149
*/
150150
private Object writeReplace() throws ObjectStreamException {
151151
MailDateFormat fmt = new MailDateFormat();
152-
fmt.superApplyPattern("EEE, d MMM yyyy HH:mm:ss 'XXXXX' (z)");
152+
fmt.superApplyPattern(PATTERN);
153153
fmt.setTimeZone(getTimeZone());
154154
return fmt;
155155
}

api/src/main/java/jakarta/mail/internet/MimeMessage.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,6 @@ public class MimeMessage extends Message implements MimePart {
169169
*/
170170
protected Object cachedContent;
171171

172-
// Used to parse dates
173-
private static final MailDateFormat mailDateFormat = new MailDateFormat();
174-
175172
// Should addresses in headers be parsed in "strict" mode?
176173
private boolean strict = true;
177174
// Is UTF-8 allowed in headers?
@@ -897,10 +894,9 @@ public void setSubject(String subject, String charset)
897894
public Date getSentDate() throws MessagingException {
898895
String s = getHeader("Date", null);
899896
if (s != null) {
897+
MailDateFormat mailDateFormat = new MailDateFormat();
900898
try {
901-
synchronized (mailDateFormat) {
902-
return mailDateFormat.parse(s);
903-
}
899+
return mailDateFormat.parse(s);
904900
} catch (ParseException pex) {
905901
return null;
906902
}
@@ -926,9 +922,8 @@ public void setSentDate(Date d) throws MessagingException {
926922
if (d == null)
927923
removeHeader("Date");
928924
else {
929-
synchronized (mailDateFormat) {
930-
setHeader("Date", mailDateFormat.format(d));
931-
}
925+
MailDateFormat mailDateFormat = new MailDateFormat();
926+
setHeader("Date", mailDateFormat.format(d));
932927
}
933928
}
934929

api/src/test/java/jakarta/mail/internet/MailDateFormatTest.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2025 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -79,7 +79,7 @@ public void formatMustThrowNpeForNullArgs() {
7979
@Test
8080
public void mustFormatRfc2822WithOptionalCfws() {
8181
Date date = mustPass(getDefault(), "1 Jan 2015 00:00 +0000");
82-
assertThatDate(date, "Thu, 1 Jan 2015 00:00:00 +0000 (UTC)");
82+
assertThatDate(date, "Thu, 1 Jan 2015 00:00:00 +0000");
8383
}
8484

8585
@Test
@@ -89,7 +89,7 @@ public void mustUseTimeZoneForFormatting() {
8989
Date date = mustPass(fmt, input);
9090
fmt.setTimeZone(TimeZone.getTimeZone("Etc/GMT+8"));
9191
assertThat(fmt.format(date),
92-
is("Wed, 31 Dec 2014 15:00:00 -0800 (GMT-08:00)"));
92+
is("Wed, 31 Dec 2014 15:00:00 -0800"));
9393
}
9494

9595
/*
@@ -239,7 +239,7 @@ public void mustFailOrSkipCfws() {
239239
// NOTE this test fails with getLenient(),
240240
// since lenient parsing must remain backward compatible
241241
Date date = getStrict().parse(input);
242-
assertThatDate(date, "Thu, 1 Jan 2015 00:00:00 +0000 (UTC)");
242+
assertThatDate(date, "Thu, 1 Jan 2015 00:00:00 +0000");
243243
} catch (ParseException ignored) {
244244
assertTrue("Not supporting CFWS is allowed", true);
245245
}
@@ -309,7 +309,7 @@ public void lenientMustAcceptYearsBetween1000and1899() {
309309
@Test
310310
public void lenientMustAdd1900To3DigitYear() {
311311
Date date = mustPass(getLenient(), "1 Jul 900 00:00 +0000");
312-
assertThatDate(date, "Sat, 1 Jul 2800 00:00:00 +0000 (UTC)");
312+
assertThatDate(date, "Sat, 1 Jul 2800 00:00:00 +0000");
313313
}
314314

315315
@Test
@@ -338,13 +338,13 @@ public void mustParseLeapSecondAsJsr310() {
338338
@Test
339339
public void mustParseNegativeZeroesZoneAsUtc() {
340340
Date date = mustPass(getDefault(), "1 Jan 2015 00:00 -0000");
341-
assertThatDate(date, "Thu, 1 Jan 2015 00:00:00 +0000 (UTC)");
341+
assertThatDate(date, "Thu, 1 Jan 2015 00:00:00 +0000");
342342
}
343343

344344
@Test
345345
public void mustCorrectlyParseInputWithTrailingDigits() {
346346
Date date = mustPass(getStrict(), "1 Jan 2015 00:00 -001530");
347-
assertThatDate(date, "Thu, 1 Jan 2015 00:15:00 +0000 (UTC)");
347+
assertThatDate(date, "Thu, 1 Jan 2015 00:15:00 +0000");
348348
}
349349

350350
@Test
@@ -424,7 +424,7 @@ public void mustProhibitSetDateFormatSymbols() {
424424
SimpleDateFormat fmt = getStrict();
425425
fmt.setDateFormatSymbols(new DateFormatSymbols(Locale.FRENCH));
426426
Date date = mustPass(fmt, "1 Jan 2015 00:00:00 +0000");
427-
assertThatDate(date, "jeu., 1 janv. 2015 00:00:00 +0000 (UTC)");
427+
assertThatDate(date, "jeu., 1 janv. 2015 00:00:00 +0000");
428428
}
429429

430430
/*

doc/release/CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ E 758 Improve MimeMessage UTF8 handling
2828
E 804 Restore streamProvider fields for backwards compatibility
2929
E 810 InternetHeaders.InternetHeader toString()
3030
E 818 Change getSize() return type from int to long
31+
E 590 move from MailDateFormat (not thread safe) to DateTimeFormatter
32+
E 820 Default MailDateFormat pattern might not be compatible with RFC 2822?
3133

3234
CHANGES IN THE 2.1.5 RELEASE
3335
----------------------------

www/docs/COMPAT.txt

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,46 @@
33

44
-- Jakarta Mail 2.2.0 --
55

6-
Removal of finalize()
6+
- Removal of finalize()
77

88
The finalize() methods have been removed throughout the Mail API.
99

1010
This has several implications:
1111

1212
1. No-arg close() behavior
13-
Calling the no-argument close() method on objects that are already closed is no longer considered "hostile" (i.e. it no longer throws or behaves unexpectedly if the resource is already closed).
13+
Calling the no-argument close() method on objects that are already
14+
closed is no longer considered "hostile" (i.e. it no longer throws
15+
or behaves unexpectedly if the resource is already closed).
1416

1517
2. Event thread shutdown
16-
The no-arg close() is now required to signal internal event threads to stop. In other words, previously finalize() might have been relied on to clean up or shut down threads; that is no longer the case. Users must call close() explicitly to ensure proper shutdown.
18+
The no-arg close() is now required to signal internal event threads
19+
to stop. In other words, previously finalize() might have been
20+
relied on to clean up or shut down threads; that is no longer the
21+
case. Users must call close() explicitly to ensure proper shutdown.
1722

1823
3. Resource / memory management
19-
Because finalizers are removed, users must more explicitly manage resources: ensure streams, folders, other resources are closed properly. Do not rely on garbage collection / finalization to clean up.
24+
Because finalizers are removed, users must more explicitly manage
25+
resources: ensure streams, folders, other resources are closed
26+
properly. Do not rely on garbage collection / finalization to
27+
clean up.
28+
29+
- MailDateFormat pattern change
30+
31+
The default date format pattern used by MailDateFormat has been
32+
updated. The legacy pattern:
33+
34+
"EEE, d MMM yyyy HH:mm:ss Z (z)"
35+
36+
has been changed to:
37+
38+
"EEE, d MMM yyyy HH:mm:ss Z"
39+
40+
The optional parenthesized time zone name was removed to improve
41+
consistency with RFC 2822–style header formatting and to align with
42+
the behavior of modern Java time formatters. Applications that rely
43+
on the presence of the "(z)" suffix in generated Date headers should
44+
update their expectations accordingly.
45+
2046

2147
-- Jakarta Mail 2.1.4 --
2248

0 commit comments

Comments
 (0)