Java:简述Java中对时间和日期的操作

一、Date

1. java.util.Date

(1)java.util.Date的构造方法如下:

public Date() {
	this(System.currentTimeMillis());
}

public Date(long date) {
	fastTime = date;
}

(2)java.util.Date的测试用例及输出结果如下:

java.util.Date date_util_1=new java.util.Date();
System.out.println(date_util_1);
输出:Tue Oct 22 17:37:10 CST 2019
		
java.util.Date date_util_2=new java.util.Date(System.currentTimeMillis());
System.out.println(date_util_2);
输出:Tue Oct 22 17:37:10 CST 2019

java.util.Date date_util_3=new java.util.Date(System.currentTimeMillis());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(simpleDateFormat.format(date_util_3));
输出:2019-10-22 17:54:28

(3)总结
Date如果不格式化,打印出的日期可读性较差。使用SimpleDateFormat对时间进行格式化,但SimpleDateFormat是线程不安全的。SimpleDateFormat的format方法的源码如下。

源码中calendar是共享变量,并且这个共享变量没有做线程安全控制。当多个线程同时使用相同的SimpleDateFormat对象【如用static修饰的SimpleDateFormat】调用format方法时,多个线程会同时调用calendar.setTime方法,可能一个线程刚设置好time值另外的一个线程马上把设置的time值给修改了,导致返回的格式化时间可能是错误的。

java.text.DateFormat
protected Calendar calendar;

java.text.SimpleDateFormat
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) {
  // Convert input date to time field list
  calendar.setTime(date);
  
  boolean useDateFormatSymbols = useDateFormatSymbols();

  for (int i = 0; i < compiledPattern.length; ) {
      int tag = compiledPattern[i] >>> 8;
      int count = compiledPattern[i++] & 0xff;
      if (count == 255) {
          count = compiledPattern[i++] << 16;
          count |= compiledPattern[i++];
      }

      switch (tag) {
      case TAG_QUOTE_ASCII_CHAR:
          toAppendTo.append((char)count);
          break;

      case TAG_QUOTE_CHARS:
          toAppendTo.append(compiledPattern, i, count);
          i += count;
          break;

      default:
          subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
          break;
      }
  }
  
  return toAppendTo;
}

在多并发情况下使用SimpleDateFormat需格外注意:SimpleDateFormat除了format是线程不安全以外,parse方法也是线程不安全的。parse方法实际调用alb.establish(calendar).getTime()方法来解析,alb.establish(calendar)方法里主要完成了:
①重置日期对象cal的属性值;②使用calb中中属性设置cal;③返回设置好的cal对象。

但是这三步不是原子操作。多线程并发为了保证线程安全

  • 避免线程之间共享一个SimpleDateFormat对象,每个线程使用时都创建一次SimpleDateFormat对象 => 创建和销毁对象的开销大;
  • 对使用format和parse方法的地方进行加锁 => 线程阻塞性能差;
  • 使用ThreadLocal保证每个线程最多只创建一次SimpleDateFormat对象 => 较好的方法。

Date对时间处理比较麻烦,比如想获取某年、某月、某星期,以及n天以后的时间,如果用Date来处理的话真是太难了,虽然Date类有getYear、getMonth这些方法,获取年月日很Easy,但都被弃用了。

2. java.sql.Date

(1)java.sql.Date的构造方法如下:

public Date(long date) {
	super(date);
}

(2)java.util.Date的测试用例及输出结果如下:

java.sql.Date date_sql=new java.sql.Date(System.currentTimeMillis());
System.out.println(date_sql);
输出:2019-10-22

(3)总结
java.sql.Date是针对SQL语句使用的,它只包含日期而没有时间部分,一般在读写数据库的时候用,PreparedStament的setDate()的参数和ResultSet的getDate()方法的都是java.sql.Date。java.util.Date 是 java.sql.Date 的父类,即:继承关系:java.lang.Object --》 java.util.Date --》 java.sql.Date。

3. java.util.Date 与 java.sql.Date的互相转化

java.sql.Date转为java.util.Date
java.sql.Date date=new java.sql.Date();
java.util.Date d=new java.util.Date (date.getTime());

java.util.Date转为java.sql.Date
java.util.Date utilDate=new Date();
java.sql.Date sqlDate=new java.sql.Date(utilDate.getTime());

java.util.Date utilDate=new Date();
java.sql.Date sqlDate=new java.sql.Date(utilDate.getTime());
java.sql.Time sTime=new java.sql.Time(utilDate.getTime());
java.sql.Timestamp stp=new java.sql.Timestamp(utilDate.getTime());

这里所有时间日期都可以被SimpleDateFormat格式化format()
SimpleDateFormat f=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
f.format(stp);
f.format(sTime);
f.format(sqlDate);
f.format(utilDate)

java.sql.Date sqlDate=java.sql.Date.valueOf(" 2005-12-12");
utilDate=new java.util.Date(sqlDate.getTime());

另类取得年月日的方法:
import java.text.SimpleDateFormat;
import java.util.*;
java.util.Date date = new java.util.Date();

如果希望得到YYYYMMDD的格式SimpleDateFormat
sy1=new SimpleDateFormat("yyyyMMDD");
String dateFormat=sy1.format(date);

如果希望分开得到年,月,日SimpleDateFormat
sy=new SimpleDateFormat("yyyy");
SimpleDateFormat sm=new SimpleDateFormat("MM");
SimpleDateFormat sd=new SimpleDateFormat("dd");
String syear=sy.format(date);
String smon=sm.format(date);
String sday=sd.format(date);

二、java8全新的日期和时间API

1. LocalDate

