2020-08-22

基于java8的时间工具类(个人总结)

项目开发中经常使用到时间处理,但是这些时间处理方法有许多值得探讨的方法

引用:

https://zhuanlan.zhihu.com/p/135985813--分析

https://blog.csdn.net/qq_36596145/article/details/85331002--时间工具类

一.SimpleDateFormat线程不安全

阿里手册中是禁止使用static修饰SimpleDateFormat

 1.SimpleDateFormat的format方法

  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;
    }

其中的calendar是一个共享变量,并且没有做线程安全控制,因此在多并发的情况下,调用到calendar.setTime方法的时候,可能会造成一个线程刚设置好time就被另外一个线程给修改了,导致获取不到正确的值.

    protected Calendar calendar;

测试

package src.com.sinosig.sl.sssc.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatTest {


    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static Date d1 = null;

    private static Date d2 = null;

    private static Date d3 = null;

    static {
        try {
            d1 =sdf.parse("2020-12-12 12:12:12");
            d2 =sdf.parse("2019-11-11 11:11:11");
            d3 =sdf.parse("2018-10-10 10:10:10");
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            String parse = sdf.format(d1);
            System.out.println("当前日期d1:" + parse);
        });

        Thread t2 = new Thread(() -> {
            String parse = sdf.format(d2);
            System.out.println("当前日期d2:" + parse);
        });


        Thread t3 = new Thread(() -> {
            String parse = sdf.format(d3);
            System.out.println("当前日期d3:" + parse);
        });

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
}

测试结果

当前日期d1:2018-11-11 11:11:11
当前日期d3:2018-11-11 11:11:11
当前日期d2:2019-11-11 11:11:11

 

2.SimpleDateFormat的parse方法

        Date parsedDate;
        try {
            parsedDate = calb.establish(calendar).getTime();
            // If the year value is ambiguous,
            // then the two-digit year == the default start year
            if (ambiguousYear[0]) {
                if (parsedDate.before(defaultCenturyStart)) {
                    parsedDate = calb.addYear(100).establish(calendar).getTime();
                }
            }
        }
        // An IllegalArgumentException will be thrown by Calendar.getTime()
        // if any fields are out of range, e.g., MONTH == 17.
        catch (IllegalArgumentException e) {
            pos.errorIndex = start;
            pos.index = oldStart;
            return null;
        }

其中parse方法中有一步是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();

方法内部的实现有一个clear操作,多并发的情况下存在一个线程执行了clear,另外一个线程来get就没有东西了,就会报空指针.

测试

package com.sinosig.sl.sssc.service.impl;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.lang3.time.DateFormatUtils;

import com.sinosig.sl.sssc.base.utils.ThreadLocalDateUtil;

public class DateFormatTest {
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static Date parse(String date){
        Date parse = null;
         try {
            return sdf.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    private static Date parse1(String date){
        Date parse = null;
         try {
            return sdf.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
    public static void main(String[] args)  {
        Thread t1 = new Thread(() -> {
            Date parse=parse("2020-12-12 12:12:12");
			 System.out.println("当前日期:" + parse);
        });

        Thread t2 = new Thread(() -> {
        	Date parse=parse("2020-12-12 12:12:12");
            System.out.println("当前日期:" + parse);
        });


        Thread t3 = new Thread(() -> {
            Date parse=parse("2020-12-12 12:12:12");
            System.out.println("当前日期:" + parse);
        });

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程执行完毕");

    }
}

测试结果

Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Long.parseLong(Long.java:601)
	at java.lang.Long.parseLong(Long.java:631)
	at java.text.DigitList.getLong(DigitList.java:195)
	at java.text.DecimalFormat.parse(DecimalFormat.java:2084)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at com.sinosig.sl.sssc.service.impl.DateFormatTest.parse(DateFormatTest.java:17)
	at com.sinosig.sl.sssc.service.impl.DateFormatTest.lambda$1(DateFormatTest.java:40)
	at java.lang.Thread.run(Thread.java:748)
当前日期:Sat Dec 12 12:12:12 CST 2020
当前日期:Sat Dec 12 12:12:12 CST 2020
线程执行完毕

二.解决方案

1使用局部变量

在方法内部每次用到都new SimpleDateformat();但是这样使用可能会占用大量的内存和jvm空间

2.ThreadLocal

	private static ThreadLocal<SimpleDateFormat> sdf= new ThreadLocal<SimpleDateFormat>() {
		   @Override
		   protected SimpleDateFormat initialValue() {
		       return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		   }
		};

3.java8的新时间处理类(重要)

  LocalDate

   只会获取年月日

  • 创建LocalDate
    //获取当前年月日
    LocalDate localDate = LocalDate.now();
    //构造指定的年月日
    LocalDate localDate1 = LocalDate.of(2019, 9, 10);
  • 获取年、月、日、星期几
    int year = localDate.getYear();
    int year1 = localDate.get(ChronoField.YEAR);
    Month month = localDate.getMonth();
    int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
    int day = localDate.getDayOfMonth();
    int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
    DayOfWeek dayOfWeek = localDate.getDayOfWeek();
    int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK)

LocalTime

只会获取几点几分几秒

  • 创建LocalTime
    LocalTime localTime = LocalTime.of(13, 51, 10);
    LocalTime localTime1 = LocalTime.now();
  • 获取时分秒
    //获取小时
    int hour = localTime.getHour();
    int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
    //获取分
    int minute = localTime.getMinute();
    int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
    //获取秒
    int second = localTime.getSecond();
    int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);

LocalDateTime

获取年月日时分秒,等于LocalDate+LocalTime

  • 创建LocalDateTime
    LocalDateTime localDateTime = LocalDateTime.now();
    LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);
    LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);
    LocalDateTime localDateTime3 = localDate.atTime(localTime);
    LocalDateTime localDateTime4 = localTime.atDate(localDate);
  • 获取LocalDate
    LocalDate localDate2 = localDateTime.toLocalDate();
  • 获取LocalTime
    LocalTime localTime2 = localDateTime.toLocalTime();

