Java-小技巧-004-jdk时间,jdk8时间,joda,calendar,获取当前时间前一周、前一月、前一年的时间
一、常用的Date【不推荐】
1.1、基础用法
示例
@Test
public void testDate() throws Exception {
Date date=new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr="2019-07-23 08:17:01";
//Date→ String
String format = sdf.format(date);
System.out.println(format);
//String → Date
Date date1 = sdf.parse(dateStr);
System.out.println(date1);
// 2019-07-23 08:25:03
// Tue Jul 23 08:17:01 CST 2019
}
1.2、线程不安全示例
@Test
public void testParse() {
ExecutorService executorService = Executors.newCachedThreadPool();
List<String> dateStrList = Arrays.asList(
"2018-04-01 10:00:01",
"2018-04-02 11:00:02",
"2018-04-03 12:00:03",
"2018-04-04 13:00:04",
"2018-04-05 14:00:05"
);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
for (String str : dateStrList) {
executorService.execute(() -> {
try {
simpleDateFormat.parse(str);
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
输出
java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2089)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
线程不安全说明:
SimpleDateFormat继承了DateFormat,公用了一个变量
public abstract class DateFormat extends Format { protected Calendar calendar;
在parse方法的最后,会调用CalendarBuilder的establish方法,入参就是SimpleDateFormat维护的Calendar实例,在establish方法中会调用calendar的clear方法,如下:
parsedDate = calb.establish(calendar).getTime();
Calendar establish(Calendar cal) {
boolean weekDate = isSet(WEEK_YEAR)
&& field[WEEK_YEAR] > field[YEAR];
if (weekDate && !cal.isWeekDateSupported()) {
// Use YEAR instead
if (!isSet(YEAR)) {
set(YEAR, field[MAX_FIELD + WEEK_YEAR]);
}
weekDate = false;
} cal.clear();
可知SimpleDateFormat维护的用于format和parse方法计算日期-时间的calendar被清空了,如果此时线程A将calendar清空且没有设置新值,线程B也进入parse方法用到了SimpleDateFormat对象中的calendar对象,此时就会产生线程安全问题!
1.3、解决线程安全的方案
1.3.1、加synchronized锁
以synchronized同步SimpleDateFormat对象。
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static synchronized Date parse(String s) throws ParseException {
return sdf.parse(s);
}
串行执行,在大批量调用效率很低,不推荐
高并发时,使用该对象会出现阻塞,当前使用者使用时,其他使用者等待,尽管结果是对的,但是并发成了排队,实际上并没有解决问题,还会对性能以及效率造成影响。
1.3.2、每次调用创建一个对象
public Date parse(String s) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.parse(s);
}
缺点,耗内存,GC压力增大,每次创建对象销毁,分配和销毁内存空间,整理内存,GC。不推荐
SimpleDateFormat中使用了Calendar对象,由于该对象相当重,在高并发的情况下会大量的new SimpleDateFormat以及销毁SimpleDateFormat,极其耗费资源。
1.3.3、每个线程创建一个
static ThreadLocal<SimpleDateFormat> localSdf= new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
推荐使用ThreadLocal,每个线程一个对象,提高format对象利用率
注意:使用ThreadLocal的Thread均持有ThreadLocal自定义的map对象,key是ThreadLocal对象的弱引用,随ThreadLocal的GC或者内存不足就会GC,value是我们创建的对象是强引用,可能一直存在。因此ThreadLocal调用结束后,需要显示调用remove()方法
使用ThreadLocal时,如果执行原子任务的过程是每一个线程执行一个任务,那么这样的声明基本和每次使用前创建实例对象是没区别的;如果使用的是多线程加任务队列,举个例子,tomcat有m个处理线程,外部有n个待处理任务请求,那么当执行n个任务时,其实只会创建m个SimpleDateFormat实例,对于单一的处理线程,执行任务是有序的,所以对于当前线程而言,不存在并发。
二、java8 localdate【推荐】
LocalDateTime,Date,String 互转
@Test
// 01. java.util.Date --> java.time.LocalDateTime
public void UDateToLocalDateTime() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
} @Test
// 02. java.util.Date --> java.time.LocalDate
public void UDateToLocalDate() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
LocalDate localDate = localDateTime.toLocalDate();
}
@Test
// 03. java.util.Date --> java.time.LocalTime
public void UDateToLocalTime() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
LocalTime localTime = localDateTime.toLocalTime();
} @Test
// 04. java.time.LocalDateTime --> java.util.Date
public void LocalDateTimeToUdate() {
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
} @Test
// 05. java.time.LocalDate --> java.util.Date
public void LocalDateToUdate() {
LocalDate localDate = LocalDate.now();
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
} @Test
// 06. java.time.LocalTime --> java.util.Date
public void LocalTimeToUdate() {
LocalTime localTime = LocalTime.now();
LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
} @Test
// 07. java.time.LocalDateTime --> String
public void localDateTimeToString() {
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = localDateTime.format(formatter);
System.out.println(format);
} @Test
// 08. String - - > java.time.LocalDateTime
public void stringTolocalDateTime() {
String dateStr="2019-07-23 09:49:19";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
System.out.println(dateTime);
}
时间加减
LocalDateTime localDateTime3 = LocalDateTime.now();
LocalDate.now();
LocalTime.now();
localDateTime3.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime localDateTime4 = localDateTime3.minus(23,ChronoUnit.MONTHS);
localDateTime4.atZone(ZoneId.systemDefault());
localDateTime4 = localDateTime4.withHour(3);
localDateTime4 = localDateTime4.withYear(2016);
localDateTime4 = localDateTime4.with(ChronoField.MONTH_OF_YEAR,3);
间隔计算
使用Duration进行 day,hour,minute,second等的计算
使用Period进行Year,Month的计算
Duration duration = Duration.between(localDateTime,localDateTime4);
duration.toDays();
duration.toHours();
duration.toMinutes();
Period period2 = Period.between(localDateTime.toLocalDate(),localDateTime4.toLocalDate());
period2.getYears();
period2.getMonths();
period2.toTotalMonths();
更多介绍、推荐使用java8 localdate等 线程安全 支持较好 地址
三、joda
3.1、问题背景
3.1.1、使用jdk1.8 推荐使用二中方式
3.1.2、在没有jdk1.8的time类库时
1》使用SampleDateFormat存在问题
2》Apache的 DateFormatUtils 与 FastDateFormat
使用org.apache.commons.lang.time.FastDateFormat 与 org.apache.commons.lang.time.DateFormatUtils。
存在的问题:
apache保证是线程安全的,并且更高效。但是DateFormatUtils与FastDateFormat这两个类中只有format()方法,所有的format方法只接受long,Date,Calendar类型的输入,转换成时间串,目前不存在parse()方法,可由时间字符串转换为时间对象。
3.2、Joda-Time
使用Joda-Time类库。
存在的问题:暂无
1、使用maven包
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
2、使用
Joda-Time — 面向 Java 应用程序的日期/时间库的替代选择,Joda-Time 令时间和日期值变得易于管理、操作和理解。事实上,易于使用是 Joda 的主要设计目标。其他目标包括可扩展性、完整的特性集以及对多种日历系统的支持。并且 Joda 与 JDK 是百分之百可互操作的,因此您无需替换所有 Java 代码,只需要替换执行日期/时间计算的那部分代码。
1.创建一个用时间表示的某个随意的时刻 — 比如,2015年12月21日0时0分
DateTime dt = new DateTime(2015, 12, 21, 0, 0, 0, 333);// 年,月,日,时,分,秒,毫秒
2.格式化时间输出
DateTime dateTime = new DateTime(2015, 12, 21, 0, 0, 0, 333);
System.out.println(dateTime.toString("yyyy/MM/dd HH:mm:ss EE"));
3.解析文本格式时间
DateTimeFormatter format = DateTimeFormat .forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dateTime = DateTime.parse("2015-12-21 23:22:45", format);
System.out.println(dateTime.toString("yyyy/MM/dd HH:mm:ss EE"));
4.在某个日期上加上90天并输出结果
DateTime dateTime = new DateTime(2016, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");
注意:plus后原值没变,返回一个新的Datetime
5.到新年还有多少天
public Days daysToNewYear(LocalDate fromDate) {
LocalDate newYear = fromDate.plusYears(1).withDayOfYear(1);
return Days.daysBetween(fromDate, newYear);
}
6.与JDK日期对象的转换
DateTime dt = new DateTime(); //转换成java.util.Date对象
Date d1 = new Date(dt.getMillis());
Date d2 = dt.toDate();
7.时区
//默认设置为日本时间
DateTimeZone.setDefault(DateTimeZone.forID("Asia/Tokyo"));
DateTime dt1 = new DateTime();
System.out.println(dt1.toString("yyyy-MM-dd HH:mm:ss")); //伦敦时间
DateTime dt2 = new DateTime(DateTimeZone.forID("Europe/London"));
System.out.println(dt2.toString("yyyy-MM-dd HH:mm:ss"));
8.计算间隔和区间
DateTime begin = new DateTime("2015-02-01");
DateTime end = new DateTime("2016-05-01"); //计算区间毫秒数
Duration d = new Duration(begin, end);
long millis = d.getMillis(); //计算区间天数
Period p = new Period(begin, end, PeriodType.days());
int days = p.getDays(); //计算特定日期是否在该区间内
Interval interval = new Interval(begin, end);
boolean contained = interval.contains(new DateTime("2015-03-01"));
9.日期比较
DateTime d1 = new DateTime("2015-10-01");
DateTime d2 = new DateTime("2016-02-01"); //和系统时间比
boolean b1 = d1.isAfterNow();
boolean b2 = d1.isBeforeNow();
boolean b3 = d1.isEqualNow(); //和其他日期比
boolean f1 = d1.isAfter(d2);
boolean f2 = d1.isBefore(d2);
boolean f3 = d1.isEqual(d2);
资料:
Joda-Time 简介(中文)https://www.ibm.com/developerworks/cn/java/j-jodatime.html
Joda-Time 文档(英文)http://joda-time.sourceforge.net/
3、传统方式
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar c = Calendar.getInstance(); //过去七天
c.setTime(new Date());
c.add(Calendar.DATE, - 7);
Date d = c.getTime();
String day = format.format(d);
System.out.println("过去七天:"+day); //过去一月
c.setTime(new Date());
c.add(Calendar.MONTH, -1);
Date m = c.getTime();
String mon = format.format(m);
System.out.println("过去一个月:"+mon); //过去三个月
c.setTime(new Date());
c.add(Calendar.MONTH, -3);
Date m3 = c.getTime();
String mon3 = format.format(m3);
System.out.println("过去三个月:"+mon3); //过去一年
c.setTime(new Date());
c.add(Calendar.YEAR, -1);
Date y = c.getTime();
String year = format.format(y);
System.out.println("过去一年:"+year);
Java-小技巧-004-jdk时间,jdk8时间,joda,calendar,获取当前时间前一周、前一月、前一年的时间的更多相关文章
- Java 小技巧和在Java避免NullPonintException的最佳方法(翻译)
前几天就g+里面看到有人引用这篇博文.看了一下.受益颇多. 所以翻译过来,希望和大家一起学习.本人英语水平有限,假设有错,请大家指正. 原文地址(须要翻墙):http://ja ...
- mysql中获取一天、一周、一月时间数据的各种sql语句写法
今天抽时间整理了一篇mysql中与天.周.月有关的时间数据的sql语句的各种写法,部分是收集资料,全部手工整理,自己学习的同时,分享给大家,并首先默认创建一个表.插入2条数据,便于部分数据的测试,其中 ...
- Java小技巧输出26个英文字母
相信有的童鞋写到过与字母有关的小东西,是否有写过全部的字母呢?26个这么多字母,一个个打会疯掉.所有咱们可以用一个小技巧使用for循环帮我们把26个字母自动搞出来,大家来瞅一眼把! 使用Java遍历2 ...
- java小技巧
String 转 Date String classCode = RequestHandler.getString(request, "classCode"); SimpleDat ...
- 【Java】NO.120.JDK.1.JDK8.1.001-【Java8实战】
Style:Mac Series:Java Since:2018-09-26 End:2018-09-26 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- java小技巧-生成重复的字符
今天碰到个需求,根据字段个数,动态生成sql的占位符,如下: public static void main(String[] args) { System.out.println(String.jo ...
- java小技巧:如何分批次导入大量数据
//List 需要导入的数据int count = 1000;//每批次导入的数目int Lastindex = count;List<List<T>> shareList = ...
- Java小技巧:怎么循环日期?
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");try{//起始日期Date start = sdf.parse ...
- Date小技巧:set相关操作及应用_获取当前月(季度/年)的最后一天
set操作还是有不少的,具体见 http://www.w3school.com.cn/jsref/jsref_obj_date.asp, 今天我就只说 setFullYear, setMonth, s ...
- php获取一年中某一周的开始和结束时间
PHP来获取一年中的每星期的开始日期和结束日期的代码 函数get_week()通过传入参数$year年份,获取当年第一天和最后一天所在的周数,计算第一周的日期,通过循环获取每一周的第一天和最后一天的日 ...
随机推荐
- PLSQL 配置连接ORACLE数据库
http://blog.csdn.net/leather0906/article/details/6456095 PLSQL配置登录用户信息 http://www.2cto.com/database/ ...
- C语言中对输入输出格式的控制
格式化输出的控制 #include<stdio.h> int main(void){ float a=111123.681111f; printf("%1.3f",a) ...
- 将php和mysql命令加入到环境变量中
方法一:直接运行命令export PATH=$PATH:/usr/local/webserver/php/bin 和 export PATH=$PATH:/usr/local/webserver/my ...
- 如何从CentOS官网下载我们想要的版本
今天想从官网下载6.5版本的CentOS,结果找了好一会儿才找到,赶紧记录下来,以备以后查询. 第一步在百度搜索centos,点击"Download CentOS",如下图所示. ...
- ubuntu14.04中安装jdk
1. 下载JDK http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 将下载的 .g ...
- Mac 安装Jupyter notebook
python:mac下自带Python 2.7.10 1.先升级了pip安装工具:sudo python -m pip install --upgrade --force pip 2.安装setupt ...
- 隐马尔可夫树(HMT模型)
HMT(Hidden Markov Tree)隐马尔可夫树 [论文] 小波变换与HMT模型的图像插值算法-郭昌-中山大学学报(自然科学版)
- 华为 进入和退出Fastboot、eRecovery和Recovery升级模式
手机关机状态下,可以进入Fastboot/eRecovery / Recovery/升级这几种模式: 需要连接电脑:Fastboot模式:长按音量下键+电源键.eRecovery 模式:长按音量上键+ ...
- Myeclipse下使用Maven搭建spring boot项目
开发环境:Myeclipse2017.JDK1.6.Tomcat 8.0.Myeclipse下使用Maven搭建spring boot项目,详细过程如下: 1. New -> Project.. ...
- eclipse中的SVN文件还原到历史版本
转载自:http://www.softown.cn/post/103.html 由于某些特殊原因,我们可能需要将SVN资源库中的某个文件回滚到以前的某个历史版本(准确地说,这不是"回滚&qu ...