填坑:Java 中的日期转换
我们之前讨论过时间,在Java 中有一些方法会出现横线?比如Date 过期方法。
参考文章:知识点:java一些方法会有横线?以Date 过期方法为例
Java中的日期和时间处理方法
- Date类(官方不再推荐使用,官方解释Date类不利于国际化,推荐使用Calendar类)
- Calendar类
- DateFormat类 使用此类来时间初始化
我们发现,时间toLocalString 会有横线:
vo.setSubmitDate(new Date().toLocaleString());
可以改为:
vo.setSubmitDate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance(TimeZone.getTimeZone("GMT+08:00")).getTime()));
在最近的项目中,用SimpleDateFormat出现一些奇奇怪怪的BUG:
- 1.结果值不对:转换的结果值经常和预期不同。
- 2.内存泄漏: 由于转换的结果值不对,后续的一些操作会导致系统内存泄漏,频繁触发GC(垃圾回收),造成系统不可用。
1 什么是SimpleDateFormat
在java doc对SimpleDateFormat的解释如下:
SimpleDateFormatis a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows for formatting(date → text), parsing (text → date), and normalization.
SimpleDateFormat是一个用来对位置敏感的格式化和解析日期的实体类。他允许把日期格式化成text,把text解析成日期和规范化。
1.1 使用SimpleDateFormat
simpleDateFormat的使用方法比较简单:
public static void main(String[] args) throws Exception {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
System.out.println(simpleDateFormat.format(new Date()));
System.out.println(simpleDateFormat.parse("2018-10-24 12:10:24"));
}
- 1.定义一个日期"yyyy-mm-dd HH:mm:ss"的pattern, 也就是我们这个simpleDateFormat不管是格式化还是解析都需要按照这个pattern。
- 2.对于format需要传递Date的对象,会返回一个String类型,这个String会按照我们上面的格式生成。
- 3.对于parse需要传递一个按照上面pattern的字符串,如果传递错误的pattern会抛出java.text.ParseException异常,如果传递正确的会生成一个Date对象。
附:格式占位符 G 年代标志符 y 年 M 月 d 日 h 时 在上午或下午 (1~12) H 时 在一天中 (0~23) m 分 s 秒 S 毫秒 E 星期 D 一年中的第几天 F 一月中第几个星期几 w 一年中第几个星期 W 一月中第几个星期 a 上午 / 下午 标记符 k 时 在一天中 (1~24) K 时 在上午或下午 (0~11) z 时区复制代码
回到我们遇到的坑:
2 为什么SimpleDateFormat会线程不安全呢?
在SimpleDateFormat源码中,所有的格式化和解析都需要通过一个中间对象Calendar进行转换,而这将会出现线程不安全的操作
比如当多个线程操作同一个Calendar的时候后来的线程会覆盖先来线程的数据,那最后其实返回的是后来线程的数据,这样就导致我们上面所述的BUG的产生
为什么会出现这么多问题呢?
因为SimpleDateFormat线程不安全,很多人都会写个Util类,然后把SimpleDateFormat定义成全局的一个常量,所有线程都共享这个常量:
protected static final SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd");
public static Date formatDate(String date) throws ParseException {
returndayFormat.parse(date);
}
3.如何避坑
对于SimpleDateFormat的解决方法有下面几种:
3.1 新建SimpleDateFormat
原因:所有线程都共用一个SimpleDateFormat,
解决办法:每次使用的时候都创建一个新的SimpleDateFormat,我们可以在DateUtils中将创建SimpleDateFormat放在方法内部:
public static Date formatDate(String date) throws ParseException {
SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd");
return dayFormat.parse(date);
}
上面这个方法虽然能解决我们的问题但是引入了另外一个问题就是,如果这个方法使用量比较大,有可能会频繁造成Young gc,整个系统还是会受一定的影响。
3.2 使用ThreadLocal
使用ThreadLocal能避免造成Young gc,我们对每个线程都使用ThreadLocal进行保存
由于ThreadLocal是线程之间隔离开的,所以不会出现线程安全问题:
private static ThreadLocal simpleDateFormatThreadLocal = new ThreadLocal<>();
public static Date formatDate(String date) throws ParseException {
SimpleDateFormat dayFormat = getSimpleDateFormat();
returndayFormat.parse(date);
}
private static SimpleDateFormatgetSimpleDateFormat() {
SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get();
if(simpleDateFormat == null){
simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss") simpleDateFormatThreadLocal.set(simpleDateFormat);
}
returnsimpleDateFormat;
}
3.3使用第三方工具包
虽然上面的ThreadLocal能解决我们出现的问题,但是第三方工具包提供的功能更加强大,在java中有两个类库比较出名一个是Joda-Time,一个是Apache common包
3.3.1 Joda-Time(推荐)
Joda-Time 令时间和日期值变得易于管理、操作和理解。对于我们复杂的操作都可以使用Joda-Time操作,下面我列举两个例子,对于把日期加上90天,如果使用原生的Jdk我们需要这样写:
Calendar calendar = Calendar.getInstance();calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);SimpleDateFormat sdf = new SimpleDateFormat("E MM/dd/yyyy HH:mm:ss.SSS");
calendar.add(Calendar.DAY\_OF\_MONTH, 90);
System.out.println(sdf.format(calendar.getTime()));
但是在我们的joda-time中只需要两句话,并且api也比较通俗易懂,所以你为什么不用Joda-Time呢?
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");
3.3.2 common-lang包
在common-lang包中有个类叫FastDateFormat,由于common-lang这个包基本被很多Java项目都会引用,所以你可以不用专门去引用处理时间包,即可处理时间,在FastDateFormat中每次处理时间的时候会创建一个calendar,使用方法比较简单代码如下所示:
FastDateFormat.getInstance().format(new Date());
3.4升级jdk8(推荐)
在java8中Date这个类中的很多方法包括构造方法都被打上了@Deprecated废弃的注解,取而代之的是LocalDateTime,
LocalDate LocalTime这三个类:
- LocalDate无法包含时间;
- LocalTime无法包含日期;
- LocalDateTime才能同时包含日期和时间。
知识点:Java8 在日期的格式化和解析方面不用考虑线程安全性
代码如下:
public static String formatTime(LocalDateTime time,String pattern) {
returntime.format(DateTimeFormatter.ofPattern(pattern));
}
localDateTime不仅解决了线程安全的问题,同样也提供了一些其他的运算比如加减天数:
//日期加上一个数,根据field不同加不同值,field为ChronoUnit.*
public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
return time.plus(number, field);
}
//日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*
public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field){
return time.minus(number,field);
}
延伸
使用LocalDateTime 会改变现有的代码,我们可以将他们两进行互转:
//Date转换为LocalDateTime
public static LocalDateTime convertDateToLDT(Date date) {
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
//LocalDateTime转换为Date
public static Date convertLDTToDate(LocalDateTime time) {
return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
}
【公众号】:一只阿木木
填坑:Java 中的日期转换的更多相关文章
- java中的日期转换
在java中有两种Date对象,一种是java.sql.Date,另一种是java.util.Date 一.java.sql.Date对象: 这种Date对象使用了进行数据库操作的,它对应了数据库中的 ...
- Java中的日期操作 分类: B1_JAVA 2015-02-16 17:55 6014人阅读 评论(0) 收藏
在日志中常用的记录当前时间及程序运行时长的方法: public void inject(Path urlDir) throws Exception { SimpleDateFormat sdf = n ...
- Delphi与Java中的日期互换
在最近做的一个项目中用到了Java和Delphi,发现它们不能正确读取对方的日期类型,如在Java中写入一个值为“2007-12-1”的日期值,通过Delphi读取却不是这个值了.通过查阅资料,发现两 ...
- Java中的日期操作
在日志中常用的记录当前时间及程序运行时长的方法: public void inject(Path urlDir) throws Exception { SimpleDateFormat sdf = n ...
- Java中的日期和时间
Java中的日期和时间 Java在java.util包中提供了Date类,这个类封装了当前的日期和时间. Date类支持两种构造函数.第一个构造函数初始化对象的当前日期和时间. Date() 下面的构 ...
- java中的时区转换
目录 java中的时区转换 一.时区的说明 二.时间的表示 三.时间戳 四.Date类和时间戳 五.java中的时区转换 java中的时区转换 一.时区的说明 地球表面按经线从东到西,被划成一个个区域 ...
- java中汉字自动转换成拼音
java中汉字自动转换成拼音 1.需要下载jar包 pinyin4j.2.5.0.jar ,加入到WEB-INF下的lib里边,右键add to bulid path. 2.[代码]PinYinUti ...
- 第七节:详细讲解Java中的日期,java.util.date
前言 大家好,给大家带来详细讲解Java中的日期,java.util.date的概述,希望你们喜欢 类Date Java.lang.Object->java.util.Date public c ...
- MYSQL中的日期转换
MYSQL中的日期转换 网址: http://www.eygle.com/digest/2006/09/mysql_date_convert.html 对于每个类型拥有的值范围以及并且指定日期何时间值 ...
随机推荐
- ChIP-seq 核心分析 下游分析
http://icb.med.cornell.edu/wiki/index.php/Elementolab/ChIPseeqer_Tutorial [怪毛匠子 整理] ChIP-seq[核心分析 下游 ...
- hashlib模块(加密模块)
hash = hashlib.md5(b"str") #md5对象,md5不能反解,可加参数 hash.update(b"str") #对字符串进行加密 has ...
- 查询MySQL数据库中表结构的几种方法
什么是表结构?表结构就是定义数据表文件名,确定数据表包含哪些字段,各字段的字段名.字段类型.及宽度,并将这些数据输入到计算机当中. 查询方法:以表‘employees’为例子 1.describe(d ...
- python 将汉字转换为拼音
xpinyin提供把汉字转为汉语拼音的功能. 安装此模块 pip install xpinyin简单用例: from xpinyin import Pinyin pin = Pinyin() test ...
- git教程:撤销修改
转载:撤销修改 自然,你是不会犯错的.不过现在是凌晨两点,你正在赶一份工作报告,你在readme.txt中添加了一行: $ cat readme.txt Git is a distributed ve ...
- MATLAB实现Brovey图像融合
自定义函数: function BF=Brovey_fuse(Hyperspectral_image,High_resolution_image) x0=imread(Hyperspectral_im ...
- [Java Web学习]Spring MVC使用普通类对象,声明的对象为null
由于对Spring还不熟悉,目前还处于学习阶段,因此经常会遇到一些小白问题,这个问题需要在Spring文件中将普通对象注入bean,然后在MVC中添加set方法,填充普通对象.
- Redis事务和实现秒杀功能的实现
今天带着学生学习了Redis的事务功能,Redis的事务与传统的关系型数据库(如MySQL)有所不同,Redis的事务不能回滚. Redis中使用multi.exec.discard.watch.un ...
- 学习笔记DL005:线性相关、生成子空间,范数,特殊类型矩阵、向量
线性相关.生成子空间. 逆矩阵A⁽-1⁾存在,Ax=b 每个向量b恰好存在一个解.方程组,向量b某些值,可能不存在解,或者存在无限多个解.x.y是方程组的解,z=αx+(1-α),α取任意实数. A列 ...
- Python每日练习汇总
练习1 2019-3-19# 写一个函数实现99乘法表 def x99(x): if x >=1 and x <=9: line = 1 while line <= x: start ...