java.util.Date
Date
类从JDK1.0开始就已经提供了,封装了当前的日期和时间。月份和小时从0开始,月份的天数从1开始,年份从1900开始。
但是,Date类不能实现国际化,偏移量也不统一。
⚠️注意:该类中大部分方法都已经过时 ,并且是线程不安全的。
常用的方法:
构造方法java.util.Date#Date()
、java.util.Date#Date(long)
java.util.Date#getTime
,返回自1970起已经过的毫秒数
Date a = new Date (); System.out.println(a); Date b = new Date (System.currentTimeMillis());System.out.println(b); System.out.println(b.getTime());
java.time.Instant
替代 Date 类
Date 时间精确到 ms,Instant 时间精确到 ns。
LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDateTime); ZoneId zoneId = ZoneId.systemDefault(); System.out.println(zoneId); ZonedDateTime zdt = localDateTime.atZone(zoneId); System.out.println(zdt); System.out.println(zdt.toInstant()); Date date = Date.from(zdt.toInstant());System.out.println(date); System.out.println("-----------" ); System.out.println(Instant.now()); System.out.println(Instant.now().atZone(ZoneId.systemDefault()));
java.util.Calendar
不推荐使用
Calendar calendar = Calendar.getInstance(); System.out.println(calendar); int days = calendar.get(Calendar.DAY_OF_YEAR); System.out.println(days); int weeks = calendar.get(Calendar.WEEK_OF_YEAR); System.out.println(weeks); int day = calendar.get(Calendar.DAY_OF_WEEK) - 1 ; System.out.println(day);
java.text.SimpleDateFormat
在使用Date
对象时,我们会使用
SimpleDateFormat
进行格式化显示。用于格式化输出日期,线程不安全,所以需要在每个方法内部都定义一个局部变量。
SimpleDateFormat#format
方法,底层会调用
calendar.setTime(date)
。
private StringBuffer format (Date date, StringBuffer toAppendTo, FieldDelegate delegate) { calendar.setTime(date); }
该类在多线程时,会存在线程不安全问题。因为 SimpleDateFormat
被多个线程调用 format()
方法时,t1
线程进入方法,执行了calendar.setTime(date);
,设置成功,此时
t2 线程也执行该方法,也执行
calendar.setTime(date);
,这就会导致线程在切换时,t1
线程设置好的时间被修改了,导致 t1 返回的时间格式是错误的。
解决方案:
可以使用 ThreadLocal
使得线程和SimpleDateFormat实例对象绑定,解决线程不安全的问题;
使用 LocalDateTime
类替代 Date
。
SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" );String time = sdf.format(new Date ());System.out.println(time); try { String dateStr = "2024-01-25 20:00:00" ; Date dateParse = sdf.parse(dateStr); System.out.println(dateParse); } catch (ParseException e) { e.printStackTrace(); } class DateUtil { private static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" )); }
在使用 LocalDateTime
或者
ZonedLocalDateTime
时,需要进行格式化显示就需要使用到
DateTimeFormatter
,用于替代SimpleDateFormat
。
和SimpleDateFormat
不同的是,DateTimeFormatter
既是不变对象,还是线程安全的(因为变量都是使用final
修饰的)。
DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" );LocalDateTime ldt = LocalDateTime.now();System.out.println(ldt); System.out.println(dft.format(ldt)); String dateStr = "2024/01/25 20时00分01秒" ; DateTimeFormatter dft2 = DateTimeFormatter.ofPattern( "yyyy/MM/dd HH时mm分ss秒" ); LocalDateTime ldt2 = LocalDateTime.parse(dateStr, dft2); System.out.println(ldt2);
java.time.LocalDate
/ LocalTime / LocalDateTime
代替 Date,线程安全
LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDateTime); System.out.println(localDateTime.getYear()); System.out.println(localDateTime.getMonth()); System.out.println(localDateTime.getMonthValue()); System.out.println(localDateTime.getDayOfMonth()); System.out.println(localDateTime.getHour()); System.out.println(localDateTime.getMinute()); System.out.println(localDateTime.getSecond()); LocalDateTime now = LocalDateTime.now();LocalDateTime nextTime = now .plusYears(5 ) .plusMonths(5 ) .plusDays(5 ) .plusHours(5 ) .plusMinutes(5 ) .plusSeconds(5 ); System.out.println(nextTime); LocalDateTime prevTime = now.minusYears(5 );System.out.println(prevTime);
两个时间作比较,第一个时间减去第二个时间,如果年份相同,比较月份,月份相同比较天数,以此类推。
计算时间差
LocalDateTime now = LocalDateTime.now();LocalDateTime time = LocalDateTime.of(2024 , 1 , 1 , 1 , 1 , 1 );Duration duration = Duration.between(time, now); System.out.println(duration.toDays()); System.out.println(duration.toHours()); System.out.println(duration.toMinutes()); System.out.println(duration.toNanos());
实现:每周四的定时任务
通过 Executors.newScheduledThreadPool
实现
LocalDateTime now = LocalDateTime.now();LocalDateTime time = now.withHour(18 ).withMinute(0 ).withSecond(0 ).withNano(0 ) .with(DayOfWeek.THURSDAY); if (now.isAfter(time)) { time = time.plusWeeks(1 ); } Duration duration = Duration.between(now, time);long initialDelay = duration.toMillis();long period = 1000 * 60 * 60 * 24 * 7 ;ScheduledExecutorService pool = Executors.newScheduledThreadPool(1 );pool.scheduleAtFixedRate(() -> { System.out.println("executor task..." ); }, initialDelay, period, TimeUnit.MICROSECONDS);
获取从
1970-01-01 00:00:00
到现在的毫秒数
long currentTimeMillis = System.currentTimeMillis();System.out.println(currentTimeMillis); long currentTimeMills2 = Clock.systemDefaultZone().millis(); System.out.println(currentTimeMills2);