问题描述
此示例有 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());
}
}
执行时显示以下帧。请注意,第二列是毫秒。
当日期更改为“dd-MM-yyyy”并按下“激活新日期格式”按钮时,第 2 列中的日期将丢失小时、分钟和秒,尤其是毫秒。
是否可以对一列使用单一日期格式(几乎可以是任何格式),同时对另一列使用相同的格式,同时确保 HH:mm:ss.sss 始终存在?
解决方法
您首先需要明确定义输入规则、输出要求以及调整输入以满足这些输出要求的规则。
似乎用户输入没有规则,这使得这很困难..他们可以输入任何他们想要的!也许至少需要数年、数月和数天来简化这一点?另一个输入要求可能是,如果它们包括毫秒,那么它们也必须包括秒,如果是秒然后是分钟,如果是分钟然后是小时,等等。这意味着你不必担心只有分钟没有小时或类似的东西.
似乎您的输出要求是包含年、月、日、小时、分钟、秒、毫秒,所有这些都在用户格式中,用于他们定义的任何内容,但添加到未定义的任何格式中。但是您添加剩余字段的规范是什么?
这里有一些代码可以帮助您入门。如果您要向模式添加更多字段,则需要添加的其他内容可能是进一步检查输入使用的分隔符。
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; }
}
,
只需将渲染器更改为此
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.您不应使用 Date
和 SimpleDateFormat
。此外,我最好不要在您的模型中使用 String
而是已经使用 LocalDateTime
- 所以它不允许将字符串转换为时间,反之亦然