java8處理日期和時(shí)間

自屎诘巍:java8用了好久了慰于,雖然一直知道有新的時(shí)間API祖驱,但是一直用java.util.Date用習(xí)慣了秒啦,也沒有特意的去了解java8的時(shí)間類java.time秽澳,雖然效果同樣達(dá)到了吝沫,但不是當(dāng)前最適當(dāng)?shù)姆绞脚朊蓿院笠〗逃?xùn)慷蠕。

在java8中,java.time包下主要包含下面幾個(gè)主要的類:

Instant:時(shí)間戳厘托,相當(dāng)于java.util的Date
LocalDate:只包含日期友雳,比如:2016-10-20
LocalTime:只包含時(shí)間,比如:23:12:10
LocalDateTime:包含日期和時(shí)間铅匹,比如:2016-10-20 23:14:21
Duration:計(jì)算兩個(gè)“時(shí)間”的間隔
Period:用于計(jì)算兩個(gè)“日期”的間隔
ZoneOffset:時(shí)區(qū)偏移量押赊,比如:+8:00
ZonedDateTime:可以得到特定時(shí)區(qū)的日期/時(shí)間
Clock:時(shí)鐘,比如獲取目前美國紐約的時(shí)間

時(shí)間格式化以DateTimeFormatter代替SimpleDateFormat

DateTimeFormatter:時(shí)間格式化

方法前綴的含義,統(tǒng)一了api:

of:靜態(tài)工廠方法(用類名去調(diào)用)包斑。
parse:靜態(tài)工廠方法流礁,關(guān)注于解析(用類名去調(diào)用)。
now: 靜態(tài)工廠方法罗丰,用當(dāng)前時(shí)間創(chuàng)建實(shí)例(用類名去調(diào)用)
get:獲取某些東西的值神帅。
is:檢查某些東西的是否是true。
with:返回一個(gè)部分狀態(tài)改變了的時(shí)間日期對(duì)象拷貝(單獨(dú)一個(gè)with方法,參數(shù)為TemporalAdjusters類型)萌抵。
plus:返回一個(gè)時(shí)間增加了的找御、時(shí)間日期對(duì)象拷貝(如果參數(shù)是負(fù)數(shù)也能夠有minus方法的效果)。
minus:返回一個(gè)時(shí)間減少了的谜嫉、時(shí)間日期對(duì)象拷貝萎坷。
to:把當(dāng)前時(shí)間日期對(duì)象轉(zhuǎn)換成另外一個(gè),可能會(huì)損失部分狀態(tài)沐兰。
at:把這個(gè)對(duì)象與另一個(gè)對(duì)象組合起來哆档,例如: date.atTime(time)。
format :根據(jù)某一個(gè)DateTimeFormatter格式化為字符串住闯。

通過以上一系列方法瓜浸,java.time完成了加、減比原、格式化插佛、解析、從日期/時(shí)間中提取單獨(dú)部分等任務(wù)量窘。

java.time包里面的類實(shí)例如果用了上面的方法而被修改了,那么會(huì)返回一個(gè)新的實(shí)例過來,而不像Calendar那樣可以在同一個(gè)實(shí)例進(jìn)行不同的修改,體現(xiàn)了不可變雇寇。

下面講解具體的類

Instant :時(shí)間戳,相當(dāng)于java.util的Date

Instant用于表示一個(gè)時(shí)間戳蚌铜,它與我們常使用的System.currentTimeMillis()有些類似锨侯,不過Instant可以精確到納秒(Nano-Second),System.currentTimeMillis()方法只精確到毫秒(Milli-Second)冬殃。如果查看Instant源碼囚痴,發(fā)現(xiàn)它的內(nèi)部使用了兩個(gè)常量,seconds表示從1970-01-01 00:00:00開始到現(xiàn)在的秒數(shù)审葬,nanos表示納秒部分(nanos的值不會(huì)超過999,999,999)深滚。Instant除了使用now()方法創(chuàng)建外奕谭,還可以通過ofEpochSecond方法創(chuàng)建:

Instant instant = Instant.ofEpochSecond(120, 100000);

