关于“java中日期格式化的大坑”,我会从以下几个方面进行讲解:
- Java中日期格式化的基本知识
- Java中日期格式化的坑点
- 解决Java中日期格式化的坑点的方法
- 两个示例来说明日期格式化的坑点
Java中日期格式化的基本知识
在Java中,要进行日期格式化,需要用到SimpleDateFormat
类。该类是线程不安全的类,一般情况下,建议使用ThreadLocal
来保证线程安全性。
下面是一个示例代码:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = sdf.format(new Date());
System.out.println(dateStr);
以上代码中,首先定义了一个SimpleDateFormat
对象sdf
,并指定了日期格式为“yyyy-MM-dd HH:mm:ss”。其次,使用sdf
对象对当前时间进行格式化,返回一个字符串类型的时间值dateStr
。最后,在控制台输出该值。
Java中日期格式化的坑点
Java中日期格式化的坑点在于,如果使用不当,可能会导致一些意想不到的结果。具体来说,有以下几点:
1. yyyy和YYYY的区别
在格式化字符串中,yyyy和YYYY有着不同的含义。其中,yyyy表示“年”,而YYYY表示“周年数”。
示例代码:
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat sdf2 = new SimpleDateFormat("YYYY/MM/dd");
String dateStr = "2020/01/01";
Date date = sdf1.parse(dateStr);
System.out.println(sdf1.format(date));
System.out.println(sdf2.format(date));
在以上示例代码中,我们分别定义了两个SimpleDateFormat
对象,分别用于表示yyyy和YYYY的含义。然后,对字符串“2020/01/01”进行parse()
操作,并通过sdf1
对象和sdf2
对象进行格式化。我们会发现,输出的结果如下所示:
2020/01/01
2019/01/01
结果表明,使用YYYY会导致格式化后的年份比实际年份少1年。这是由于YYYY表示的是“周年数”,计算周年数是从上一年12月31日开始计算的。而我们这里只给出了月份和日期,格式化时出现了上述问题。
2. HH和hh的区别
在格式化字符串中,HH和hh也有着不同的含义。其中,HH表示“24小时制”,而hh表示“12小时制”。
示例代码:
SimpleDateFormat sdf1 = new SimpleDateFormat("HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("hh:mm:ss a");
String dateStr = "21:30:00";
Date date = sdf1.parse(dateStr);
System.out.println(sdf1.format(date));
System.out.println(sdf2.format(date));
在以上示例代码中,我们分别定义了两个SimpleDateFormat
对象,分别用于表示24小时制和12小时制。然后,对字符串“21:30:00”进行parse()
操作,并通过sdf1
对象和sdf2
对象进行格式化。我们会发现,输出的结果如下所示:
21:30:00
09:30:00 PM
结果表明,使用hh进行格式化后,会在输出结果中添加AM或PM,表示上午或下午。而使用HH则不会添加。
解决Java中日期格式化的坑点的方法
要解决Java中日期格式化的坑点,我们可以采取以下策略:
- 使用yyyy而不是YYYY
在格式化字符串中,应该使用yyyy而不是YYYY,以避免出现上述问题。
- 使用HH而不是hh
在格式化字符串中,应该使用HH而不是hh,以避免出现上述问题。
- 使用ThreadLocal来保证线程安全
由于SimpleDateFormat
类是线程不安全的类,建议使用ThreadLocal
来保证线程安全性。
以下是一个示例代码:
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(DATE_FORMAT));
public static String format(Date date) {
SimpleDateFormat sdf = dateFormatThreadLocal.get();
return sdf.format(date);
}
以上代码中,我们定义了一个dateFormatThreadLocal
对象,用于存储SimpleDateFormat
对象。在调用时,通过get()方法获取预设的SimpleDateFormat
对象,并在使用完毕后,通过remove()方法删除。
两个示例来说明日期格式化的坑点
示例1
在实际开发中,我们可能会遇到对日期进行加减操作的场景。下面是一个示例代码:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String dateStr = "2020/01/31";
Date date = sdf.parse(dateStr);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MONTH, 1);
date = calendar.getTime();
System.out.println(sdf.format(date));
以上代码中,我们首先定义了一个SimpleDateFormat
对象sdf
,并对字符串“2020/01/31”进行parse()
操作,得到一个Date类型的日期值。然后,我们使用Calendar.getInstance()
获取一个Calendar对象,并调用setTime()
方法将其设置为刚才解析得到的日期。接着,我们对日期进行了加1个月的操作,并通过getTime()
方法获取修改后的Date值。最后,我们使用format()
方法将其格式化输出。
但是,如果我们对2020年1月31日进行加1个月的操作,期望得到的结果应该是2020年2月29日(因为2020年2月28日后一天就是2020年2月29日)。然而,如果我们运行以上代码,得到的结果是“2020/03/02”。
出现上述问题的原因在于,当我们对日期进行加减操作时,Java会自动调整日期。例如,在2020年1月31日加1个月,Java会先将日期调整为2月28日,然后再加1个月得到3月28日,最后将月份+1,得到3月份。因此,我们得到的结果是“2020/03/02”。
为了避免以上问题,我们应该在进行加减操作时,先将日期调整到月份的最后一天,然后才进行加减操作。例如:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String dateStr = "2020/01/31";
Date date = sdf.parse(dateStr);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
calendar.add(Calendar.MONTH, 1);
date = calendar.getTime();
System.out.println(sdf.format(date));
以上代码中,我们使用getActualMaximum(Calendar.DAY_OF_MONTH)
方法获取当前月份的最大天数,并调用set(Calendar.DAY_OF_MONTH, ...)
方法将日期调整到最后一天。然后,我们再进行加1个月的操作,得到一个正确的结果:“2020/02/29”。
示例2
在项目开发中,我们可能需要对数据进行导出,并将日期格式化为字符串类型。下面是一个示例代码:
List<Map<String, Object>> dataList = new ArrayList<>();
Map<String, Object> data1 = new HashMap<>();
data1.put("name", "张三");
data1.put("age", 25);
data1.put("date", new Date());
dataList.add(data1);
Map<String, Object> data2 = new HashMap<>();
data2.put("name", "李四");
data2.put("age", 30);
data2.put("date", new Date());
dataList.add(data2);
String[] headers = {"姓名", "年龄", "日期"};
String[] fields = {"name", "age", "date"};
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("sheet1");
XSSFRow headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
XSSFCell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
int rowIndex = 1;
for (Map<String, Object> data : dataList) {
XSSFRow dataRow = sheet.createRow(rowIndex++);
for (int i = 0; i < fields.length; i++) {
XSSFCell cell = dataRow.createCell(i);
Object value = data.get(fields[i]);
if (value == null) {
continue;
}
if (value instanceof Date) {
cell.setCellValue(sdf.format((Date) value));
} else {
cell.setCellValue(value.toString());
}
}
}
File file = new File("test.xlsx");
try (FileOutputStream out = new FileOutputStream(file)) {
workbook.write(out);
} catch (IOException e) {
e.printStackTrace();
}
以上代码中,我们定义了一个dataList
列表,其中包含了两个Map类型的数据,分别表示两个人的信息。每个Map中都包含了一个Date类型的日期值。然后,我们定义了一个XSSFWorkbook对象,用于创建一个Excel表格,将数据输出到Excel中。在输出时,我们需要对日期进行格式化,并将格式化后的字符串输出到Excel的单元格中。
但是,如果我们运行以上代码,会发现Excel中单元格的值并不是我们预想的日期格式化值,而是一个乱码字符串。
问题出现在于,Excel对于日期的默认格式化方式与Java不同。如果我们将日期格式化成了字符串,Excel就不能识别并正确展示。因此,我们需要将日期转换成Excel能够识别的格式。
以下是一个修复后的示例代码:
List<Map<String, Object>> dataList = new ArrayList<>();
Map<String, Object> data1 = new HashMap<>();
data1.put("name", "张三");
data1.put("age", 25);
data1.put("date", new Date());
dataList.add(data1);
Map<String, Object> data2 = new HashMap<>();
data2.put("name", "李四");
data2.put("age", 30);
data2.put("date", new Date());
dataList.add(data2);
String[] headers = {"姓名", "年龄", "日期"};
String[] fields = {"name", "age", "date"};
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("sheet1");
XSSFRow headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
XSSFCell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
CellStyle dateCellStyle = workbook.createCellStyle();
DataFormat dataFormat = workbook.createDataFormat();
dateCellStyle.setDataFormat(dataFormat.getFormat("yyyy/MM/dd"));
int rowIndex = 1;
for (Map<String, Object> data : dataList) {
XSSFRow dataRow = sheet.createRow(rowIndex++);
for (int i = 0; i < fields.length; i++) {
XSSFCell cell = dataRow.createCell(i);
Object value = data.get(fields[i]);
if (value == null) {
continue;
}
if (value instanceof Date) {
cell.setCellValue((Date)value);
cell.setCellStyle(dateCellStyle);
} else {
cell.setCellValue(value.toString());
}
}
}
File file = new File("test.xlsx");
try (FileOutputStream out = new FileOutputStream(file)) {
workbook.write(out);
} catch (IOException e) {
e.printStackTrace();
}
以上代码中,我们创建了一个CellStyle对象,并通过setDataFormat()
方法,为它设置了一个Excel能够识别的日期格式。然后,在将日期值写入Excel单元格时,我们使用setCellValue()
方法将日期值直接写入单元格,并将dateCellStyle
设置给该单元格。这样,在输出Excel时,Excel就可以正确识别和展示日期值了。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java中日期格式化的大坑 - Python技术站