【问题标题】:Validating dates containing & or $ in Java在 Java 中验证包含 & 或 $ 的日期
【发布时间】:2021-05-26 03:34:00
【问题描述】:

我惊讶地发现,当使用 DateFormat.parse()DateFormat.SHORT 时,嵌入年份的某些字符(例如 $ 或 &)将“成功”解析。比如"08/01/20&&"会解析成"Sat Aug 01 00:00:00 EDT 2020"

更令我惊讶的是,我在谷歌上找不到任何关于该问题的点击。

练习是解析和验证日期。我们可以扫描我们正在解析的字符串中的特殊字符,但这似乎不合适。

有人有什么建议吗?

public static void main(String[] args) {
    String s = "08/01/20&&";
    Date value = null;
    try {
        value = getDateFormat().parse(s);
    } catch (ParseException pe) {
        System.out.println("' must be a valid date in the form 'mm/dd/yyyy'");
    }
    System.out.println("Value:" + value);
}

public static DateFormat getDateFormat() {
    DateFormat formatDate = null;
    if (formatDate == null) {
        formatDate = DateFormat.getDateInstance(DateFormat.SHORT);
        //or at least in English locale
        //formatDate = DateFormat.getDateInstance(DateFormat.SHORT,Locale.ENGLISH);
        formatDate.setLenient(false);
    }
    return formatDate;
}

【问题讨论】:

  • 对于无法重现此行为的人,请尝试使用Locale.ENGLISH(我在代码示例中添加了具有此语言环境的替代格式化程序,只需将注释部分切换为当前formatDate)。
  • 其实所有非数字字符都可以用&代替,日期也可以解析。
  • 建议是:永远不要再使用DateDateFormat。相反,请使用 java.time 包中的类。

标签: java parsing date-formatting


【解决方案1】:

DateFormat.getDateInstance 返回的DateFormatSimpleDateFormat

formatDate instanceof SimpleDateFormat => true

根据SimpleDateFormat中的toPattern()方法,模式(Locale.US)是M/d/yy

parse 方法似乎不会考虑超出日期模式的尾随文本。 s 的以下值将生成 Sat Aug 01 00:00:00 PDT 2020 而不会引发异常。对于格式字符 yy20 被解释为 2020,并且尾随文本似乎被忽略了。

"08/01/20"
"08/01/20&&"
"08/01/20**"
"08/01/20..."
"08/01/20ABCDEFGHIJKLMNOPQRSTUVWXYZ"

Javadocs for DateFormat.parse 状态:

从给定字符串的开头解析文本以生成日期。该方法可能不会使用给定字符串的整个文本。

它肯定不会解析整个字符串。此外,您使用的 & 字符并没有什么特别之处,只是它们是无关的。

你可以得到模式的长度,然后将它与输入的字符串的长度进行比较,看看是否有多余的字符。这适用于 DateFormat.SHORT,因为预期的字符数最多为 8 个。

【讨论】:

  • 你可以得到模式的长度, 但是没有任何额外字符的08/01/20 也比M/d/yy 长,所以我看不出这有什么帮助(工作解决方案在另一个答案中)。
【解决方案2】:

java.time

随着 2014 年 3 月 Java SE 8 的发布,过时且容易出错的旧日期时间 API(java.util 日期时间类型及其格式类型,SimpleDateFormat 等)被java.time 取代,modern date-time API*,强烈建议切换到这个新的 API。

使用现代 API,您不会遇到这个问题,例如

有效日期:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String s = "08/01/20";
        DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).localizedBy(Locale.ENGLISH);
        System.out.println(LocalDate.parse(s, dtf));
    }
}

输出:

2020-08-01

日期无效:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String s = "08/01/20&&";
        DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).localizedBy(Locale.ENGLISH);
        System.out.println(LocalDate.parse(s, dtf));
    }
}

输出:

Exception in thread "main" java.time.format.DateTimeParseException:
                    Text '08/01/20&&' could not be parsed, unparsed text found at index 8

如果我希望现代 API 的行为方式与 SimpleDateFormat 默认情况下的行为方式相同。 following rule:

从给定字符串的开头解析文本以生成日期。 该方法可能不会使用给定字符串的整个文本

如果您需要,DateTimeFormatter#parse(CharSequence, ParsePosition) 随时为您服务:

import java.text.ParsePosition;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String s = "08/01/20&&";
        DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).localizedBy(Locale.ENGLISH);
        LocalDate date = LocalDate.from(dtf.parse(s, new ParsePosition(0)));
        System.out.println(date);
    }
}

输出:

2020-08-01

Trail: Date Time了解更多关于java.timemodern date-time API*

为了完整起见:

这是您可以使用旧版 API 完成的操作。

import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Date;
import java.util.Locale;

public class Main {
    public static void main(String[] args) throws ParseException {
        String s = "08/01/20&&";
        ParsePosition pp = new ParsePosition(0);
        Date value = DateFormat.getDateInstance(DateFormat.SHORT, Locale.ENGLISH).parse(s, pp);
        if (value == null || pp.getIndex() != s.length()) {
            System.out.println("The input must be a valid date in the form MM/dd/yyyy");
        } else {
            System.out.println("Value: " + value);
        }
    }
}

输出:

The input must be a valid date in the form MM/dd/yyyy

ParsePosition#getIndex 返回解析的最后一个字符之后的字符的索引,即字符串中第一个& 的索引08/01/20&&


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

【讨论】:

    猜你喜欢
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-28
    • 2021-05-11
    • 2013-06-03
    相关资源
    最近更新 更多