ofEpochSecond()方法的第一個(gè)參數(shù)為秒,第二個(gè)參數(shù)為納秒痴荐,上面的代碼表示從1970-01-01 00:00:00開始后兩分鐘的10萬納秒的時(shí)刻血柳,控制臺(tái)上的輸出為:

1970-01-01T00:02:00.000100Z

Duration : 計(jì)算兩個(gè)“時(shí)間”的間隔

這個(gè)很好理解,看下面的了栗子就懂了

LocalDateTime from = LocalDateTime.of(2019, Month.JANUARY, 21, 15, 56, 0);    // 2019-01-21 15:56:00
LocalDateTime to = LocalDateTime.of(2019, Month.FEBRUARY, 21, 15, 56, 0);     // 2019-02-21 15:56:00
Duration duration = Duration.between(from, to);     // 表示從 2019-01-21 15:56:00 到 2019-02-21 15:56:00

long days = duration.toDays();              // 這段時(shí)間的總天數(shù)
long hours = duration.toHours();            // 這段時(shí)間的小時(shí)數(shù)
long minutes = duration.toMinutes();        // 這段時(shí)間的分鐘數(shù)
long seconds = duration.getSeconds();       // 這段時(shí)間的秒數(shù)
long milliSeconds = duration.toMillis();    // 這段時(shí)間的毫秒數(shù)
long nanoSeconds = duration.toNanos();      // 這段時(shí)間的納秒數(shù)

Duration對(duì)象還可以通過of()方法創(chuàng)建蹬昌,該方法接受一個(gè)時(shí)間段長度混驰,和一個(gè)時(shí)間單位作為參數(shù)

Duration duration1 = Duration.of(5, ChronoUnit.DAYS);       // 5天
Duration duration2 = Duration.of(1000, ChronoUnit.MILLIS);  // 1000毫秒

Period : 用于計(jì)算兩個(gè)“日期”的間隔

Period在概念上和Duration類似攀隔,區(qū)別在于Period是以年月日來衡量一個(gè)時(shí)間段皂贩,比如2年3個(gè)月6天

Period period = Period.of(2, 3, 6);

由于Period是以年月日衡量時(shí)間段,所以between()方法只能接收LocalDate類型的參數(shù):

// 2019-01-21 到 2019-02-21 這段時(shí)間
Period period = Period.between(
                LocalDate.of(2019, 1, 21),
                LocalDate.of(2019, 2, 21));

ZoneId : 時(shí)區(qū)

獲取所有合法的“區(qū)域/城市”字符串 :

Set<String> zoneIds = ZoneId.getAvailableZoneIds();

獲取系統(tǒng)默認(rèn)時(shí)區(qū) :

ZoneId systemZoneId = ZoneId.systemDefault();

創(chuàng)建時(shí)區(qū) :

ZoneId shanghaiZoneId = ZoneId.of("Africa/Bangui");

LocalDateTime:包含日期和時(shí)間昆汹,比如:2016-10-20 23:14:21

獲取當(dāng)前時(shí)間 :

LocalDateTime localDateTime = LocalDateTime.now();//2019-01-21T16:15:52.863

創(chuàng)建特定日期 (多種自定義):

LocalDateTime localDateTime = LocalDateTime.of(2019,01,21,16,22,34);

獲取獲取年明刷、月、日信息 :

LocalDateTime.now().getYear();//2019
LocalDateTime.now().getMonth();//JANUARY
LocalDateTime.now().getDayOfYear();//21
LocalDateTime.now().getDayOfMonth();//21
LocalDateTime.now().getDayOfWeek();//MONDAY
LocalDateTime.now().getHour();//16

能夠自定義時(shí)間 :

LocalDateTime time = LocalDateTime.of(2017, 1, 1, 1, 1,1);
System.out.println(time); //2017-01-01T01:01:01

//使用plus方法增加年份
//改變時(shí)間后會(huì)返回一個(gè)新的實(shí)例nextYearTime
LocalDateTime nextYearTime = time.plusYears(1); 
System.out.println(nextYearTime); //2018-01-01T01:01:01

//使用minus方法減年份
LocalDateTime time = LocalDateTime.of(2017, 1, 1, 1, 1,1);
LocalDateTime lastYearTime = time.minusYears(1);
System.out.println(lastYearTime); //2016-01-01T01:01:01

