即使日期格式不包含毫秒,如何始终包含毫秒?
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。你不应该使用 Date
和 SimpleDateFormat
。此外,我最好不要在你的模型中使用 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; }
}
这个例子有 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。你不应该使用 Date
和 SimpleDateFormat
。此外,我最好不要在你的模型中使用 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; }
}