JXDatePicker 使用 SimpleDateFormat 将 dd.MM.yy 格式化为当前世纪的 dd.MM.yyyy
Posted
技术标签:
【中文标题】JXDatePicker 使用 SimpleDateFormat 将 dd.MM.yy 格式化为当前世纪的 dd.MM.yyyy【英文标题】:JXDatePicker using SimpleDateFormat to format dd.MM.yy to dd.MM.yyyy with current century 【发布时间】:2012-02-20 12:41:54 【问题描述】:正如我已经解释的那样,当用户在 JXDatePicker 中编辑日期时,他可以选择,天气他以相同的格式再次键入它,默认情况下是 dd.MM.yyyy 或只是 dd。嗯。当他使用缩写形式时,我希望 Picker 选择当前世纪。
示例:
27.01.2012 edited to 27.01.10 should result in 27.01.2010
还有:
27.01.2012 edited to 27.01.2010 should also result in 27.01.2010
默认情况下,JXDatePicker 以下列方式处理它:
27.01.2012 edited to 27.01.10 results in 27.01.0010
这并不是我真正想要的工作方式。经过一些简短的研究,我在 SimpleDateFormat 中找到了以下方法
/**
* Sets the 100-year period 2-digit years will be interpreted as being in
* to begin on the date the user specifies.
*
* @param startDate During parsing, two digit years will be placed in the range
* <code>startDate</code> to <code>startDate + 100 years</code>.
*/
public void set2DigitYearStart(Date startDate)
乍一看,这听起来完全符合我的需要。所以我测试了它,不幸的是它没有像我希望的那样工作。这是因为我想使用 dd.MM.yyyy 作为格式来显示日期,并且还希望它像在编辑模式中那样显示。例如,当用户点击像 27.01.2012 这样的日期时,我也希望它在编辑模式下也一样,而不仅仅是缩写形式:27.01.12。
我现在的问题是,不幸的是,set2DigitYearStart(Date) 仅在我选择在编辑模式下使用简写形式时才有效。我做了一个小例子来展示这个案例(需要 SwingX 库,因为 jxdatepicker 可以找到 here)。
public class DatePickerExample extends JPanel
static JFrame frame;
public DatePickerExample()
JXDatePicker picker = new JXDatePicker();
JTextField field = new JTextField( 10 );
add( field );
add( picker );
final Calendar instance = Calendar.getInstance();
instance.set( 2012, 01, 26 );
Date date = instance.getTime();
picker.setDate( date );
// SimpleDateFormat format = new SimpleDateFormat( "dd.MM.yy" );//Works, but I wonna display and edit it with dd.MM.yyyy
SimpleDateFormat format = new SimpleDateFormat( "dd.MM.yyyy" );
final Date startDate = new Date( 0 );//01.01.1970
format.set2DigitYearStart( startDate );
picker.setFormats( format );
public static void main( String[] args )
frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setBounds( 400, 400, 400, 400 );
frame.setLayout( new BorderLayout() );
frame.add( new DatePickerExample() );
frame.setVisible( true );
任何人已经有相同的要求,可以告诉我如何进行这项工作吗?欢迎任何想法。非常感谢您提前。梅内
【问题讨论】:
“我想让 Picker 选择当前世纪。” 啊,早点准备 Y2.1K 的 bug,我明白了。 ;) 哈哈,抓到我了!尽管它是 2012 年,但考虑该软件未来的工作方式并没有错! :P 【参考方案1】:决赛(希望如此:)
第一次编辑总结:
DatePickerFormatter 已经实现了一个查找策略(或 @Robin 建议的 CompoundFormat) 解析的查找顺序可由客户端代码配置 想法是尝试从第一个(通常是“最长”)开始解析,如果失败则尝试下一个(通常是“不太长”),依此类推,直到成功或抛出 parseException 对于年份解析,SimpleDateFormat 的规则与最长优先查找冲突:它要求在“yyyy”之前尝试“yy” 在 datePicker 中这样做会产生不希望的副作用,即始终以短年份格式显示日期原因是 DatePickerFormatter:它不允许指定 formatting 格式(仅使用第一种)。出路是一个自定义的 DatePickerFormatter,它支持它(在 sn-p 中,它被硬编码为使用第二个):
SimpleDateFormat longFormat = new SimpleDateFormat( "dd.MM.yyyy" );
SimpleDateFormat shortFormat = new SimpleDateFormat( "dd.MM.yy" );
Date startDate = new Date( 0 );//01.01.1970
shortFormat.set2DigitYearStart( startDate );
DatePickerFormatter formatter = new DatePickerFormatter(
// invers sequence for parsing to satisfy the year parsing rules
new DateFormat[] shortFormat, longFormat)
@Override
public String valueToString(Object value) throws ParseException
if (value == null) return null;
return getFormats()[1].format(value);
;
DefaultFormatterFactory factory = new DefaultFormatterFactory(formatter );
picker.getEditor().setFormatterFactory(factory);
不完全确定我们是否应该支持在基类中配置格式化程序。 DatePickerFormatter 有点奇怪,它没有扩展 InternalFormatter 并且查找过程有点与 FormatterFactory 竞争......
原创
不完全是 datePicker 以这种方式处理它,它是核心格式(正如 D1e 已经指出的那样)。默认的格式/ter/s 都不支持两种格式:看看,尝试用一个核心 JFormattedTextField 来实现你的目标:-)
出路可能是 FormatterFactory:它允许使用不同的格式,具体取决于上下文:显示和编辑 - 后者在字段获得焦点时使用,前者在所有其他时间使用。由于选取器的编辑器是一个 JFormattedTextField,你可以直接配置它(而不是使用 setFormats 方法)
SimpleDateFormat format = new SimpleDateFormat( "dd.MM.yyyy" );
SimpleDateFormat editFormat = new SimpleDateFormat( "dd.MM.yy" );
final Date startDate = new Date( 0 );//01.01.1970
instance.setTime(startDate);
editFormat.set2DigitYearStart( instance.getTime() );
DefaultFormatterFactory factory = new DefaultFormatterFactory(
new DatePickerFormatter(new DateFormat[] format),
new DatePickerFormatter(new DateFormat[] format),
new DatePickerFormatter(new DateFormat[] editFormat)
);
picker.getEditor().setFormatterFactory(factory);
编辑
在阅读了 Robin 最近的回答 (+1!) 后头晕目眩 - 最后,令人尴尬的是,多年之后,我明白 SwingX 的 DatePickerFormatter 试图做什么:即支持格式化程序的查找链(从更长到更短) ),提交后使用时间最长,时间越短,方便用户输入。
不幸的是,这并没有按直觉预期工作。给定一系列格式,从长到短(并适当配置为世纪):
"yyyy", "yy"
和给定的输入
"10"
感觉像是从第一个传到第二个,导致
2010
但不是。正如在 SimpleDateFormat 中记录的那样(谁阅读了文档……懒惰我,咳嗽……)
Year: [ ... ] 对于解析,如果模式字母的数量超过 2 个,则按字面解释年份,而不考虑位数。所以使用模式“MM/dd/yyyy”,“01/11/12”解析到公元 12 年 1 月 11 日
归根结底 - 由于 DatePickerFormatter 尝试支持该查找但未成功 - 毕竟这可能被视为 SwingX 问题:-)
【讨论】:
克娄巴特拉!我希望在里面放一个 swingx 标签,我会引起你的注意:D。首先感谢您的回答,很抱歉我之前无法对此发表评论。不幸的是,我也已经按照您的建议进行了尝试。我知道这工作得很好。我不喜欢的唯一一点是:当用户在显示 27.01.2012 时单击文本字段时,在用户开始自行更改日期之前,它会在编辑模式(由于 editformat)中更改为 27.01.12 .我希望它仍然显示 27.01.2012,而他正在编辑它。 您是否有任何简单的建议如何实现这一点,或者我真的必须覆盖所涉及的 JFormattedTextFields 以自行格式化这些字符串?我还注意到我可以添加几种格式作为编辑格式。但不幸的是,这也没有帮助。 正如我在回答中已经指出的那样:这是核心行为,完全不受 SwingX 的控制。没有真正挖掘,但我认为您通常不能同时拥有两者 - 格式化程序应该如何知道使用哪个?你可能尝试在两种格式之上实现一个格式化程序..当你成功时请告诉我们,这可能是对 SwingX 的一个很好的贡献:-) 这是一个有趣的想法。由于我的时间不多了,我将首先使用默认行为,但您的想法已被记录,一旦我有一个令人满意的解决方案,我会通知您:-) @kleopatra 看看我对复合格式化程序的回答。【参考方案2】:我不太了解 JXDatePicker,但如果您要模拟的具体功能是:用户输入 27.01.2010 和 27.01.10 独立应导致 27.01.2010
那么这将起作用:
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Main
public static void main(String[] args) throws ParseException
String inputLiteralDateYY = "27.01.10"; //Also works with "27.01.97"
String inputLiteralDateYYYY = "27.01.2010"; //Also works with "27.01.1997"
DateFormat dfYYYY = new SimpleDateFormat("dd.MM.yyyy");
DateFormat dfYY = new SimpleDateFormat("dd.MM.yy");
Date dateFromYY = dfYY.parse(inputLiteralDateYY);
Date dateFromYYYY = dfYY.parse(inputLiteralDateYYYY);
String outputLiteralDateFromYY = dfYYYY.format(dateFromYY);
String outputLiteralDateFromYYYY = dfYYYY.format(dateFromYYYY);
System.out.println(outputLiteralDateFromYY);
System.out.println(outputLiteralDateFromYYYY);
事情是首先你用“dd.MM.yy”模式解析输入,然后用“dd.MM.yyyy”模式返回它的格式。
希望这有助于或有助于将其应用于您的场景。
【讨论】:
抱歉我的回复晚了,但我不确定是否可以或应该进入组件验证/格式化过程之间,尽管我也已经考虑过覆盖 JFormattedTextField 的 commitEdit 以使我自己有所不同, 哪种格式适合使用。因此,我将此作为最后的解决方案。谢谢你:)【参考方案3】:kleopatra 已经解释了如何在日期选择器上设置Format
。对于这个用例,我将应用CompositeFormat
和ParseAllFormat
的组合,而不是使用单独的编辑格式和常规模式以避免在开始编辑时更改String
(正如您已经注意到的那样)。
复合格式
复合格式,顾名思义,是Format
类的composite implementation,但仅用于解析。对于格式,它使用一个Format
。这允许用户以多种形式输入他/她的日期,同时通过使用一种特定格式来统一格式化。
您也可以通过编写更复杂的Format
来获得这种行为。但在这种情况下,只使用 JDK 的SimpleDateFormat
类提供的格式化/解析功能会更容易。
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.List;
/**
* <p>Composite form of @link java.text.Format Format. It uses multiple formats for parsing, and
* only one format for formatting.</p>
*
* <p>A possible use-case is the formatting of user input (e.g. in a @code JFormattedTextField).
* Multiple formats for parsing allows accepting multiple forms of user input without having to
* write a complicated format.</p>
*/
public class CompositeFormat extends Format
private List<Format> fFormats = new ArrayList<>();
private Format fFormattingFormat;
/**
* Create a new
*/
public CompositeFormat()
/**
* Add a format to this composite format
*
* @param aFormat The format to add
*/
public void addFormat( Format aFormat )
assertNotNull( aFormat, "You cannot add a null Format" );
if ( !( fFormats.contains( aFormat ) ) )
fFormats.add( aFormat );
/**
* Remove a format from this composite format
*
* @param aFormat The format to remove
*/
public void removeFormat( Format aFormat )
assertNotNull( aFormat, "You cannot remove a null Format" );
fFormats.remove( aFormat );
updateFormattingFormat();
/**
* Sets <code>aFormat</code> as the format which will be used for formatting the
* objects. The format will also be added to the list of available formats.
* @param aFormat The format which will be used for formatting
*/
public void setFormattingFormat( Format aFormat )
assertNotNull( aFormat, "Formatting format may not be null" );
addFormat( aFormat );
fFormattingFormat = aFormat;
private void assertNotNull( Object aObjectToCheck, String aMessage )
if ( aObjectToCheck == null )
throw new NullPointerException( aMessage );
private void updateFormattingFormat()
if ( !( fFormats.contains( fFormattingFormat ) ) )
fFormattingFormat = null;
if ( !( fFormats.isEmpty() ) )
fFormattingFormat = fFormats.iterator().next();
@Override
public StringBuffer format( Object obj, StringBuffer toAppendTo, FieldPosition pos )
assertNotNull( fFormattingFormat, "Set a formatting format before using this format" );
return fFormattingFormat.format( obj, toAppendTo, pos );
@Override
public Object parseObject( String source, ParsePosition pos )
if ( fFormats.isEmpty() )
throw new UnsupportedOperationException( "Add at least one format before using this composite format" );
Format formatToUse = fFormats.iterator().next();
int maxIndex = pos.getIndex();
for ( Format format : fFormats )
ParsePosition tempPos = new ParsePosition( pos.getIndex() );
tempPos.setErrorIndex( pos.getErrorIndex() );
format.parseObject( source, tempPos );
if ( tempPos.getIndex() > maxIndex )
maxIndex = tempPos.getIndex();
formatToUse = format;
if( maxIndex == source.length() )
//found a format which parses the whole string
break;
return formatToUse.parseObject( source, pos );
ParseAllFormat
通常对于用户输入,您希望可以对整个用户输入进行格式化/解析,以避免用户输入一半正确的字符串。 ParseAllFormat
是常规 Format
的 decorator,当只能解析 String
的一部分时,它会抛出 ParseException
s。
import java.text.AttributedCharacterIterator;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
/**
* <p>Decorator for a @link Format Format which only accepts values which can be completely parsed
* by the delegate format. If the value can only be partially parsed, the decorator will refuse to
* parse the value.</p>
*/
public class ParseAllFormat extends Format
private final Format fDelegate;
/**
* Decorate <code>aDelegate</code> to make sure if parser everything or nothing
*
* @param aDelegate The delegate format
*/
public ParseAllFormat( Format aDelegate )
fDelegate = aDelegate;
@Override
public StringBuffer format( Object obj, StringBuffer toAppendTo, FieldPosition pos )
return fDelegate.format( obj, toAppendTo, pos );
@Override
public AttributedCharacterIterator formatToCharacterIterator( Object obj )
return fDelegate.formatToCharacterIterator( obj );
@Override
public Object parseObject( String source, ParsePosition pos )
int initialIndex = pos.getIndex();
Object result = fDelegate.parseObject( source, pos );
if ( result != null && pos.getIndex() < source.length() )
int errorIndex = pos.getIndex();
pos.setIndex( initialIndex );
pos.setErrorIndex( errorIndex );
return null;
return result;
@Override
public Object parseObject( String source ) throws ParseException
//no need to delegate the call, super will call the parseObject( source, pos ) method
return super.parseObject( source );
这两个类的组合允许以下代码
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class FormattingDemo
private static Format createCompositeDateFormat()
Format formattingFormat = new ParseAllFormat( new SimpleDateFormat( "dd.MM.yyyy" ) );
SimpleDateFormat shortFormat = new SimpleDateFormat( "dd.MM.yy" );
Format otherFormat = new ParseAllFormat( shortFormat );
CompositeFormat compositeFormat = new CompositeFormat();
compositeFormat.addFormat( otherFormat );
compositeFormat.addFormat( formattingFormat );
compositeFormat.setFormattingFormat( formattingFormat );
return compositeFormat;
public static void main( String[] args ) throws ParseException
Format dateFormat = createCompositeDateFormat();
System.out.println( dateFormat.parseObject( "27.01.2010" ) );
System.out.println( dateFormat.parseObject( "27.01.10" ) );
System.out.println( dateFormat.parseObject( "27.01.2012" ) );
System.out.println(dateFormat.format( dateFormat.parseObject( "27.01.2010" ) ));
System.out.println(dateFormat.format( dateFormat.parseObject( "27.01.10" ) ));
System.out.println(dateFormat.format( dateFormat.parseObject( "27.01.2012" ) ));
产生以下输出
Wed Jan 27 00:00:00 CET 2010
Wed Jan 27 00:00:00 CET 2010
Fri Jan 27 00:00:00 CET 2012
27.01.2010
27.01.2010
27.01.2012
请注意,有一个小问题我没有找到合适的解决方案。将Format
实例添加到CompositeFormat
的顺序也是评估它们以进行解析的顺序。在这种情况下,您需要以正确的顺序添加它们,因为即使 new SimpleDateFormat( "dd.MM.yyyy" )
似乎也接受输入字符串 27.01.10
并且可以将整个 String
解析为等效于 27.01.0010
的 Date
对象。
【讨论】:
daaarn ...咳嗽..查看我的编辑(+1!希望这有助于在 SwingX 中正确使用) @kleopatra 我在解析年份时遇到了同样的问题......我添加了关于复合格式中格式顺序的注释的主要原因以上是关于JXDatePicker 使用 SimpleDateFormat 将 dd.MM.yy 格式化为当前世纪的 dd.MM.yyyy的主要内容,如果未能解决你的问题,请参考以下文章
JXDatePicker 使用 SimpleDateFormat 将 dd.MM.yy 格式化为当前世纪的 dd.MM.yyyy