即使日期格式不包含毫秒,如何始终包含毫秒?

How to always include milliseconds even when Date format does not include it?

这个例子有 2 列数据。

第 1 列是 没有 毫秒的日期,第 2 列是 毫秒的数据。

显示允许用户更改日期格式。关键是第 1 列不应包含毫秒,而第 2 列应始终包含毫秒。

如果日期格式为 dd-MM-yyyy HH:mm:ss,代码可以在处理第 2 列中的数据时简单地附加“.SSS”,但格式可能有很大差异。例如,它甚至可以是不包括任何时间参考的“MM-dd-yy”,但第二列应始终包括时间和毫秒。

这是代码。有额外的杂项代码只是为了确保毫秒在第一时间可见。

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;

public class DateFormatRendererExample 
{

    public static void main(String[] args) 
    {
        DateExampleSetup dateExampleSetup = new DateExampleSetup (); 

    }

}


class DateExampleSetup extends JFrame 
{
    final static Class[] columnClass = new Class[] { Date.class, Date.class };
    String currentDateFormat = "MM-dd-yyyy HH:mm:ss";
    String oldDateFormat = "MM-dd-yyyy HH:mm:ss";
    MyTableModel model;
    
    public DateExampleSetup() 
    {

        model = new MyTableModel();
        model.addRow("05-22-2020 12:12:12", "06-02-2020 20:11:55.123");
        model.addRow("10-18-2020 14:16:17", "01-02-2020 09:09:49.567");
        
        JTable table = new JTable(model);
        table.setDefaultRenderer(Date.class, new MyDateCellRenderer(this));
        JTextField dateFormatField = new JTextField(20);
        dateFormatField.setText(currentDateFormat);
        JButton activateDateFormat = new JButton("Activate new Date Format");
        activateDateFormat.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent arg0) 
            {
                
                setCurrentDateFormat(dateFormatField.getText());
                MyTableModel newModel = new MyTableModel ();
                newModel.addRow("05-22-2020 12:12:12", "06-02-2020 20:11:55.1234");
                newModel.addRow("10-18-2020 14:16:17", "02-02-2020 09:09:49.5678");
                table.setModel(newModel);
                newModel.fireTableDataChanged();
                setOldDateFormat(getCurrentDateFormat());
            }
            
        });
        setLayout(new BorderLayout());
        add(dateFormatField, BorderLayout.NORTH);
        add(table, BorderLayout.CENTER);
        add(activateDateFormat, BorderLayout.SOUTH);
        setVisible(true);
        setSize(500,300);
        
    }
    public String getCurrentDateFormat()
    {
        return currentDateFormat;
    }
    public void setCurrentDateFormat(String newCurrent)
    {
        currentDateFormat = newCurrent;
    }
    public String getOldDateFormat()
    {
        return oldDateFormat;
    }
    public void setOldDateFormat(String newCurrent)
    {
        currentDateFormat = newCurrent;
    }

}

class MyDateCellRenderer extends JLabel implements TableCellRenderer 
{
    DateExampleSetup des;
    boolean firstTimeRow1 = true;
    boolean firstTimeRow2 = true;
    
    public MyDateCellRenderer(DateExampleSetup des)
    {
        super();
        this.des = des;
    }
      public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
          boolean hasFocus, int row, int col) {
          
        try 
        {
            ///Just to get the milliseconds to show the first time.
            if (firstTimeRow1 && row == 0 && col == 1)
            {
                SimpleDateFormat tempDateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS");
                Date tempDate = null;
                tempDate = tempDateFormat.parse((String)value);
                value = tempDateFormat.format(tempDate);
                setText(value.toString());
                firstTimeRow1 = false;
            }
            else if (firstTimeRow2 && row == 1 && col == 1)
            {
                SimpleDateFormat tempDateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS");
                Date tempDate = null;
                tempDate = tempDateFormat.parse((String)value);
                value = tempDateFormat.format(tempDate);
                setText(value.toString());
                firstTimeRow2 = false;
            }
            else
            {
                SimpleDateFormat oldDateFormat = new SimpleDateFormat(des.getOldDateFormat());
                SimpleDateFormat newDateFormat = new SimpleDateFormat(des.getCurrentDateFormat());
                Date date = null;
                date = oldDateFormat.parse((String)value);
                value = newDateFormat.format(date);
                setText(value.toString());
            }
        } 
        catch (ParseException e) 
        {
            e.printStackTrace();
        }

        return this;
      }
    }

class MyTableModel extends AbstractTableModel {

    private List<String[]> rows;
    private String columnNames[] = { "Regular Date ", "Date with milliseconds"};

    public MyTableModel() {
        rows = new ArrayList<>(25);
    }
    
    @Override
    public int getRowCount() {
        return rows.size();
    }

    @Override
    public int getColumnCount() {
        return 2;
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return Date.class;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        String[] row = rows.get(rowIndex);
        return row[columnIndex];
    }
    
    @Override
    public String getColumnName(int col) {
        return columnNames[col];
    }
    
    public void addRow(String s1, String s2) 
    {
        String[] row = new String[getColumnCount()];
        row[0] = s1;
        row[1] = s2;
        rows.add(row);
        fireTableRowsInserted(getRowCount(), getRowCount());
    }            
} 

执行下面的Frame时会显示出来。注意第二列有毫秒。

当日期更改为“dd-MM-yyyy”并按下“激活新日期格式”按钮时,第二列中的日期将丢失其小时、分钟和秒,尤其是毫秒。

