java 时间日期 详解

时间:2022-09-22 17:43:27
在 JDK1.8 之前都使用时将相关的操作存在多线程问题,可以通过 ThreadLocal 来解决
public class DateFormatThreadLocal {
    private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat( "yyyyMMdd" );
        }
    };
    public static final Date convert(String source) throws ParseException {
        return df .get().parse(source);
    }
}

使用 LocalDate、 LocalTime、 LocalDateTime
LocalDate、 LocalTime、 LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息
注 : ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法

LocalDateTime ldt = LocalDateTime. now ();
System. out .println(ldt);

LocalDateTime ld2 = LocalDateTime. of ( 2016 , 11 , 21 , 10 , 10 , 10 );
System. out .println(ld2);

LocalDateTime ldt3 = ld2.plusYears( 20 );
System. out .println(ldt3);

LocalDateTime ldt4 = ld2.minusMonths( 2 );
System. out .println(ldt4);

System. out .println(ldt.getYear());
System. out .println(ldt.getMonthValue());
System. out .println(ldt.getDayOfMonth());
System. out .println(ldt.getHour());
System. out .println(ldt.getMinute());
System. out .println(ldt.getSecond());
方法 描述 示例
now() 静态方法,根据当前时间创建对象
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
of() 静态方法,根据指定日期/时间创建对象
LocalDate localDate = LocalDate.of(2016, 10, 26);
LocalTime localTime = LocalTime.of(02, 22, 56);
LocalDateTime localDateTime = LocalDateTime.of(2016, 10, 26, 12, 10, 55);
plusDays, plusWeeks,
plusMonths, plusYears
向当前 LocalDate 对象添加几天、几周、 几个月、 几年  
minusDays, minusWeeks,
minusMonths, minusYears
从当前 LocalDate 对象减去几天、几周、 几个月、 几年  
plus, minus 添加或减少一个 Duration 或 Period  
withDayOfMonth,
withDayOfYear,
withMonth,
withYear
将月份天数、 年份天数、 月份、 年份 修 改 为 指 定 的 值 并 返 回 新 的LocalDate 对象  
getDayOfMonth 获得月份天数(1-31)  
getDayOfYear 获得年份天数(1-366)  
getDayOfWeek 获得星期几(返回一个 DayOfWeek枚举值)  
getMonth 获得月份, 返回一个 Month 枚举值  
getMonthValue 获得月份(1-12)  
getYear 获得年份  
until 获得两个日期之间的 Period 对象,或者指定 ChronoUnits 的数字  
isBefore, isAfter 比较两个 LocalDate  
isLeapYear 判断是否是闰年  

Instant 时间戳 (使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值)
用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算
Instant ins = Instant. now ();  //默认使用 UTC 时区
System. out .println(ins);

OffsetDateTime odt = ins.atOffset(ZoneOffset. ofHours ( 8 ));
System. out .println(odt);

System. out .println(ins.getNano());

Instant ins2 = Instant. ofEpochSecond ( 5 );
System. out .println(ins2);

Duration 和 Period
Duration : 用于计算两个“时间”间隔
Period : 用于计算两个“日期”间隔
Instant ins1 = Instant. now ();

System. out .println( "--------------------" );
try {
    Thread. sleep ( 1000 );
} catch (InterruptedException ignored) {
}

Instant ins2 = Instant. now ();

System. out .println( "所耗费时间为:" + Duration. between (ins1, ins2));

System. out .println( "----------------------------------" );

LocalDate ld1 = LocalDate. now ();
LocalDate ld2 = LocalDate. of ( 2011 , 1 , 1 );

Period pe = Period. between (ld2, ld1);
System. out .println(pe.getYears());
System. out .println(pe.getMonths());
System. out .println(pe.getDays());

日期的操纵
TemporalAdjuster : 时间校正器。有时我们可能需要获取例如 : 将日期调整到“下个周日”等操作
TemporalAdjusters : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现
例如获取下个周日:
LocalDate nextSunday = LocalDate. now ().with(TemporalAdjusters. next (DayOfWeek. SUNDAY ));

LocalDateTime ldt = LocalDateTime. now ();
System. out .println(ldt);

LocalDateTime ldt2 = ldt.withDayOfMonth( 10 );
System. out .println(ldt2);

LocalDateTime ldt3 = ldt.with(TemporalAdjusters. next (DayOfWeek. SUNDAY ));
System. out .println(ldt3);

