在計算機中,應該如何表示日期和時間呢?
我們經常看到的日期和時間表示方式如下:
2019-11-20 0:15:00 GMT+00:00
11/19/2019 19:15:00 America/New_York
如果直接以字符串的形式存儲,那麼不同的格式,不同的語言會讓表示方式非常繁瑣。
在理解日期和時間的表示方式之前,我們先要理解數據的存儲和展示。
當我們定義一個整型變量並賦值時:
int n = 123400;編譯器會把上述字符串(程序源碼就是一個字符串)編譯成字節碼。在程序的運行期,變量n指向的內存實際上是一個4位元組區域:
│00│01│e2│08│注意到計算機內存除了二進位的0/1外沒有其他任何格式。上述十六機制是為了簡化表示。
當我們用System.out.println(n)列印這個整數的時候,實際上println()這個方法在內部把int類型轉換成String類型,然後列印出字符串123400。
它們實際上是數據的展示格式,分別按英國時區、中國時區、紐約時區對同一個時刻進行展示。而這個「同一個時刻」在計算機中存儲的本質上只是一個整數,我們稱它為Epoch Time。
Epoch Time是計算從1970年1月1日零點(格林威治時區/GMT+00:00)到現在所經歷的秒數,例如:
1574208900表示從從1970年1月1日零點GMT時區到該時刻一共經歷了1574208900秒,換算成倫敦、北京和紐約時間分別是:
1574208900 = 北京時間2019-11-20 8:15:00
= 倫敦時間2019-11-20 0:15:00
= 紐約時間2019-11-19 19:15:00
img格式化字符串和日期對象的DateFormat格式轉換類一、古老的Date類Date這個類自jdk1.0開始就被設計出來, 從它的原始碼中我們也是可以看出來,Date類曾經扮演過很重要的角色,jdk早期的版本中有關日期和時間的操作幾乎都是由Date類完成的,下面我們一起看看它的源碼:
private transient long fastTime;二、處理年月日的年曆類Calendar以前我們是可以使用Date來處理日期年月日的,但是由於該類不支持國際化等原因,現在其中大部分方法被註解,不再推薦使用,現在的Date類更像是代表著某一個時刻的對象,而處理年月日的這種轉換則完全交給了Calendar類處理。所以Calendar目前是日期時間處理中的核心類,接下來我們看看其中源碼:
//和Date一樣封裝了毫秒屬性
protected long time;
protected int fields[];
//封裝了十七個靜態常量
public final static int ERA = 0;
public final static int YEAR = 1;
public final static int MONTH = 2;
public final static int WEEK_OF_YEAR = 3;
....
public final static int DST_OFFSET = 16;三、DateFormat處理格式轉換DateFormat是一個抽象類,該類主要用於實現Date對象和字符串之間相互轉換, 涉及到兩個轉換的方法:
//將Date類型轉換為String類型
public final String format(Date date)
//將String類型轉換Date類型
public Date parse(String source)四、優秀的實現類SimpleDateFormatSimpleDateFormat是DateFormat的一個優秀的實現類,它增強了一個重要的性質。它允許自定義格式輸出模板。構造SimpleDateFormat實例的時候,可以傳入一個pattern作為輸出模板。看個例子:
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
SimpleDateFormat sm = new SimpleDateFormat("yyyy年MM月dd日 E HH時mm分ss秒");
System.out.println(sm.format(c.getTime()));輸出結果:
2017年05月29日 星期一 20時25分31秒上述的代碼中,字符串yyyy年MM月dd日 E HH時mm分ss秒就是一個模板pattern,其中:
五、開源第三方庫Joda-TimeJoda-Time庫中的內容還是很多的,我們簡單了解下基本的使用即可,至於深入學習該庫,大家可以自行嘗試,此處限於篇幅,不再贅述。在該庫中DateTime相當於jdk中Calendar,主要完成對日期年月日的計算操作。首先我們通過簡單易理解的方式創建DateTime的實例對象:
//2017-05-29 21:40
DateTime dt = new DateTime(2017,5,29,21,40);
//2017-05-29 21:40 50秒
DateTime dt2 = new DateTime(2017,5,29,21,40,50);Java8以前在java.util這個包裡,主要包括Date、Calendar和TimeZone這幾個類;
Java8中推出了全新的日期時間類在java.time這個包裡面,使用起來更加簡單和安全,常用的有以下幾個
LocalDate:本地日期,只包括日期
LocalTime:本地時間,只包括時間
LocalDateTime:本地日期時間,包括日期和時間
ZonedDateTime:完整的本地日期時間,包括日期,時間,和時區
代碼片段:獲取當前日期LocalDate localDate = LocalDate.now();
System.out.println("當前日期為:" + localDate);//當前日期為:2021-12-24
LocalDate提供了獲取年、月、日的快捷方法System.out.println("當前年為:" + localDate.getYear());//當前年為:2021
System.out.println("當前月為:" + localDate.getMonth());//當前月為:JUNE
System.out.println("當前月為:" + localDate.getMonthValue());//當前月為:6
System.out.println("當前日為當月的第:" + localDate.getDayOfMonth() + "天");//當前日為當月的第:19天
System.out.println("當前日為當周的:" + localDate.getDayOfWeek());//當前日為當周的:FRIDAY
System.out.println("當前日為當年的第:" + localDate.getDayOfYear() + "天");//當前日為當年的第:171天
指定日期,這裡的月對應的就是實際的月LocalDate zdDate = LocalDate.of(2020, 06, 18);
System.out.println("指定日期為:" + zdDate);//指定日期為:2021-12-24
判斷兩個日期是否相等,直接使用equalsboolean flag = localDate.equals(zdDate);
System.out.println("兩個日期是否相等" + flag);//兩個日期是否相等false
boolean isEqual = localDate.isEqual(zdDate);
System.out.println("兩個日期是否相等" + flag);//兩個日期是否相等false
判斷是否為閏年System.out.println("是否為閏年:" + localDate.isLeapYear());//是否為閏年:true
只需要年月、月日,獲取方式有多種YearMonth yearMonth = YearMonth.now();
System.out.println("當前年月為:" + yearMonth);//當前年月為:2020-06
YearMonth currentYearMonth1 = YearMonth.from(localDate);
System.out.println("當前年月為:" + currentYearMonth1);//當前年月為:2020-06
YearMonth zdYearMonth = YearMonth.of(zdDate.getYear(), zdDate.getMonth());
System.out.println("指定年月為:" + zdYearMonth);//指定年月為:2020-06
返回當月天數System.out.println("當月天數為:" + yearMonth.lengthOfMonth());//當月天數為:30
獲取月日MonthDay monthDay = MonthDay.now();
System.out.println("當前月日為:" + monthDay);//當前月日為:--06-19
MonthDay currentMonthDay = MonthDay.from(localDate);
System.out.println("當前月日為:" + currentMonthDay);//當前月日為:--06-19
MonthDay zdMonthDay = MonthDay.of(zdDate.getMonth(), zdDate.getDayOfMonth());
System.out.println("指定月日為:" + zdMonthDay);//指定月日為:--06-18
日期的加減通過LocalDate的plus方法增加年、月、周、天,也可以使用plus後直接跟年(plusYears)、月(plusMonths)、周(plusWeeks)、日(plusDays);minus方法減少年、月、周、天,也可以使用minus後直接跟年(minusYears)、月(minusMonths)、周(minusWeeks)、日(minusDays)
LocalDate nextWeek = localDate.plus(1, ChronoUnit.WEEKS);
System.out.println("一周後日期為:" + nextWeek);//一周後日期為:2020-06-26
LocalDate newWeek = localDate.plusWeeks(1);
System.out.println("一周後日期為:" + newWeek);//一周後日期為:2020-06-26
LocalDate oldWeek = localDate.minus(1, ChronoUnit.WEEKS);
System.out.println("一周前日期為:" + oldWeek);//一周前日期為:2020-06-12
獲取當前時間,默認時間格式為hh:mm:ss:nnnLocalTime localTime = LocalTime.now();
System.out.println("當前時間為:" + localTime);//當前時間為:16:17:35.717
LocalTime zdLocalTime = LocalTime.of(14, 40, 30);
System.out.println("指定時間為:" + zdLocalTime);//指定時間為:14:40:30
時間的加減通過LocalTime的plus方法增加時、分、秒,也可以使用plus後直接跟時(plusHours)、分(plusMinutes)、秒(plusSeconds);minus方法減少時、分、秒,也可以使用minus後直接跟時(minusHours)、分(minusMinutes)、秒(minusSeconds)
LocalTime newLocalTimeHour = localTime.plusHours(2);
System.out.println("2個小時後時間為:" + newLocalTimeHour);//2個小時後時間為:18:17:35.717
LocalTime oldLocalTimeHour = localTime.minusHours(2);
System.out.println("2個小時前時間為:" + oldLocalTimeHour);//2個小時前時間為:14:17:35.717
LocalTime newLocalTimeMinute = localTime.plusMinutes(20);
System.out.println("20分鐘後時間為:" + newLocalTimeMinute);//20分鐘後時間為:16:37:35.717
LocalTime oldLocalTimeMinute = localTime.minusMinutes(20);
System.out.println("20分鐘前時間為:" + oldLocalTimeMinute);//20分鐘前時間為:15:57:35.717
使用Duration計算兩個時間間隔多少小時、分鐘、秒long minutes = Duration.between(zdLocalTime, localTime).toMinutes();
System.out.println("兩個時間間隔為:" + minutes);//兩個時間間隔為:97
使用Duration計算兩個日期間隔天數LocalDate localDate = LocalDate.now();
LocalDate agoDate = LocalDate.of(2020, 12, 20);
long days = Duration.between(agoDate.atStartOfDay(), localDate.atStartOfDay()).toDays();
使用ChronoUnit計算兩個日期間隔天數LocalDate localDate = LocalDate.now();
LocalDate agoDate = LocalDate.of(2020, 12, 20);
long days = ChronoUnit.DAYS.between(agoDate, localDate);
使用LocalDate計算兩個日期間隔天數LocalDate localDate = LocalDate.now();
LocalDate agoDate = LocalDate.of(2020, 12, 20);
long days = localDate.toEpochDay() - agoDate.toEpochDay();
使用Period計算兩個日期間隔多少年、月、天long days = Period.between(zdDate, localDate).getDays();
System.out.println("兩個日期間隔為:" + days);//兩個日期間隔為:1
Instant時間戳,默認使用 UTC 時區(世界協調時間)Instant instant = Instant.now();
System.out.println(instant);//2020-06-19T08:17:35.718Z
System.out.println("時間戳秒" + instant.getEpochSecond());//時間戳秒1592554655
System.out.println("時間戳毫秒" + instant.toEpochMilli());//時間戳毫秒1592554655718
System.out.println("在1970-01-01T00:00:00上加上20秒為:" + Instant.ofEpochSecond(20));//在1970-01-01T00:00:00上加上20秒為:1970-01-01T00:00:20Z
System.out.println("在1970-01-01T00:00:00上加上20毫秒為:" + Instant.ofEpochMilli(20));//在1970-01-01T00:00:00上加上20毫秒為:1970-01-01T00:00:00.020Z
使用LocalDateTime獲取時間戳LocalDateTime localDateTime = LocalDateTime.now();
long second = localDateTime.toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
System.out.println("當前日期時間戳(精確到秒)為:" + second);
long milli = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
System.out.println("當前日期時間戳(精確到毫秒)為:" + milli);
將時間戳轉化為LocalDateTimeLocalDateTime localDateTime1 = LocalDateTime.ofEpochSecond(second, 0, ZoneOffset.ofHours(8));
System.out.println("將時間戳(精確到秒)轉化為時間為:" + localDateTime1);
LocalDateTime localDateTime2 = LocalDateTime.ofInstant(Instant.ofEpochSecond(second), ZoneId.systemDefault());
System.out.println("將時間戳(精確到秒)轉化為時間為:" + localDateTime2);
LocalDateTime localDateTime3 = LocalDateTime.ofInstant(Instant.ofEpochMilli(milli), ZoneId.systemDefault());
System.out.println("將時間戳(精確到毫秒)轉化為時間為:" + localDateTime3);
時區偏移量計算OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println("偏移8小時後為:" + offsetDateTime);//偏移8小時後為:2020-06-19T16:30:55.011+08:00
Clock時鐘類獲取當前時間戳Clock clock = Clock.systemUTC();
System.out.println("Clock:" + clock);//Clock:SystemClock[Z]
System.out.println("Clock時間戳為" + clock.millis());//Clock時間戳為1592555455013
Clock defaultClock = Clock.systemDefaultZone();
System.out.println(defaultClock);//SystemClock[Asia/Shanghai]
System.out.println("Clock系統時間戳為" + defaultClock.millis());//Clock系統時間戳為1592555455013
判斷日期早於或晚於boolean isBeforeDate = localDate.isBefore(zdDate);
System.out.println("當前日期是否早於指定日期" + isBeforeDate);//當前日期是否早於指定日期false
boolean isAfterDate = localDate.isAfter(zdDate);
System.out.println("當前日期是否晚於指定日期" + isAfterDate);//當前日期是否晚於指定日期true
判斷時間早於或晚於boolean isBeforeTime = localTime.isBefore(zdLocalTime);
System.out.println("當前時間是否早於指定時間" + isBeforeTime);//當前時間是否早於指定時間false
boolean isAfterTime = localTime.isAfter(zdLocalTime);
System.out.println("當前時間是否晚於指定時間" + isAfterTime);//當前時間是否晚於指定時間true
獲取當前日期時間LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("當前日期時間為:" + localDateTime);//當前日期時間為:2020-06-19T16:30:55.013
LocalDateTime zdLocalDateTime1 = LocalDateTime.of(2020, 06, 19, 15, 20, 30);
System.out.println("指定日期時間為:" + zdLocalDateTime1);//指定日期時間為:2020-06-19T15:20:30
LocalDateTime zdLocalDateTime2 = LocalDateTime.of(zdDate, zdLocalTime);
System.out.println("指定日期時間為:" + zdLocalDateTime2);//指定日期時間為:2020-06-18T14:40:30
LocalDateTime nextLocalDateTime = localDateTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println("下一個周日的日期時間為:" + nextLocalDateTime);//下一個周日的日期時間為:2020-06-21T16:30:55.013
帶時區的日期時間ZoneId america = ZoneId.of("America/New_York");
ZonedDateTime zonedDateTimeNewYork = ZonedDateTime.of(localDateTime, america);
System.out.println("指定時區日期時間為:" + zonedDateTimeNewYork);//指定時區日期時間為:2020-06-19T16:30:55.013-04:00[America/New_York]
格式化日期時間String formatLocalDate = localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
System.out.println("格式化日期為:" + formatLocalDate);//格式化日期為:20200619
String formatLocalDateTime = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
System.out.println("格式化日期時間為:" + formatLocalDateTime);//格式化日期時間為:20200619163055
將字符串轉換為日期LocalDate parseLocalDate = LocalDate.parse("2020-06-19", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println("將指定的日期字符串轉換為日期為:" + parseLocalDate);//將指定的日期字符串轉換為日期為:2020-06-19
Date轉換為LocalDateTime、LocalDateDate date = new Date();
Instant instant = date.toInstant();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
LocalDateTime localDateTime1 = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDate localDate1 = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
LocalDate localDate2 = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
LocalDateTime、LocalDate轉換為DateLocalDateTime localDateTime = LocalDateTime.now();
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
LocalDate localDate = LocalDate.now();
Date date1 = Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());2.