是否可以在确保 HH:mm:ss.sss 始终存在的同时,对一列使用单一日期格式(几乎可以是任何格式),同时对另一列使用相同的格式?

只需将渲染器更改为此

try {
     if (col == 0) {
          SimpleDateFormat oldDateFormat = new SimpleDateFormat(des.getOldDateFormat());
          SimpleDateFormat newDateFormat = new SimpleDateFormat(des.getCurrentDateFormat());
          Date date = oldDateFormat.parse((String) value);
          value = newDateFormat.format(date) ;
          setText(value.toString());
    } else {
          SimpleDateFormat tempDateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS");
          Date tempDate = tempDateFormat.parse((String) value);
          value = tempDateFormat.format(tempDate);
          setText(value.toString());
    }
} catch (ParseException e) {
            e.printStackTrace();
}

但如@Ole 所述V.V。你不应该使用 DateSimpleDateFormat。此外,我最好不要在你的模型中使用 String,而是已经使用 LocalDateTime - 所以它不允许将字符串转换为时间,反之亦然

您首先需要明确定义输入规则、输出要求以及调整输入以满足这些输出要求的规则。

好像用户输入没有规则,所以很难..他们想输入什么就输入什么!也许至少需要几年、几个月和几天才能稍微简化一下?另一个输入要求可能是,如果它们包括毫秒,则它们还必须包括秒,如果是秒,则必须包括分钟,如果是分钟,则必须包括小时,等等。这意味着您不必担心只有分钟而没有小时或类似的东西.

您的输出要求似乎是包含年、月、日、时、分、秒、毫秒,所有定义的内容均采用用户格式,但未定义的内容添加到格式中.但是您对添加这些剩余字段的规范是什么?

这里有一些代码可以帮助您入门。如果您要向模式添加更多字段,可能需要进一步检查输入使用的分隔符。

public static void main(String[] args) throws Exception {

    // try with additional valid input patterns, such as 'MM/dd/yyyy' etc
    String[] patterns = {
            "yyyy-MM-dd HH:mm:ss.SSS",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd HH:mm",
            "yyyy-MM-dd HH",
            "yyyy-MM-dd",
            "HH:mm:ss.S yyyy-MM-dd",
            "HH:mm:ss yyyy-MM-dd",
            "HH:mm yyyy-MM-dd", };
    LocalDateTime date = LocalDateTime.now();
    for (String pattern : patterns) {
        String newPattern = fillInPattern(pattern);
        System.out.println(pattern + "\t\t" + newPattern + "\t\t" + DateTimeFormatter.ofPattern(newPattern).format(date));
    }
    // Output:
    // yyyy-MM-dd HH:mm:ss.SSS      yyyy-MM-dd HH:mm:ss.SSS     2021-05-04 11:15:53.743
    // yyyy-MM-dd HH:mm:ss          yyyy-MM-dd HH:mm:ss.SSS     2021-05-04 11:15:53.743
    // yyyy-MM-dd HH:mm             yyyy-MM-dd HH:mm:ss.SSS     2021-05-04 11:15:53.743
    // yyyy-MM-dd HH                yyyy-MM-dd HH:mm:ss.SSS     2021-05-04 11:15:53.743
    // yyyy-MM-dd                   yyyy-MM-dd HH:mm:ss.SSS     2021-05-04 11:15:53.743
    // HH:mm:ss.S yyyy-MM-dd        HH:mm:ss.SSS yyyy-MM-dd     11:15:53.743 2021-05-04
    // HH:mm:ss yyyy-MM-dd          HH:mm:ss.SSS yyyy-MM-dd     11:15:53.743 2021-05-04
    // HH:mm yyyy-MM-dd             HH:mm:ss.SSS yyyy-MM-dd     11:15:53.743 2021-05-04
}

private static String fillInPattern(String pattern) {
    // Get the range of each of the pattern fields you need to check
    // Parsing like this should be mostly safe since field pattern characters are generally adjacent to each other
    // and you don't expect repeats of a field. You could invalidate any pattern which violates this assumption.
    Map<Character, Range> fields = new HashMap<>();
    for (int i = 0; i < pattern.length(); i++) {
        char c = pattern.charAt(i);
        if (c == 'H' || c == 'm' || c == 's' || c == 'S') {
            int start = i;
            while (i < pattern.length() && pattern.charAt(i) == c) {
                i++;
            }
            fields.put(c, new Range(start, i));
        }
    }
    // Add the missing fields:
    // Works when you guarantee that if there's millis then there's seconds, if seconds then minutes, and if minutes then hours..
    StringBuilder builder = new StringBuilder(pattern);
    if (!fields.containsKey('H')) {
        builder.append(" HH:mm:ss.SSS");
    } else if (!fields.containsKey('m')) {
        builder.insert(fields.get('H').end(), ":mm:ss.SSS");
    } else if (!fields.containsKey('s')) {
        builder.insert(fields.get('m').end(), ":ss.SSS");
    } else if (!fields.containsKey('S')) {
        builder.insert(fields.get('s').end(), ".SSS");
    } else {
        // it contains all the fields but make sure there's enough fractional seconds
        Range fSecs = fields.get('S');
        if (fSecs.length() < 3) {
            builder.replace(fSecs.start(), fSecs.end(), "SSS");
        }
    }
    return builder.toString();
}

public static class Range {
    private final int start;
    private final int end;

    public Range(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public int start() { return start;  }
    public int end() { return end; }
    public int length() { return end - start; }
    @Override public String toString() { return start + "-" + end; }
}