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日

相关文章

  • 使用weixin-java-miniapp配置进行单个小程序的配置详解

    使用weixin-java-miniapp配置进行单个小程序的配置,需要遵循下面的步骤: 1. 引入依赖 在pom.xml文件中引入以下依赖: <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-miniap…

    Java 2023年5月23日
    00
  • IDEA-Maven环境配置及使用教程

    关于「IDEA-Maven环境配置及使用教程」,我可以提供以下攻略和演示示例。 Maven的安装 在开始前,我们需要先安装Maven。Maven是一个Java应用程序构建管理工具,所以确保你已经在运行Maven的操作系统上安装了Java。 首先,下载Maven并解压。 然后,将Maven的bin目录添加到系统环境变量PATH中。此时在终端命令行中输入mvn …

    Java 2023年5月20日
    00
  • Spring AOP结合注解实现接口层操作日志记录

    注解可以方便快捷地给方法或类加上标记,用于识别它们的作用。在Spring AOP中,我们可以通过在方法上使用注解来实现操作日志记录。本文将介绍如何使用Spring AOP结合注解实现接口层操作日志记录。 添加依赖 在pom.xml文件中添加以下依赖: <dependency> <groupId>org.springframework.…

    Java 2023年6月15日
    00
  • 透明化Sharding-JDBC数据库字段加解密方案

    为了实现对敏感信息的保护,常常需要对数据库中的字段加密。Sharding-JDBC提供了一个透明的加解密解决方案,可以在不修改Java代码的情况下加解密字段数据。以下为透明加解密方案的攻略: 步骤一:添加加解密配置 在sharding-jdbc配置文件中添加加解密配置项,如下所示: encryptors: aes: type: aes # 加密算法,支持AE…

    Java 2023年6月16日
    00
  • Android监听事件

    监听事件 ​ 监听事件机制由事件源,事件和事件监听器三类对象组成,事件源一般就是activity中的UI控件。 下面引用别人整理的图来更加形象的表达这些关系。 ​ 事件监听机制的意义就是让事件源的行为委托给事件监听器,让监听器控制事件的发生。 ​ 1.实现监听事件的方法 通过内部类实现 通过匿名内部类实现(大部分都是这样用) 通过事件源所在类实现 也可以直接…

    Java 2023年4月27日
    00
  • SpringMVC 中HttpMessageConverter简介和Http请求415 的问题

    SpringMVC 中HttpMessageConverter简介和Http请求415 的问题 在SpringMVC中,HttpMessageConverter是用于将请求和响应的HTTP消息转换为Java对象的组件。本文将详细介绍HttpMessageConverter的作用和使用方法,并解决Http请求415的问题。 HttpMessageConvert…

    Java 2023年5月17日
    00
  • C#结合数据库实现验证识别ID卡内容的方法

    C#结合数据库实现验证识别ID卡内容的方法 实现一种基于C#语言和数据库的ID卡内容验证识别方法,能够方便地对ID卡进行读取、保存、查询、验证等操作,可以提高办公自动化、信息管理和安全性水平。下面将分别介绍实现步骤、示例代码和注意事项。 实现步骤 建立数据库 使用Microsoft SQL Sever等数据库软件,创建一个名为IDCardInfo的数据库。新…

    Java 2023年5月19日
    00
  • Springmvc 4.x利用@ResponseBody返回Json数据的方法

    以下是关于“SpringMVC 4.x利用@ResponseBody返回JSON数据的方法”的完整攻略,其中包含两个示例。 SpringMVC 4.x利用@ResponseBody返回JSON数据的方法 在SpringMVC 4.x中,我们可以使用@ResponseBody注解将Java对象转换为JSON格式的数据,并将其返回给客户端。本文将介绍两个示例,包…

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