【问题标题】:Two-digit dates in JFormattedTextFieldsJFormattedTextFields 中的两位数日期
【发布时间】:2021-11-11 06:39:49
【问题描述】:

我有一个本质上是 JFormattedTextField 的东西,它使用 DateFormatter,从用户控制的语言环境中获取其格式 环境。下面的代码试图在没有大量专有代码的情况下重现这一点。 (对于我在顶级 Swing 方面的粗鲁尝试,我深表歉意……)

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

原因似乎是 JFormattedTextField likes to "normalize" 它的数据通过文本表示来回传递。一种解决方法是使用测试往返的方法覆盖 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);
    }
}

【问题讨论】:

  • 有什么理由不使用 Swing 日期选择器(如 sourceforge.net/projects/jdatepicker)?
  • @PeterMmm 没有的理由。不好的原因是,这都是内部应用程序框架的一部分,离开 JFormattedTextField 需要的更改比我目前关心的要多。 :)
  • 如何用两位数的日期表示 100 多年?您可以做的最好的事情是为每个日期字段类型创建适当的滑动窗口。生日可以是当年 - 120,当年 - 21;交易日期可以是当年 - 1、当年 + 99。以此类推。
  • @camickr 我也这么认为,但显然有人忘了告诉德国人......
  • java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,改用modern Date-Time API

标签: java swing date dateformatter jformattedtextfield


【解决方案1】:

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

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

我建议您在使用JFormattedTextField 时也使用java.time,这是现代Java 日期和时间API。要获取和修改用户区域设置的格式模式:

    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

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

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

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

关于如何同时使用FormatJFormattedTextField,例如你可以看到我的另一个答案,下面有一个链接。

链接

【讨论】:

    【解决方案2】:

    以下方法对我有用,但我并不是说这是最好的或唯一的方法。

    您的问题仅在于日期而不是时间,因此在下面的代码中,我忽略了时间并仅处理日期。

    在您的情况下,JFormattedTextField 类的方法 getValue 返回 java.util.Date 的实例。此类已被包java.time 中的类取代。原因之一是您看到的 [有问题的] 行为。

    因此,在下面的代码中,我将 JFormattedTextField 的文本内容提取出来,提取 date 部分并将其转换为 java.time.LocalDate,以显示您想要的年份。

    我在上面添加了很多 println 语句只是为了演示我写的内容。

    import java.awt.Container;
    import java.awt.EventQueue;
    import java.awt.GridBagLayout;
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.time.LocalDate;
    import java.time.format.DateTimeFormatter;
    import java.util.Date;
    import java.util.Locale;
    
    import javax.swing.JButton;
    import javax.swing.JFormattedTextField;
    import javax.swing.JFrame;
    import javax.swing.text.DateFormatter;
    
    public class FmTxtFld {
        private void createAndShowGUI() {
            JFrame frame = new JFrame("FmTxtFld");
            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(e -> {
                String text = dateField.getText();
                System.out.println("Text: " + text);
                int ndx = text.indexOf(',');
                String str = text.substring(0, ndx);
                System.out.println("Date: " + str);
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yy", Locale.GERMANY);
                LocalDate ld = LocalDate.parse(str, formatter);
                System.out.println("java.time.LocalDate = " + ld);
                try {
                    dateField.commitEdit();
                }
                catch (ParseException ex) {
                    ex.printStackTrace();
                }
                Object obj = dateField.getValue();
                System.out.println("Value type: " + obj.getClass().getName());
                System.out.println("Value: " + obj);
            });
            frame.pack();
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(() -> new FmTxtFld().createAndShowGUI());
        }
    }
    

    当我运行上述代码并将年份更改为 47 时,当我按下 OK 按钮时,我得到以下输出:

    Text: 16.09.47, 15:16
    Date: 16.09.47
    java.time.LocalDate = 2047-09-16
    Value type: java.util.Date
    Value: Tue Sep 16 15:16:00 IST 1947
    

    换句话说,java.util.Date 将年份显示为 1947,而 java.time.LocalDate 将年份显示为 2047(这正是您想要的,对吧?)

    【讨论】:

      猜你喜欢
      • 2018-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多