This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-mime4j.git
The following commit(s) were added to refs/heads/master by this push: new 999f319 MIME4J-298 Convert DateTimeFieldLenientImpl to DateTimeFormatter (#44) 999f319 is described below commit 999f319dcfe7786f15177378f4d7e2b028e74809 Author: Tellier Benoit <btell...@linagora.com> AuthorDate: Sun Jun 20 09:45:20 2021 +0700 MIME4J-298 Convert DateTimeFieldLenientImpl to DateTimeFormatter (#44) This allows: - Specifying all patterns at once, avoiding one parsing pass per pattern - DateTimeFormatter is thread safe, thus can be initialized once and reused Special care have been taken to preserve previous behaviour (missing tests were added): - Accept extra input after the date - Relax cross-validation --- .../mime4j/field/DateTimeFieldLenientImpl.java | 129 +++++++++++++++------ .../mime4j/field/LenientDateTimeFieldTest.java | 23 +++- 2 files changed, 115 insertions(+), 37 deletions(-) diff --git a/dom/src/main/java/org/apache/james/mime4j/field/DateTimeFieldLenientImpl.java b/dom/src/main/java/org/apache/james/mime4j/field/DateTimeFieldLenientImpl.java index 06dce84..43e7949 100644 --- a/dom/src/main/java/org/apache/james/mime4j/field/DateTimeFieldLenientImpl.java +++ b/dom/src/main/java/org/apache/james/mime4j/field/DateTimeFieldLenientImpl.java @@ -19,14 +19,29 @@ package org.apache.james.mime4j.field; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Collections; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; + +import java.text.ParsePosition; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.ResolverStyle; +import java.time.format.SignStyle; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalField; import java.util.Date; -import java.util.List; +import java.util.HashMap; import java.util.Locale; -import java.util.TimeZone; +import java.util.Map; import org.apache.james.mime4j.codec.DecodeMonitor; import org.apache.james.mime4j.dom.FieldParser; @@ -37,34 +52,83 @@ import org.apache.james.mime4j.stream.Field; * Date-time field such as <code>Date</code> or <code>Resent-Date</code>. */ public class DateTimeFieldLenientImpl extends AbstractField implements DateTimeField { + private static final int INITIAL_YEAR = 1970; + public static final DateTimeFormatter RFC_5322 = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .parseLenient() + .optionalStart() + .appendText(DAY_OF_WEEK, dayOfWeek()) + .appendLiteral(", ") + .optionalEnd() + .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(' ') + .appendText(MONTH_OF_YEAR, monthOfYear()) + .appendLiteral(' ') + .appendValueReduced(YEAR, 2, 4, INITIAL_YEAR) + .appendLiteral(' ') + .appendValue(HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2) + .optionalStart() + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2) + .optionalEnd() + .optionalStart() + .appendLiteral('.') + .appendValue(MILLI_OF_SECOND, 3) + .optionalEnd() + .optionalStart() + .appendLiteral(' ') + .appendOffset("+HHMM", "GMT") + .optionalEnd() + .optionalStart() + .appendLiteral(' ') + .appendOffsetId() + .optionalEnd() + .optionalStart() + .appendLiteral(' ') + .appendPattern("0000") + .optionalEnd() + .toFormatter() + .withZone(ZoneId.of("GMT")) + .withResolverStyle(ResolverStyle.LENIENT) + .withResolverFields(DAY_OF_MONTH, MONTH_OF_YEAR, YEAR, HOUR_OF_DAY, MINUTE_OF_HOUR, SECOND_OF_MINUTE, MILLI_OF_SECOND, OFFSET_SECONDS) + .withLocale(Locale.US); - private static final String[] DEFAULT_DATE_FORMATS = { - "EEE, dd MMM yy HH:mm:ss ZZZZ", - "dd MMM yy HH:mm:ss ZZZZ", - "EEE, dd MMM yy HH:mm:ss.SSS 0000", - "EEE, dd MMM yy HH:mm:ss 0000", - "EEE, dd MMM yyyy HH:mm:ss ZZZZ", - "dd MMM yyyy HH:mm:ss ZZZZ", - "EEE, dd MMM yyyy HH:mm:ss.SSS 0000", - "EEE, dd MMM yyyy HH:mm:ss 0000", - "EEE, dd MMM yy HH:mm:ss X", - "dd MMM yy HH:mm:ss X", - "EEE, dd MMM yy HH:mm:ss.SSS X", - "EEE, dd MMM yy HH:mm:ss X", - "EEE, dd MMM yyyy HH:mm:ss X", - "dd MMM yyyy HH:mm:ss X", - "EEE, dd MMM yyyy HH:mm:ss.SSS X", - "EEE, dd MMM yyyy HH:mm:ss X", - }; + private static Map<Long, String> monthOfYear() { + HashMap<Long, String> result = new HashMap<>(); + result.put(1L, "Jan"); + result.put(2L, "Feb"); + result.put(3L, "Mar"); + result.put(4L, "Apr"); + result.put(5L, "May"); + result.put(6L, "Jun"); + result.put(7L, "Jul"); + result.put(8L, "Aug"); + result.put(9L, "Sep"); + result.put(10L, "Oct"); + result.put(11L, "Nov"); + result.put(12L, "Dec"); + return result; + } - private final List<String> datePatterns; + private static Map<Long, String> dayOfWeek() { + HashMap<Long, String> result = new HashMap<>(); + result.put(1L, "Mon"); + result.put(2L, "Tue"); + result.put(3L, "Wed"); + result.put(4L, "Thu"); + result.put(5L, "Fri"); + result.put(6L, "Sat"); + result.put(7L, "Sun"); + return result; + } private boolean parsed = false; private Date date; private DateTimeFieldLenientImpl(Field rawField, DecodeMonitor monitor) { super(rawField, monitor); - this.datePatterns = Collections.unmodifiableList(Arrays.asList(DEFAULT_DATE_FORMATS)); } public Date getDate() { @@ -81,15 +145,10 @@ public class DateTimeFieldLenientImpl extends AbstractField implements DateTimeF if (body != null) { body = body.trim(); } - for (String datePattern : datePatterns) { - try { - SimpleDateFormat parser = new SimpleDateFormat(datePattern, Locale.US); - parser.setTimeZone(TimeZone.getTimeZone("GMT")); - parser.setLenient(true); - date = parser.parse(body); - break; - } catch (ParseException ignore) { - } + try { + date = Date.from(Instant.from(RFC_5322.parse(body, new ParsePosition(0)))); + } catch (Exception e) { + // Ignore } } diff --git a/dom/src/test/java/org/apache/james/mime4j/field/LenientDateTimeFieldTest.java b/dom/src/test/java/org/apache/james/mime4j/field/LenientDateTimeFieldTest.java index 55a4738..284faac 100644 --- a/dom/src/test/java/org/apache/james/mime4j/field/LenientDateTimeFieldTest.java +++ b/dom/src/test/java/org/apache/james/mime4j/field/LenientDateTimeFieldTest.java @@ -19,6 +19,8 @@ package org.apache.james.mime4j.field; +import java.util.Date; + import org.apache.james.mime4j.MimeException; import org.apache.james.mime4j.dom.field.DateTimeField; import org.apache.james.mime4j.stream.RawField; @@ -28,8 +30,6 @@ import org.apache.james.mime4j.util.ContentUtil; import org.junit.Assert; import org.junit.Test; -import java.util.Date; - public class LenientDateTimeFieldTest { static DateTimeField parse(final String s) throws MimeException { @@ -45,6 +45,18 @@ public class LenientDateTimeFieldTest { } @Test + public void extraPDTShouldBeTolerated() throws Exception { + DateTimeField f = parse("Date: Wed, 16 Jul 2008 17:12:33 +0200 (PDT)"); + Assert.assertEquals(new Date(1216221153000L), f.getDate()); + } + + @Test + public void extraCharsShouldBeTolerated() throws Exception { + DateTimeField f = parse("Date: Thu, 4 Oct 2001 20:12:26 -0700 (PDT),Thu, 4 Oct 2001 20:12:26 -0700"); + Assert.assertEquals(new Date(1002251546000L), f.getDate()); + } + + @Test public void parseShouldSupportPartialYears() throws Exception { DateTimeField f = parse("Date: Wed, 16 Jul 08 17:12:33 +0200"); Assert.assertEquals(new Date(1216221153000L), f.getDate()); @@ -70,6 +82,13 @@ public class LenientDateTimeFieldTest { } @Test + public void parseShouldAcceptWrongDayOfWeek() throws Exception { + // Should be Thu + DateTimeField f = parse("Date: Fri, 01 Jan 1970 12:00:00 +0000"); + Assert.assertEquals(43200000L, f.getDate().getTime()); + } + + @Test public void testMime4j219() throws Exception { DateTimeField f = parse("Date: Tue, 17 Jul 2012 22:23:35.882 0000"); Assert.assertEquals(1342563815882L, f.getDate().getTime()); --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org