SimpleDateFormat.parse() 忽略模式中的字符数 [英] SimpleDateFormat.parse() ignores the number of characters in pattern

查看:22
本文介绍了SimpleDateFormat.parse() 忽略模式中的字符数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试解析一个日期字符串,它可以具有不同的树格式.即使字符串不应该与第二个模式匹配,它也会以某种方式匹配并因此返回错误的日期.

I'm trying to parse a date String which can have tree different formats. Even though the String should not match the second pattern it somehow does and therefore returns a wrong date.

这是我的代码:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Start {

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
        try{
            System.out.println(sdf.format(parseDate("2013-01-31")));
        } catch(ParseException ex){
            System.out.println("Unable to parse");
        }
    }

    public static Date parseDate(String dateString) throws ParseException{
        SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
        SimpleDateFormat sdf2 = new SimpleDateFormat("dd-MM-yyyy");
        SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd");

        Date parsedDate;
        try {
            parsedDate = sdf.parse(dateString);
        } catch (ParseException ex) {
            try{
                parsedDate = sdf2.parse(dateString);
            } catch (ParseException ex2){
                parsedDate = sdf3.parse(dateString);    
            }
        }
        return parsedDate;
    }
}

输入 2013-01-31 我得到输出 05.07.0036.

With the input 2013-01-31 I get the output 05.07.0036.

如果我尝试解析 31-01-201331.01.2013,我会按预期得到 31.01.2013.

If I try to parse 31-01-2013 or 31.01.2013 I get 31.01.2013 as expected.

我认识到,如果我像这样设置模式,程序会给我完全相同的输出:

I recognized that the programm will give me exactly the same output if I set the patterns like this:

SimpleDateFormat sdf = new SimpleDateFormat("d.M.y");
SimpleDateFormat sdf2 = new SimpleDateFormat("d-M-y");
SimpleDateFormat sdf3 = new SimpleDateFormat("y-M-d");

为什么它会忽略我的模式中的字符数?

Why does it ignore the number of chars in my pattern?

推荐答案

SimpleDateFormat 存在严重问题.默认的 lenient 设置会产生垃圾答案,我想不出 lenient 有任何好处的情况.宽松的设置不是对人工输入的日期变化产生合理解释的可靠方法.这不应该是默认设置.

There are serious issues with SimpleDateFormat. The default lenient setting can produce garbage answers, and I cannot think of a case where lenient has any benefit. The lenient setting is not a reliable approach to produce reasonable interpretations of human entered date variations. This should never have been the default setting.

如果可以,请改用 DateTimeFormatter,请参阅 Ole V.V. 的回答.这种较新的方法更胜一筹,可以生成线程安全且不可变的实例.如果您在线程之间共享 SimpleDateFormat 实例,它们可以产生无错误或异常的垃圾结果.遗憾的是,我建议的实现继承了这种不良行为.

Use DateTimeFormatter instead if you can, see Ole V.V.'s answer. This newer approach is superior and produces thread safe and immutable instances. If you share a SimpleDateFormat instance between threads they can produce garbage results without errors or exceptions. Sadly my suggested implementation inherits this bad behavior.

禁用 lenient 只是解决方案的一部分.您仍然可能会得到在测试中难以捕捉的垃圾结果.有关示例,请参阅下面代码中的注释.

Disabling lenient is only part of the solution. You can still end up with garbage results that are hard to catch in testing. See the comments in the code below for examples.

这是 SimpleDateFormat 的扩展,它强制进行严格的模式匹配.这应该是该类的默认行为.

Here is an extension of SimpleDateFormat that forces strict pattern match. This should have been the default behavior for that class.

import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * Extension of SimpleDateFormat that implements strict matching.
 * parse(text) will only return a Date if text exactly matches the
 * pattern. 
 * 
 * This is needed because SimpleDateFormat does not enforce strict 
 * matching. First there is the lenient setting, which is true
 * by default. This allows text that does not match the pattern and
 * garbage to be interpreted as valid date/time information. For example,
 * parsing "2010-09-01" using the format "yyyyMMdd" yields the date 
 * 2009/12/09! Is this bizarre interpretation the ninth day of the  
 * zeroth month of 2010? If you are dealing with inputs that are not 
 * strictly formatted, you WILL get bad results. You can override lenient  
 * with setLenient(false), but this strangeness should not be the default. 
 *
 * Second, setLenient(false) still does not strictly interpret the pattern. 
 * For example "2010/01/5" will match "yyyy/MM/dd". And data disagreement like 
 * "1999/2011" for the pattern "yyyy/yyyy" is tolerated (yielding 2011). 
 *
 * Third, setLenient(false) still allows garbage after the pattern match. 
 * For example: "20100901" and "20100901andGarbage" will both match "yyyyMMdd". 
 * 
 * This class restricts this undesirable behavior, and makes parse() and 
 * format() functional inverses, which is what you would expect. Thus
 * text.equals(format(parse(text))) when parse returns a non-null result.
 * 
 * @author zobell
 *
 */
public class StrictSimpleDateFormat extends SimpleDateFormat {

    protected boolean strict = true;

    public StrictSimpleDateFormat() {
        super();
        setStrict(true);
    }

    public StrictSimpleDateFormat(String pattern) {
        super(pattern);
        setStrict(true);
    }

    public StrictSimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) {
        super(pattern, formatSymbols);
        setStrict(true);
    }

    public StrictSimpleDateFormat(String pattern, Locale locale) {
        super(pattern, locale);
        setStrict(true);
    }

    /**
     * Set the strict setting. If strict == true (the default)
     * then parsing requires an exact match to the pattern. Setting
     * strict = false will tolerate text after the pattern match. 
     * @param strict
     */
    public void setStrict(boolean strict) {
        this.strict = strict;
        // strict with lenient does not make sense. Really lenient does
        // not make sense in any case.
        if (strict)
            setLenient(false); 
    }

    public boolean getStrict() {
        return strict;
    }

    /**
     * Parse text to a Date. Exact match of the pattern is required.
     * Parse and format are now inverse functions, so this is
     * required to be true for valid text date information:
     * text.equals(format(parse(text))
     * @param text
     * @param pos
     * @return
     */
    @Override
    public Date parse(String text, ParsePosition pos) {
        Date d = super.parse(text, pos);
        if (strict && d != null) {
           String format = this.format(d);
           if (pos.getIndex() + format.length() != text.length() ||
                 !text.endsWith(format)) {
              d = null; // Not exact match
           }
        }
        return d;
    }
}

这篇关于SimpleDateFormat.parse() 忽略模式中的字符数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