(1)作用
只会获取年月日星期。

(2)创建LocalDate

//获取当前年月日
LocalDate localDate = LocalDate.now();
System.out.println(localDate.toString());
输出:2019-10-22

//构造指定的年月日
LocalDate localDate1 = LocalDate.of(2019, 10, 22);
System.out.println(localDate1.toString());
输出:2019-10-22

(3)获取年、月、日、星期

int year = localDate.getYear();
int year1 = localDate.get(ChronoField.YEAR);
System.out.println(year + "--" + year1);
输出:2019--2019
		
Month month = localDate.getMonth();
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
System.out.println(month + "--" + month1);
输出:OCTOBER--10
	
int day = localDate.getDayOfMonth();
int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
System.out.println(day + "--" + day1);
输出:22--22
		
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK);
System.out.println(dayOfWeek + "--" + dayOfWeek1);
输出:TUESDAY--2

2. LocalTime

(1)作用
只会获取时分秒毫秒。

(2)创建LocalDate

LocalTime localTime = LocalTime.of(20, 10, 10,10);
System.out.println(localTime.toString());
输出:20:10:10.000000010
	
LocalTime localTime1 = LocalTime.now();
System.out.println(localTime1.toString());
输出:20:20:43.105
  1. 获取时、分、秒
//获取小时
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
System.out.println(hour + "--" + hour1);
输出:20--20

//获取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
System.out.println(minute + "--" + minute1);
输出:10--10

//获取秒
int second = localTime.getSecond();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
System.out.println(second + "--" + second1);
输出:10--10

3. LocalDateTime

(1)作用
获取年月日时分秒,等于LocalDate+LocalTime 。

(2)创建LocalDateTime

LocalDateTime localDateTime = LocalDateTime.now();

LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.OCTOBER, 10, 14, 46, 56);

LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);

LocalDateTime localDateTime3 = localDate.atTime(localTime);
LocalDateTime localDateTime4 = localTime.atDate(localDate);

(3)获取LocalDate、LocalTime

LocalDate localDate = localDateTime.toLocalDate();

LocalTime localTime = localDateTime.toLocalTime();

4. Instant

(1)作用
获取秒数 。如果只是为了获取秒数或者毫秒数,使用System.currentTimeMillis()来得更为方便。

(2)创建创建Instant对象

Instant instant = Instant.now();

(3)获取秒数、毫秒数

long currentSecond = instant.getEpochSecond();
System.out.println(currentSecond);
输出:1571747741

long currentMilli = instant.toEpochMilli();
System.out.println(currentMilli);
输出:1571747741372

5. LocalDate、LocalTime、LocalDateTime、Instant的计算

LocalDate、LocalTime、LocalDateTime、Instant为不可变对象,修改这些对象对象会返回一个副本。
(1)增加、减少年数、月数、天数等,以LocalDateTime为例

LocalDateTime localDateTime = LocalDateTime.of(2019, Month.SEPTEMBER, 10,14, 46, 56);

//增加一年
localDateTime = localDateTime.plusYears(1);
localDateTime = localDateTime.plus(1, ChronoUnit.YEARS);

//减少一个月
localDateTime = localDateTime.minusMonths(1);
localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS);

//修改年为2019
localDateTime = localDateTime.withYear(2020);
//修改年为2022
localDateTime = localDateTime.with(ChronoField.YEAR, 2022);

(2)时间计算
比如有些时候想知道这个月的最后一天是几号、下个周末是几号,通过提供的时间和日期API可以很快得到答案。比如通过firstDayOfYear()返回了当前日期的第一天日期,还有很多方法这里不在举例说明。

LocalDate localDate = LocalDate.now();
LocalDate localDate1 = localDate.with(firstDayOfYear());

(3)格式化时间
DateTimeFormatter默认提供了多种格式化方式,如果默认提供的不能满足要求,可以通过DateTimeFormatter的ofPattern方法创建自定义格式化方式。

LocalDate localDate = LocalDate.of(2019, 9, 10);

String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);

//自定义格式化
DateTimeFormatter dateTimeFormatter =   DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s3 = localDate.format(dateTimeFormatter);

(4)解析时间
和SimpleDateFormat相比,DateTimeFormatter是线程安全的。

LocalDate localDate1 = LocalDate.parse("20190910", DateTimeFormatter.BASIC_ISO_DATE);

LocalDate localDate2 = LocalDate.parse("2019-09-10", DateTimeFormatter.ISO_LOCAL_DATE);

三、SpringBoot中应用LocalDateTime

1. 将LocalDateTime字段以时间戳的方式返回给前端

(1)添加日期转化类

public class LocalDateTimeConverter extends JsonSerializer<LocalDateTime> {
  @Override
  public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
  	gen.writeNumber(value.toInstant(ZoneOffset.of("+8")).toEpochMilli());
  }
}

(2)在LocalDateTime字段上添加@JsonSerialize(using = LocalDateTimeConverter.class)注解

@JsonSerialize(using = LocalDateTimeConverter.class)
protected LocalDateTime gmtModified;

2. 将LocalDateTime字段以指定格式化日期的方式返回给前端

在LocalDateTime字段上添加@JsonFormat(shape=JsonFormat.Shape.STRING, pattern=“yyyy-MM-dd HH:mm:ss”)注解即可

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
protected LocalDateTime gmtModified;

3. 对前端传入的日期进行格式化

在LocalDateTime字段上添加@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”)注解即可。

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
protected LocalDateTime gmtModified;

参考:

  1. https://mp.weixin.qq.com/s/IMAGF5Nmh3amHi78B7DCMQ
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王小二(海阔天空)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值