java中日期格式化的大坑

关于“java中日期格式化的大坑”,我会从以下几个方面进行讲解:

  1. Java中日期格式化的基本知识
  2. Java中日期格式化的坑点
  3. 解决Java中日期格式化的坑点的方法
  4. 两个示例来说明日期格式化的坑点

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中日期格式化的坑点,我们可以采取以下策略:

  1. 使用yyyy而不是YYYY

在格式化字符串中,应该使用yyyy而不是YYYY,以避免出现上述问题。

  1. 使用HH而不是hh

在格式化字符串中,应该使用HH而不是hh,以避免出现上述问题。

  1. 使用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技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Java GUI实现学生成绩管理系统

    作为Java GUI实现学生成绩管理系统的作者,以下是详细的攻略: 1. 学习Java GUI 首先需要熟悉Java GUI相关的知识,包括Swing和AWT等基础知识。可以通过在线课程、教程和书籍等途径来学习。 2. 设计学生成绩管理系统 在学习了Java GUI基础知识之后,开始设计学生成绩管理系统。首先需要确定系统的功能和界面设计,包括成绩录入、成绩查…

    Java 2023年5月24日
    00
  • Java Apache POI报错“POIXMLException”的原因与解决办法

    “POIXMLException”是Java的Apache POI类库中的一个异常,通常由以下原因之一引起: 文件格式错误:如果文件格式不正确,则可能会出现此异常。例如,可能会尝试读取非Excel文件或尝试读取损坏的Excel文件。 以下是两个实例: 例1 如果文件格式错误,则可以尝试使用正确的文件格式以解决此问题。例如,在Java中,可以使用以下代码: F…

    Java 2023年5月5日
    00
  • JAVA 十六进制与字符串的转换

    Java 中可以通过多种方式实现十六进制和字符串之间的转化。本文将介绍两种主要的方法:使用内置类库和字节数组转换。 使用内置类库实现 Java 内置的 Integer、Long 和 Short 等类库提供了十六进制和字符串之间的转化方法。下面是一个示例: // 十六进制转字符串 int hexVal = 0x1F; String hexStr = Integ…

    Java 2023年5月27日
    00
  • spring boot配置MySQL数据库连接、Hikari连接池和Mybatis的简单配置方法

    下面是详细的攻略: 1. 准备工作 在开始配置之前,我们需要先确保以下的几个准备工作已经完成: 安装好 JDK 和 MySQL 数据库,并且配置好相关环境变量。 在本地搭建好 Spring Boot 项目。 引入以下相关依赖到项目的 pom.xml 文件中: <dependency> <groupId>org.springframew…

    Java 2023年5月20日
    00
  • 使用Java打印数字组成的魔方阵及字符组成的钻石图形

    下面我详细讲解一下“使用Java打印数字组成的魔方阵及字符组成的钻石图形”的完整攻略。 打印数字组成的魔方阵 思路 魔方阵是由 $n^2$ 个数字组成的方阵,其中每一行、每一列、每一条对角线上的数字之和都相等。我们可以使用以下的算法来生成 $n \times n$ 的魔方阵: 将数字 1 放在第一行的中间列。 依次将后续的数字放入前一个数字的右上角(如果已经…

    Java 2023年5月26日
    00
  • Scala方法与函数使用和定义详解

    Scala方法与函数使用和定义详解 简介 在Scala编程语言中,方法和函数都是非常重要的概念,也是广泛使用的编程语言要素。尽管两者看似非常相似,但是它们在写法和用法上还是存在一定的差异。本篇文章将详细讲解Scala中方法与函数的定义和使用。 方法 在Scala中,方法是指带有名称和参数的代码块,方法可以通过类或对象进行调用。Scala中的方法可以有返回值,…

    Java 2023年5月26日
    00
  • JEE与Spring Boot代码性能比较分析

    让我详细介绍一下“JEE与Spring Boot代码性能比较分析”的攻略。 1. 研究背景 在开始比较JEE与Spring Boot的性能之前,首先要了解它们的基本概念和特性。JEE是Java Platform, Enterprise Edition的缩写,是面向企业级应用的Java平台,支持各种服务、组件和协议,适用于大型分布式应用的开发。而Spring …

    Java 2023年5月19日
    00
  • Java中的base64编码器

    下面是关于Java中的base64编码器的完整攻略。 简介 Base64编码是一种将二进制数据用文本形式表示的编码方式,常用于在传输过程中处理二进制数据或将二进制数据存储在文本文件中。在Java中,提供了Base64编码器和解码器,可以通过Java API方便地实现Base64编解码的功能。 使用Base64编码器 Java中提供了两种方式来实现Base64…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部