首页 > 解决方案 > JFormattedTextFields 中的两位数日期

问题描述

我有一个本质上是使用 DateFormatter 的 JFormattedTextField,它从用户控制的区域设置中获取其格式。下面的代码试图在没有大量专有代码的情况下重现这一点。(我为我在顶级 Swing 方面的粗鲁尝试道歉......)

我的问题是,如果用户选择了具有两位数年份的语言环境,则无法输入“错误”世纪的日期。例如,如果我运行下面的程序并将年份编辑为 2047 并单击确定,程序将打印 1947 年的日期。

原因似乎是JFormattedTextField 喜欢通过在文本表示中往返来“规范化”其数据。一种解决方法是使用测试往返的方法覆盖 commitEdit(),如果结果破坏了年份,则用硬编码的 YYYY-MM-DD 替换格式。不过,这似乎有点笨拙。有没有更好的方法,除了要求用户选择一个理智的语言环境或为这个特定字段硬连接格式?(我知道有多种方法可以选择哪个 100 年窗口是正确的,但这仍然将我限制在 100 年窗口)。

import javax.swing.*;
import javax.swing.text.DateFormatter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;

public class Main {
    private static void createAndShowGUI() {
        JFrame frame = new JFrame("HelloWorldSwing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        var gbl = new GridBagLayout();
        frame.setLayout(gbl);

        final Container contentPane = frame.getContentPane();

        Date date = new Date();
        JFormattedTextField dateField = new JFormattedTextField(date);
        dateField.setFormatterFactory(new JFormattedTextField.AbstractFormatterFactory() {
            @Override
            public JFormattedTextField.AbstractFormatter getFormatter(JFormattedTextField tf) {
                return new DateFormatter(DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.GERMANY));
            }
        });
        contentPane.add(dateField);

        JButton button = new JButton("Ok");
        contentPane.add(button);
        button.addActionListener((ActionEvent e) -> {
            try {
                dateField.commitEdit();
            } catch (ParseException ex) {
                ex.printStackTrace();
            }
            System.out.println(dateField.getValue());
        });
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(Main::createAndShowGUI);
    }
}

标签: javaswingdatedateformatterjformattedtextfield

解决方案


如果您需要四位数年份以确保具有正确的世纪并且您的语言环境在其SHORT日期格式中使用两位数年份,我看到两个选项:

  1. 请改用MEDIUM日期格式。我怀疑在许多语言环境中,这将迫使用户进行更多的打字,他们可能不会对此感到满意。
  2. 修改语言环境的格式以要求 4 位数年份。根据观点,它可能被视为残酷或有趣的妥协,但它为我们提供了我们需要的东西。

我建议您在使用JFormattedTextField. 要获取和修改用户区域设置的格式模式:

    Locale userSelectedLocale = Locale.GERMAN;
    String formatPattern = DateTimeFormatterBuilder
            .getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT,
                    IsoChronology.INSTANCE, userSelectedLocale);
    
    System.out.println("Localized format:          " + formatPattern);
    formatPattern = formatPattern.replaceFirst("y+", "yyyy");
    System.out.println("Modified localized format: " + formatPattern);

到目前为止的输出是:

Localized format:          dd.MM.yy, HH:mm
Modified localized format: dd.MM.yyyy, HH:mm

要将其与 a 一起使用,JFormattedTextField我们需要旧Format类(的超类DateFormat)的实例。要构建它:

    Format formatForFormattedTextField 
            = DateTimeFormatter.ofPattern(formatPattern, userSelectedLocale)
                    .toFormat(LocalDateTime::from);

我指定格式化程序应该解析成LocalDateTime对象。LocalDateTime是没有时区或偏移量的日期和时间的 java.time 类。由于用户既没有输入时区也没有输入偏移量,这似乎是我们的目的正确的类。

对于如何使用FormatJFormattedTextField一起使用,您可以例如看到我的另一个答案,下面有一个链接。

链接


推荐阅读