Instant

获取秒数

  • 创建Instant对象
    Instant instant = Instant.now();
  • 获取秒数
    long currentSecond = instant.getEpochSecond();
  • 获取毫秒数
    long currentMilli = instant.toEpochMilli();

格式化时间

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);

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

解析时间

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

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 Java 中的 LocalDateTime 类来处理时间。 首先,我们需要将给定的时间段转换为 LocalDateTime 对象: ```java LocalDateTime start = LocalDateTime.of(2020, 11, 20, 0, 0, 0); LocalDateTime end = LocalDateTime.of(2021, 10, 9, 23, 59, 59); ``` 这里将结束时间设置为 23:59:59 是因为我们想要包含该日期的所有时间。 接下来,我们可以使用一个循环来遍历时间段中的每一天,并获取该天的开始时间和结束时间: ```java LocalDateTime current = start; while (!current.isAfter(end)) { LocalDateTime dayStart = current.withHour(0).withMinute(0).withSecond(0).withNano(0); LocalDateTime dayEnd = current.withHour(23).withMinute(59).withSecond(59).withNano(999999999); System.out.println("Day " + current.toLocalDate() + " starts at " + dayStart + " and ends at " + dayEnd); current = current.plusDays(1); } ``` 这里,我们使用 `withHour`、`withMinute`、`withSecond` 和 `withNano` 方法来设置每天的开始时间和结束时间。然后,我们可以使用 `plusDays` 方法来增加 `current` 对象的日期,以便遍历整个时间段。 完整代码如下: ```java import java.time.LocalDateTime; public class Main { public static void main(String[] args) { LocalDateTime start = LocalDateTime.of(2020, 11, 20, 0, 0, 0); LocalDateTime end = LocalDateTime.of(2021, 10, 9, 23, 59, 59); LocalDateTime current = start; while (!current.isAfter(end)) { LocalDateTime dayStart = current.withHour(0).withMinute(0).withSecond(0).withNano(0); LocalDateTime dayEnd = current.withHour(23).withMinute(59).withSecond(59).withNano(999999999); System.out.println("Day " + current.toLocalDate() + " starts at " + dayStart + " and ends at " + dayEnd); current = current.plusDays(1); } } } ``` 输出结果如下: ``` Day 2020-11-20 starts at 2020-11-20T00:00 and ends at 2020-11-20T23:59:59.999999999 Day 2020-11-21 starts at 2020-11-21T00:00 and ends at 2020-11-21T23:59:59.999999999 Day 2020-11-22 starts at 2020-11-22T00:00 and ends at 2020-11-22T23:59:59.999999999 ... Day 2021-10-07 starts at 2021-10-07T00:00 and ends at 2021-10-07T23:59:59.999999999 Day 2021-10-08 starts at 2021-10-08T00:00 and ends at 2021-10-08T23:59:59.999999999 Day 2021-10-09 starts at 2021-10-09T00:00 and ends at 2021-10-09T23:59:59.999999999 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值