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就可以正确识别和展示日期值了。

阅读剩余 82%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java中日期格式化的大坑 - Python技术站

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

相关文章

  • Java 中利用泛型和反射机制抽象DAO的实例

    抽象DAO(Data Access Object)是一种数据访问设计模式,它可以对不同的数据源(比如数据库、文件系统等)进行统一的抽象和封装,提高代码的复用性和可维护性。Java 中利用泛型和反射机制可以更进一步的抽象化DAO,并实现更为灵活的数据访问。 本攻略将介绍如何利用泛型和反射机制来实现一个通用的抽象DAO。 一、定义抽象DAO 首先需要定义一个抽象…

    Java 2023年5月20日
    00
  • Java 获取指定日期的实现方法总结

    Java 获取指定日期的实现方法总结 本文总结了在Java中获取指定日期的几种常见方法。 1. 通过日期字符串生成日期对象 在Java中,我们可以通过日期字符串生成日期对象,然后可以进行各种操作。下面是一个示例代码: import java.text.SimpleDateFormat; import java.util.Date; public class …

    Java 2023年5月20日
    00
  • Spring Boot 中该如何防御计时攻击

    计时攻击是一种通过测量响应时间来推断出某些操作是否成功的方式。在Web应用程序中,计时攻击可以被用于探测密码的正确性、窃取加密令牌的密钥或破解加密算法等。 Spring Boot应用程序中要防御计时攻击,可以采取以下措施: 引入 Thymeleaf应用模板引擎,并且使用它提供的 th:if 和 th:unless 指令来控制用户输入的数据。示例代码如下: &…

    Java 2023年5月19日
    00
  • Java实时监控日志文件并输出的方法详解

    Java实时监控日志文件并输出的方法,可以使用Java IO和多线程的知识来完成。主要流程可以分为以下几步: 创建一个文件读取器,用于读取日志文件的内容。 定义一个线程类,用于不断读取文件内容,并输出到控制台或其他存储介质中。 开启线程,开始实时监控日志文件。 具体实现步骤如下: 1、创建一个文件读取器 使用Java IO中的FileReader和Buffe…

    Java 2023年5月26日
    00
  • java中对象调用成员变量与成员实例方法

    Java 中,对象调用成员变量和成员实例方法的过程是通过对象的引用来实现的。下面是完整的攻略: 对象调用成员变量 首先需要创建一个对象的实例,即对象的地址,然后通过对象的引用来调用成员变量。Java 中的成员变量可以分为类变量和实例变量。对于类变量,直接使用类名来调用即可。对于实例变量,则必须使用对象的引用来调用。 调用类变量 调用类变量可以直接使用类名,例…

    Java 2023年5月26日
    00
  • SpringBoot框架集成token实现登录校验功能

    下面是详细讲解SpringBoot框架集成token实现登录校验功能的完整攻略。 一、什么是Token 在Web开发中,服务端不能直接拿到客户端的登录状态,而客户端又需要传递一些数据,这时就需要一种机制来帮助服务端识别客户端的身份,这种机制就是Token。 Token是一种令牌,本质上就是一个字符串,客户端在登录时通过身份验证后,服务端会返回给客户端一个To…

    Java 2023年5月19日
    00
  • Java接口返回json如何忽略特定属性

    以下是Java接口返回json忽略特定属性的攻略。 第一步:引入Jackson依赖 Jackson是Java处理JSON格式数据的一个常用库,可以直接使用Jackson提供的注解来忽略特定属性。 首先,在项目的pom.xml文件中添加Jackson的依赖。如果你使用Maven,可以添加以下依赖: <dependency> <groupId&…

    Java 2023年5月26日
    00
  • 【Jmeter】按比例分配Api压测

    先看 【Jmeter】基础介绍-详细 【Jmeter】Request1输出作为Request2输入-后置处理器 继续聊提出的第二个问题,即   2.需要按比例分配API请求并发,以模拟真实的API压力场景 做压测的时候,一般的需求都是多个API同时压,不然也看不出真正的tps是多少啊。 比如虽然接口a的需求并发不高,500个用户才请求一次,但是特别耗性能,导…

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