Changeset 18735 in josm


Ignore:
Timestamp:
2023-05-24T19:32:35+02:00 (18 months ago)
Author:
taylor.smock
Message:

Fix #22921: DateTimeException when using invalid day of month for overpass query

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/tools/date/DateUtils.java

    r18013 r18735  
    1212import java.time.format.DateTimeFormatter;
    1313import java.time.format.DateTimeFormatterBuilder;
    14 import java.time.format.DateTimeParseException;
    1514import java.time.format.FormatStyle;
    1615import java.util.Date;
     
    8180    public static Instant parseInstant(String str) {
    8281        // "2007-07-25T09:26:24{Z|{+|-}01[:00]}"
    83         if (checkLayout(str, "xxxx-xx-xx") ||
    84                 checkLayout(str, "xxxx-xx") ||
    85                 checkLayout(str, "xxxx")) {
    86             final ZonedDateTime local = ZonedDateTime.of(
    87                     parsePart4(str, 0),
    88                     str.length() > 5 ? parsePart2(str, 5) : 1,
    89                     str.length() > 8 ? parsePart2(str, 8) : 1,
    90                     0, 0, 0, 0, ZoneOffset.UTC);
    91             return local.toInstant();
    92         } else if (checkLayout(str, "xxxx-xx-xxTxx:xx:xxZ") ||
    93                 checkLayout(str, "xxxx-xx-xxTxx:xx:xx") ||
    94                 checkLayout(str, "xxxx:xx:xx xx:xx:xx") ||
    95                 checkLayout(str, "xxxx/xx/xx xx:xx:xx") ||
    96                 checkLayout(str, "xxxx-xx-xx xx:xx:xxZ") ||
    97                 checkLayout(str, "xxxx-xx-xx xx:xx:xx UTC") ||
    98                 checkLayout(str, "xxxx-xx-xxTxx:xx:xx+xx") ||
    99                 checkLayout(str, "xxxx-xx-xxTxx:xx:xx-xx") ||
    100                 checkLayout(str, "xxxx-xx-xxTxx:xx:xx+xx:00") ||
    101                 checkLayout(str, "xxxx-xx-xxTxx:xx:xx-xx:00")) {
    102             final ZonedDateTime local = ZonedDateTime.of(
    103                 parsePart4(str, 0),
    104                 parsePart2(str, 5),
    105                 parsePart2(str, 8),
    106                 parsePart2(str, 11),
    107                 parsePart2(str, 14),
    108                 parsePart2(str, 17),
    109                 0,
    110                 ZoneOffset.UTC
    111             );
    112             if (str.length() == 22 || str.length() == 25) {
    113                 final int plusHr = parsePart2(str, 20);
    114                 return local.plusHours(str.charAt(19) == '+' ? -plusHr : plusHr).toInstant();
     82        try {
     83            if (checkLayout(str, "xxxx-xx-xx") ||
     84                    checkLayout(str, "xxxx-xx") ||
     85                    checkLayout(str, "xxxx")) {
     86                final ZonedDateTime local = ZonedDateTime.of(
     87                        parsePart4(str, 0),
     88                        str.length() > 5 ? parsePart2(str, 5) : 1,
     89                        str.length() > 8 ? parsePart2(str, 8) : 1,
     90                        0, 0, 0, 0, ZoneOffset.UTC);
     91                return local.toInstant();
     92            } else if (checkLayout(str, "xxxx-xx-xxTxx:xx:xxZ") ||
     93                    checkLayout(str, "xxxx-xx-xxTxx:xx:xx") ||
     94                    checkLayout(str, "xxxx:xx:xx xx:xx:xx") ||
     95                    checkLayout(str, "xxxx/xx/xx xx:xx:xx") ||
     96                    checkLayout(str, "xxxx-xx-xx xx:xx:xxZ") ||
     97                    checkLayout(str, "xxxx-xx-xx xx:xx:xx UTC") ||
     98                    checkLayout(str, "xxxx-xx-xxTxx:xx:xx+xx") ||
     99                    checkLayout(str, "xxxx-xx-xxTxx:xx:xx-xx") ||
     100                    checkLayout(str, "xxxx-xx-xxTxx:xx:xx+xx:00") ||
     101                    checkLayout(str, "xxxx-xx-xxTxx:xx:xx-xx:00")) {
     102                final ZonedDateTime local = ZonedDateTime.of(
     103                        parsePart4(str, 0),
     104                        parsePart2(str, 5),
     105                        parsePart2(str, 8),
     106                        parsePart2(str, 11),
     107                        parsePart2(str, 14),
     108                        parsePart2(str, 17),
     109                        0,
     110                        ZoneOffset.UTC
     111                );
     112                if (str.length() == 22 || str.length() == 25) {
     113                    final int plusHr = parsePart2(str, 20);
     114                    return local.plusHours(str.charAt(19) == '+' ? -plusHr : plusHr).toInstant();
     115                }
     116                return local.toInstant();
     117            } else if (checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxxZ") ||
     118                    checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx") ||
     119                    checkLayout(str, "xxxx:xx:xx xx:xx:xx.xxx") ||
     120                    checkLayout(str, "xxxx/xx/xx xx:xx:xx.xxx") ||
     121                    checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx+xx:00") ||
     122                    checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx-xx:00")) {
     123                final ZonedDateTime local = ZonedDateTime.of(
     124                        parsePart4(str, 0),
     125                        parsePart2(str, 5),
     126                        parsePart2(str, 8),
     127                        parsePart2(str, 11),
     128                        parsePart2(str, 14),
     129                        parsePart2(str, 17),
     130                        parsePart3(str, 20) * 1_000_000,
     131                        ZoneOffset.UTC
     132                );
     133                if (str.length() == 29) {
     134                    final int plusHr = parsePart2(str, 24);
     135                    return local.plusHours(str.charAt(23) == '+' ? -plusHr : plusHr).toInstant();
     136                }
     137                return local.toInstant();
     138            } else if (checkLayout(str, "xxxx/xx/xx xx:xx:xx.xxxxxx")) {
     139                return ZonedDateTime.of(
     140                        parsePart4(str, 0),
     141                        parsePart2(str, 5),
     142                        parsePart2(str, 8),
     143                        parsePart2(str, 11),
     144                        parsePart2(str, 14),
     145                        parsePart2(str, 17),
     146                        parsePart6(str, 20) * 1_000,
     147                        ZoneOffset.UTC
     148                ).toInstant();
     149            } else {
     150                // example date format "18-AUG-08 13:33:03"
     151                SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss");
     152                Date d = f.parse(str, new ParsePosition(0));
     153                if (d != null)
     154                    return d.toInstant();
    115155            }
    116             return local.toInstant();
    117         } else if (checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxxZ") ||
    118                 checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx") ||
    119                 checkLayout(str, "xxxx:xx:xx xx:xx:xx.xxx") ||
    120                 checkLayout(str, "xxxx/xx/xx xx:xx:xx.xxx") ||
    121                 checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx+xx:00") ||
    122                 checkLayout(str, "xxxx-xx-xxTxx:xx:xx.xxx-xx:00")) {
    123             final ZonedDateTime local = ZonedDateTime.of(
    124                 parsePart4(str, 0),
    125                 parsePart2(str, 5),
    126                 parsePart2(str, 8),
    127                 parsePart2(str, 11),
    128                 parsePart2(str, 14),
    129                 parsePart2(str, 17),
    130                 parsePart3(str, 20) * 1_000_000,
    131                 ZoneOffset.UTC
    132             );
    133             if (str.length() == 29) {
    134                 final int plusHr = parsePart2(str, 24);
    135                 return local.plusHours(str.charAt(23) == '+' ? -plusHr : plusHr).toInstant();
    136             }
    137             return local.toInstant();
    138         } else if (checkLayout(str, "xxxx/xx/xx xx:xx:xx.xxxxxx")) {
    139             return ZonedDateTime.of(
    140                 parsePart4(str, 0),
    141                 parsePart2(str, 5),
    142                 parsePart2(str, 8),
    143                 parsePart2(str, 11),
    144                 parsePart2(str, 14),
    145                 parsePart2(str, 17),
    146                 parsePart6(str, 20) * 1_000,
    147                 ZoneOffset.UTC
    148             ).toInstant();
    149         } else {
    150             // example date format "18-AUG-08 13:33:03"
    151             SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss");
    152             Date d = f.parse(str, new ParsePosition(0));
    153             if (d != null)
    154                 return d.toInstant();
    155         }
    156 
    157         try {
     156
    158157            // slow path for fractional seconds different from millisecond precision
    159158            return ZonedDateTime.parse(str).toInstant();
    160         } catch (IllegalArgumentException | DateTimeParseException ex) {
     159        } catch (IllegalArgumentException | DateTimeException ex) {
    161160            throw new UncheckedParseException("The date string (" + str + ") could not be parsed.", ex);
    162161        }
     
    213212    }
    214213
     214    /**
     215     * Check text for a specified layout
     216     * @param text The text to check
     217     * @param pattern The pattern to use
     218     * @return {@code true} if the layout matches, otherwise {@code false}
     219     */
    215220    private static boolean checkLayout(String text, String pattern) {
    216221        if (text.length() != pattern.length())
     
    244249
    245250    private static int parsePart6(String str, int off) {
    246         return 100000 * num(str.charAt(off))
    247               + 10000 * num(str.charAt(off + 1))
    248                + 1000 * num(str.charAt(off + 2))
    249                 + 100 * num(str.charAt(off + 3))
    250                  + 10 * num(str.charAt(off + 4))
    251                       + num(str.charAt(off + 5));
     251        return 100_000 * num(str.charAt(off))
     252              + 10_000 * num(str.charAt(off + 1))
     253               + 1_000 * num(str.charAt(off + 2))
     254                 + 100 * num(str.charAt(off + 3))
     255                  + 10 * num(str.charAt(off + 4))
     256                       + num(str.charAt(off + 5));
    252257    }
    253258
     
    268273     */
    269274    public static DateFormat getDateFormat(int dateStyle) {
    270         if (PROP_ISO_DATES.get()) {
     275        if (Boolean.TRUE.equals(PROP_ISO_DATES.get())) {
    271276            return newIsoDateFormat();
    272277        } else {
     
    281286     */
    282287    public static DateTimeFormatter getDateFormatter(FormatStyle dateStyle) {
    283         DateTimeFormatter formatter = PROP_ISO_DATES.get()
     288        DateTimeFormatter formatter = Boolean.TRUE.equals(PROP_ISO_DATES.get())
    284289                ? DateTimeFormatter.ISO_LOCAL_DATE
    285290                : DateTimeFormatter.ofLocalizedDate(dateStyle);
     
    306311     */
    307312    public static DateFormat getTimeFormat(int timeStyle) {
    308         if (PROP_ISO_DATES.get()) {
     313        if (Boolean.TRUE.equals(PROP_ISO_DATES.get())) {
    309314            // This is not strictly conform to ISO 8601. We just want to avoid US-style times such as 3.30pm
    310315            return new SimpleDateFormat("HH:mm:ss");
     
    320325     */
    321326    public static DateTimeFormatter getTimeFormatter(FormatStyle timeStyle) {
    322         DateTimeFormatter formatter = PROP_ISO_DATES.get()
     327        DateTimeFormatter formatter = Boolean.TRUE.equals(PROP_ISO_DATES.get())
    323328                ? DateTimeFormatter.ISO_LOCAL_TIME
    324329                : DateTimeFormatter.ofLocalizedTime(timeStyle);
     
    346351     */
    347352    public static DateFormat getDateTimeFormat(int dateStyle, int timeStyle) {
    348         if (PROP_ISO_DATES.get()) {
     353        if (Boolean.TRUE.equals(PROP_ISO_DATES.get())) {
    349354            // This is not strictly conform to ISO 8601. We just want to avoid US-style times such as 3.30pm
    350355            // and we don't want to use the 'T' separator as a space character is much more readable
     
    372377     */
    373378    public static DateTimeFormatter getDateTimeFormatter(FormatStyle dateStyle, FormatStyle timeStyle) {
    374         DateTimeFormatter formatter = PROP_ISO_DATES.get()
     379        DateTimeFormatter formatter = Boolean.TRUE.equals(PROP_ISO_DATES.get())
    375380                ? ISO_LOCAL_DATE_TIME
    376381                : DateTimeFormatter.ofLocalizedDateTime(dateStyle, timeStyle);
  • trunk/test/unit/org/openstreetmap/josm/tools/date/DateUtilsTest.java

    r17987 r18735  
    1717import java.util.concurrent.ForkJoinPool;
    1818
     19import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     20import net.trajano.commons.testing.UtilityClassTestUtil;
    1921import org.junit.jupiter.api.Disabled;
    2022import org.junit.jupiter.api.Test;
    2123import org.junit.jupiter.api.extension.RegisterExtension;
     24import org.junit.jupiter.params.ParameterizedTest;
     25import org.junit.jupiter.params.provider.ValueSource;
    2226import org.openstreetmap.josm.testutils.JOSMTestRules;
    2327import org.openstreetmap.josm.tools.UncheckedParseException;
    24 
    25 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    26 import net.trajano.commons.testing.UtilityClassTestUtil;
    2728
    2829/**
     
    116117     * Verifies that parsing an illegal date throws a {@link UncheckedParseException}
    117118     */
    118     @Test
    119     void testIllegalDate() {
    120         assertThrows(UncheckedParseException.class, () -> DateUtils.fromString("2014-"));
     119    @ParameterizedTest
     120    @ValueSource(strings = {"2014-", "2014-01-", "2014-01-01T", "2014-00-01", "2014-01-00"})
     121    void testIllegalDate(String date) {
     122        assertThrows(UncheckedParseException.class, () -> DateUtils.fromString(date));
    121123    }
    122124
Note: See TracChangeset for help on using the changeset viewer.