//使用with方法設(shè)置月份
LocalDateTime time = LocalDateTime.of(2017, 1, 1, 1, 1,1);
LocalDateTime changeTime = time.withMonth(12);
System.out.println(changeTime); //2017-12-01T01:01:01

//判斷當(dāng)前年份是否閏年
System.out.println("isLeapYear :" + time.isLeapYear());

//判斷當(dāng)前日期屬于星期幾
LocalDateTime time = LocalDateTime.now();
DayOfWeek dayOfWeek = time.getDayOfWeek();
System.out.println(dayOfWeek); //WEDNESDAY

LocalDateLocalTimeLocalDateTime類似满粗,不多說


其他使用場景:

判斷兩個(gè)日期是否相等

LocalDate date1 = LocalDate.of(2019, 01, 21);
if(date1.equals(LocalDate.now())){
    System.out.printf("Today %s and date1 %s are same date %n", LocalDate.now(), date1);
}

//輸出:Today 2019-01-21 and date1 2019-01-21 are same date

檢查像生日這種周期性事件

類似每月賬單辈末、結(jié)婚紀(jì)念日、保險(xiǎn)繳費(fèi)日這些周期性事件映皆。使用MonthDay類挤聘。這個(gè)類組合了月份和日,去掉 了年捅彻,這意味著你可以用它判斷每年都會(huì)發(fā)生事件组去。

LocalDate dateOfBirth = LocalDate.of(1993, 01, 21);
MonthDay birthday = MonthDay.of(dateOfBirth.getMonth(), dateOfBirth.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(LocalDate.now());
if(currentMonthDay.equals(birthday)){
    System.out.println("Many Many happy returns of the day !!");
}else{
    System.out.println("Sorry, today is not your birthday");
}

//輸出: Many Many happy returns of the day !!

判斷日期是早于還是晚于另一個(gè)日期

LocalDate tomorrow = LocalDate.of(2019, 1, 22);
if(tomorrow.isAfter(LocalDate.now())){
    System.out.println("Tomorrow comes after today");//Tomorrow comes after today
}

LocalDate yesterday = LocalDate.now().minus(1, ChronoUnit.DAYS);
if(yesterday.isBefore(LocalDate.now())){
    System.out.println("Yesterday is day before today");//Yesterday is day before today
}

java8 時(shí)間類與Date類的相互轉(zhuǎn)化

//Date與Instant的相互轉(zhuǎn)化
Instant instant  = Instant.now();
Date date = Date.from(instant);
Instant instant2 = date.toInstant();
        
//Date轉(zhuǎn)為LocalDateTime
Date date2 = new Date();
LocalDateTime localDateTime2 = LocalDateTime.ofInstant(date2.toInstant(), ZoneId.systemDefault());
        
//LocalDateTime轉(zhuǎn)Date
LocalDateTime localDateTime3 = LocalDateTime.now();
Instant instant3 = localDateTime3.atZone(ZoneId.systemDefault()).toInstant();
Date date3 = Date.from(instant);

//LocalDate轉(zhuǎn)Date
//因?yàn)長ocalDate不包含時(shí)間,所以轉(zhuǎn)Date時(shí)步淹,會(huì)默認(rèn)轉(zhuǎn)為當(dāng)天的起始時(shí)間从隆,00:00:00
LocalDate localDate4 = LocalDate.now();
Instant instant4 = localDate4.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
Date date4 = Date.from(instant);

日期的格式化和解析DateTimeFormatter

Java8DateTimeFormatter也是線程安全的,而SimpleDateFormat并不是線程安全缭裆。

*DateTimeFormatterSimpleDateFormat對(duì)比 *

  1. Date轉(zhuǎn)String

    //使用Date和SimpleDateFormat
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("G yyyy年MM月dd號(hào) E a hh時(shí)mm分ss秒");
    String format = simpleDateFormat.format(new Date());
    System.out.println(format); 
    //打印: 公元 2017年03月21號(hào) 星期二 下午 06時(shí)38分20秒
    
   //使用jdk1.8 LocalDateTime和DateTimeFormatter
   LocalDateTime now = LocalDateTime.now();
   DateTimeFormatter pattern = DateTimeFormatter.ofPattern("G yyyy年MM月dd號(hào) E a hh時(shí)mm分ss秒");
   String format = now.format(pattern);
   System.out.println(format);
   //打印: 公元 2017年03月21號(hào) 星期二 下午 06時(shí)38分20秒
  1. String轉(zhuǎn)Date

    //使用Date和SimpleDateFormat
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    Date date = simpleDateFormat.parse("2017-12-03 10:15:30");
    System.out.println(simpleDateFormat.format(date));
    //打印 2017-12-03 10:15:30
    
    //使用jdk1.8 LocalDateTime和DateTimeFormatter
    DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    //嚴(yán)格按照ISO yyyy-MM-dd驗(yàn)證键闺,03寫成3都不行
    LocalDateTime dt = LocalDateTime.parse("2017-12-03 10:15:30",pattern); 
    System.out.println(dt.format(pattern));
    

使用SimpleDateFormat的正確姿勢(shì)

方法一

在需要執(zhí)行格式化的地方都新建SimpleDateFormat實(shí)例,使用局部變量來存放SimpleDateFormat實(shí)例

public static  String formatDate(Date date)throws ParseException{
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  return sdf.format(date);
}

這種方法可能會(huì)導(dǎo)致短期內(nèi)創(chuàng)建大量的SimpleDateFormat實(shí)例澈驼,如解析一個(gè)excel表格里的字符串日期辛燥。

方法二

為了避免創(chuàng)建大量的SimpleDateFormat實(shí)例,往往會(huì)考慮把SimpleDateFormat實(shí)例設(shè)為靜態(tài)成員變量,共享SimpleDateFormat對(duì)象缝其。這種情況下就得對(duì)SimpleDateFormat添加同步挎塌。

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

public static String formatDate(Date date)throws ParseException{
  synchronized(sdf){
    return sdf.format(date);
  }  
}

這種方法的缺點(diǎn)也很明顯,就是在高并發(fā)的環(huán)境下會(huì)導(dǎo)致解析被阻塞氏淑。

方法三(推薦

要在高并發(fā)環(huán)境下能有比較好的體驗(yàn)勃蜘,可以使用ThreadLocal來限制SimpleDateFormat只能在線程內(nèi)共享,這樣就避免了多線程導(dǎo)致的線程安全問題假残。

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

public static String format(Date date) {
    return threadLocal.get().format(date);
}

//打印
 public static void main(String[] args) {
        System.out.println(format(new Date()));//2019-01-21
 }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缭贡,一起剝皮案震驚了整個(gè)濱河市炉擅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阳惹,老刑警劉巖谍失,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異莹汤,居然都是意外死亡快鱼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門纲岭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抹竹,“玉大人,你說我怎么就攤上這事止潮∏耘校” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵喇闸,是天一觀的道長袄琳。 經(jīng)常有香客問我,道長燃乍,這世上最難降的妖魔是什么唆樊? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮刻蟹,結(jié)果婚禮上逗旁,老公的妹妹穿的比我還像新娘。我一直安慰自己座咆,他們只是感情好痢艺,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著介陶,像睡著了一般堤舒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哺呜,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天舌缤,我揣著相機(jī)與錄音,去河邊找鬼某残。 笑死国撵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的玻墅。 我是一名探鬼主播介牙,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼澳厢!你這毒婦竟也來了环础?” 一聲冷哼從身側(cè)響起囚似,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎线得,沒想到半個(gè)月后饶唤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贯钩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年募狂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片角雷。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祸穷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谓罗,到底是詐尸還是另有隱情粱哼,我是刑警寧澤季二,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布檩咱,位于F島的核電站,受9級(jí)特大地震影響胯舷,放射性物質(zhì)發(fā)生泄漏刻蚯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一桑嘶、第九天 我趴在偏房一處隱蔽的房頂上張望炊汹。 院中可真熱鬧,春花似錦逃顶、人聲如沸讨便。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霸褒。三九已至,卻和暖如春盈蛮,著一層夾襖步出監(jiān)牢的瞬間废菱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國打工抖誉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留殊轴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓袒炉,卻偏偏與公主長得像旁理,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子我磁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容