// 自定义:下一个工作日
LocalDateTime ldt5 = ldt.with((l) -> {
    LocalDateTime ldt4 = (LocalDateTime) l;
    DayOfWeek dow = ldt4.getDayOfWeek();
    if (dow.equals(DayOfWeek. FRIDAY )) {
        return ldt4.plusDays( 3 );
    } else if (dow.equals(DayOfWeek. SATURDAY )) {
        return ldt4.plusDays( 2 );
    } else {
        return ldt4.plusDays( 1 );
    }
});
System. out .println(ldt5);

解析与格式化
java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法 :
1> 预定义的标准格式
2> 语言环境相关的格式
3> 自定义的格式
//     DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter dtf = DateTimeFormatter. ofPattern ( "yyyy年MM月dd日 HH:mm:ss E" );

LocalDateTime ldt = LocalDateTime. now ();
String strDate = ldt.format(dtf);
System. out .println(strDate);

LocalDateTime newLdt = ldt. parse (strDate, dtf);
System. out .println(newLdt);

时区的处理
Java8 中加入了对时区的支持,带时区的时间为分别为 : ZonedDate、 ZonedTime、 ZonedDateTime
其中每个时区都对应着 ID,地区ID都为 “ {区域}/{城市}”的格式
例如 : Asia/Shanghai 等
ZoneId : 该类中包含了所有的时区信息
              getAvailableZoneIds() : 可以获取所有时区时区信息
              of(id) : 用指定的时区信息获取 ZoneId 对象

Set<String> set = ZoneId. getAvailableZoneIds ();
set.forEach(System. out ::println);

LocalDateTime ldt = LocalDateTime. now (ZoneId. of ( "Asia/Shanghai" ));
System. out .println(ldt);

ZonedDateTime zdt = ZonedDateTime. now (ZoneId. of ( "US/Pacific" ));
System. out .println(zdt);

与传统日期处理的转换
To 遗留类 
From 遗留类
java.time.Instant
java.util.Date
Date.from(instant) date.toInstant()
java.time.Instant
java.sql.Timestamp
Timestamp.from(instant) timestamp.toInstant()
java.time.ZonedDateTime
java.util.GregorianCalendar
GregorianCalendar.from(zonedDateTime) cal.toZonedDateTime()
java.time.LocalDate
java.sql.Time
Date.valueOf(localDate) date.toLocalDate()
java.time.LocalTime
java.sql.Time
Date.valueOf(localDate) date.toLocalTime()
java.time.LocalDateTime
java.sql.Timestamp
Timestamp.valueOf(localDateTime) timestamp.toLocalDateTime()
java.time.ZoneId
java.util.TimeZone
Timezone.getTimeZone(id) timeZone.toZoneId()
java.time.format.DateTimeFormatter
java.text.DateFormat
formatter.toFormat()

JDK1.8前的时间操作与之后的时间操作
以下操作存在问题
SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" );
Callable<Date> task = new Callable<Date>() {
   @Override
   public Date call() throws Exception {
      return sdf .parse( "20161121" );
   }
};
ExecutorService pool = Executors. newFixedThreadPool ( 10 );
List<Future<Date>> results = new ArrayList<>();
for ( int i = 0 ; i < 10 ; i++) {
   results.add(pool.submit(task));
}
for (Future<Date> future : results) {
   System. out .println(future.get());
}
pool.shutdown();

解决多线程安全问题
Callable<Date> task = new Callable<Date>() {
    @Override
    public Date call() throws Exception {
        return DateFormatThreadLocal. convert ( "20161121" );
    }
};
ExecutorService pool = Executors. newFixedThreadPool ( 10 );
List<Future<Date>> results = new ArrayList<>();
for ( int i = 0 ; i < 10 ; i++) {
    results.add(pool.submit(task));
}
for (Future<Date> future : results) {
    System. out .println(future.get());
}
pool.shutdown();

使用 JDK1.8 的 DateTimeFormatter 就不存在 多线程问题
DateTimeFormatter dtf = DateTimeFormatter. ofPattern ( "yyyyMMdd" );
Callable<LocalDate> task = new Callable<LocalDate>() {
    @Override
    public LocalDate call() throws Exception {
        LocalDate ld = LocalDate. parse ( "20161121" , dtf );
        return ld;
    }
};
ExecutorService pool = Executors. newFixedThreadPool ( 10 );
List<Future<LocalDate>> results = new ArrayList<>();
for ( int i = 0 ; i < 10 ; i++) {
    results.add(pool.submit(task));
}
for (Future<LocalDate> future : results) {
    System. out .println(future.get());
}
pool.shutdown();