簡體   English   中英

JXDatePicker使用SimpleDateFormat將dd.MM.yy格式化為當前世紀的dd.MM.yyyy

[英]JXDatePicker using SimpleDateFormat to format dd.MM.yy to dd.MM.yyyy with current century

正如我已經解釋過的那樣,當用戶在JXDatePicker中編輯日期時,他可以選擇,天氣他再次以相同的格式輸入它,默認情況下是dd.MM.yyyy或者只是dd.MM.yy 。 當他使用短形式時,我希望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作為格式來顯示日期,並希望它在editmode中顯示。 例如,當用戶點擊像27.01.2012這樣的日期時,我也希望它在editmode中也是如此,而不僅僅是簡短形式:27.01.12。

我現在的問題是,當我選擇在editmode中使用shortform時,set2DigitYearStart(Date)不幸地起作用。 我做了一個小例子來展示這種情況(需要使用SwingX庫,因為jxdatepicker可以在這里找到)。

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 );
  }
}

任何人都有相同的要求,可以告訴我如何使這項工作? 歡迎任何想法。 非常感謝你提前。 ymene

決賽 (希望:)

第一次編輯摘要:

  • DatePickerFormatter已經實現了查找策略(或者@Robin建議的CompoundFormat)
  • 解析的查找序列可由客戶端代碼配置
  • 我的想法是嘗試從第一個(通常是“最長的”)開始解析,如果失敗則嘗試下一個(通常是“不那么長”),依此類推,直到成功或拋出parseException為止
  • 對於年份解析,SimpleDateFormat具有與最長優先查找沖突的規則:它要求在“yyyy”之前嘗試“yy”
  • 在datePicker中執行此操作會產生不必要的副作用,即始終以短年格式顯示日期

原因是DatePickerFormatter:它不允許指定格式化格式(只使用第一個)。 出路是一個自定義的DatePickerFormatter,它支持它(在代碼片段中,硬編碼使用第二個):

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);

不完全確定我們是否應該支持在基類中配置formatter。 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中記錄的那樣(誰讀了文檔......懶惰我,咳嗽......)

年份:[...]對於解析,如果模式字母的數量大於2,則無論數字位數如何,都將按字面解釋年份。 所以使用“MM / dd / yyyy”模式,“01/11/12”解析到公元12年1月11日

在一天結束時 - 由於DatePickerFormatter試圖支持該查找但不成功 - 畢竟這可能被認為是一個SwingX問題:-)

我並不是特別了解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”模式格式化。

希望這有助於或幫助將其應用於您的場景。

kleopatra已經解釋了如何在日期選擇器上設置Format 對於這個用例,我將應用CompositeFormatParseAllFormatCompositeFormat ,而不是使用單獨的格式進行編輯和常規模式,以避免在開始編輯時更改String (正如您已經注意到的)。

復合格式

顧名思義,復合格式是Format類的復合實現 ,但僅用於解析。 對於格式化,它使用一個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

通常,對於用戶輸入,您希望可以格式化/解析整個用戶輸入,以避免用戶輸入半正確的String。 ParseAllFormat是常規Format裝飾器 ,當只能解析部分String時拋出ParseException

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.0010Date對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM