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日

相关文章

  • SpringBoot 使用@WebMvcTest测试MVC Web Controller

    SpringBoot 使用 @WebMvcTest 测试 MVC Web Controller 在 SpringBoot 中,我们可以使用 @WebMvcTest 注解来测试 MVC Web Controller。@WebMvcTest 注解提供了一种简单的方式来测试 Spring MVC 控制器,而不需要启动完整的应用程序上下文。本文将详细讲解 Sprin…

    Java 2023年5月18日
    00
  • Java 控制流程、大数值、数组

    Java 控制流程 Java 控制流程由以下几个部分构成: if…else 语句 switch 语句 for 循环 while 循环 do…while 循环 if…else 语句 if…else 语句是 Java 中最基础的流程控制语句之一,它的语法如下: if (condition) { // 条件成立执行的代码块 } else { // …

    Java 2023年5月23日
    00
  • Gson之toJson和fromJson方法的具体使用

    标题: Gson之toJson和fromJson方法的具体使用攻略 概述:GSON 是 Google 提供的 JSON 库,在 Android 应用开发中是经常被用到的,在实现 JSON 的序列化和反序列化时会用到 toJson() 和 fromJson() 方法。 toJson() 方法是将 Java 对象转换成 JSON 对象,而fromJson() 方…

    Java 2023年5月26日
    00
  • 详解JAVA 反射机制

    详解JAVA 反射机制 什么是反射机制 反射机制是 Java 语言提供的一种能力,它允许本来在编译期无法访问的类的内部信息,在程序运行期可以获取到。使用反射机制,我们可以在程序运行时动态地获取类型信息、构造对象、访问字段和方法等。 反射机制的应用场景 设计灵活,可扩展性好。比如很多插件式的框架,允许用户开发自定义的模块,但是这些模块编译时是不确定的,只有在程…

    Java 2023年5月20日
    00
  • centos7下搭建ZooKeeper3.4中间件常用命令小结

    下面是详细讲解“centos7下搭建ZooKeeper3.4中间件常用命令小结”的完整攻略。 一、ZooKeeper介绍 ZooKeeper是一个分布式协调服务,可以用于分布式应用的协调管理。ZooKeeper提供了高可用性和高性能的数据管理和协调功能,这些功能包括配置管理、命名服务、分布式同步、群组服务等。 二、ZooKeeper安装 以下是在CentOS…

    Java 2023年5月20日
    00
  • springboot封装JsonUtil,CookieUtil工具类代码实例

    下面开始讲解”Spring Boot封装JsonUtil,CookieUtil工具类代码实例”的攻略,主要包含以下几步: 1. 创建工具类文件 在src/main/java目录下创建一个名为utils的包,然后在该包下创建两个工具类文件:JsonUtil和CookieUtil。 1.1 JsonUtil JsonUtil用于将Java对象与Json数据相互转…

    Java 2023年5月26日
    00
  • Java对象和Json文本转换工具类的实现

    Java对象和Json文本转换是我们在开发中经常遇到的问题,为了提高开发效率,我们可以创建一个工具类来实现这个功能。下面是Java对象和Json文本转换工具类的实现完整攻略。 步骤一、添加必要的工具包 在实现Java对象和Json文本转换工具类之前,我们需要添加一些必要的工具包。其中最主要的是json工具包,我们可以选择fastjson,jackson等工具…

    Java 2023年5月26日
    00
  • 详解MybatisPlus集成nacos导致druid连接不上数据库

    我很高兴为您提供“详解MybatisPlus集成nacos导致druid连接不上数据库”的完整攻略。 问题描述MybatisPlus集成nacos后,我们发现druid连接池无法连接数据库了,导致应用程序无法启动。这是由于Druid数据源在生成时需要使用一些配置参数,例如驱动类名、连接字符串、用户名/密码等,而这些参数在nacos配置中心中没有被正确指定。 …

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