本文目的:掌握 Java 中日期和時間經常使用 API 的使用。
參考:Jakob Jenkov的英文教程Java Date Time Tutorial 和 JavaDochtml
Java 8 新增 APIjava
Java 8 部分新類 | 描述 |
---|---|
Instant | 表示時間線上的某一瞬間,用秒和納秒錶示。 |
Duration | 表示時間差,用秒和納秒錶示。 |
LocalDate | 表示沒有時區信息的日期,例如生日、法定假日等。 |
LocalTime | 表示沒有時區信息的一天中的本地時間。 |
LocalDateTime | 表示沒有時區信息的日期和時間 |
ZonedDateTime | 表示日期和時間,包括時區信息 |
DateTimeFormatter | 將日期時間對象格式化爲字符串。 |
衆所周知,在 Java 8 中添加了一個全新的日期時間 API 位於 java.time 包中,主要變化是,自1970年1月1日以來,日期和時間如今再也不由單個毫秒數表示,而是由1970年1月1日以來的秒數和納秒數表示。
秒數既能夠是正的,也能夠是負的,用 long 表示。納秒數始終爲正,由 int 表示。sql
Java 7 具備如下日期和時間類和方法:數據庫
Java 7 日期時間經常使用類/方法 | 描述 |
---|---|
System.currentTimeMillis() | 自1970年1月1日起以毫秒爲單位返回當前日期和時間的靜態方法 |
java.util.Date | 表示日期和時間的類。這個類中的大多數方法都是不推薦的。 |
java.sql.Date | 表示日期的類。這個date類與JDBC一塊兒使用。 |
java.sql.Timestamp | 表示日期和時間的類。這個date和time類與JDBC一塊兒使用。 |
java.util.Calendar | 日曆類的基類。 有方法作日期和時間算術,好比將日期或月份添加到另外一個日期。 |
java.util.GregorianCalendar | 一個 Calendar 類的子類。表明公曆,在今天的西方世界大部分地區使用。擁有 Calendar 中的全部作日期和時間算術的方法。 |
java.util.TimeZone | 一個表示時區的類,在跨時區執行日曆計算時很是有用。 |
應該使用全部這些類中的哪個取決於想要作什麼,若是你須要作簡單的計時, System.currentTimeMillis() 方法就能夠了。api
currenttimemillis() 靜態方法以毫秒爲單位返回自1970年1月1日以來的時間。返回的值是long。這裏有一個例子:安全
long timeNow = System.currentTimeMillis();
這個返回值能夠用來初始化 java.util.Date, java.sql.Date, java.sql.Timestamp 和 java.util.GregorianCalendar 對象,它還能夠用於在程序中測量時間。oracle
currenttimemillis() 方法的粒度大於 1 毫秒,這取決於操做系統,還可能更大,許多操做系統以幾十毫秒爲單位測量時間。若是須要更精確的計時,請使用 System.nanoTime() ,可是這個方法返回的時間是從任意一個時刻計算的,甚至有多是負數,因此不能用於初始化日期時間對象,只適合用於計算兩個時間點的時間差。函數
用來表示日期,包含年月日時分秒 ,目前該類中的大多數方法都不同意使用了,通常用 Calendar 類來代替它,但仍是有必要簡單瞭解一下。
下面是一些使用例子:this
Date dateNow = new Date(); // 使用當前日期和時間建立
Date 類的默認構造器,源碼是這樣的:操作系統
public Date() { this(System.currentTimeMillis()); }
也可使用一個 long 型的有參構造函數:
Date date = new Date(long);
Date 類還有一個 getTime() 實例方法,這個方法的返回值就是 new Date(long) 時指定的 long 參數。
從 Java 8 開始,新增了和 Instant 互相轉換的方法,關於 Instant 請參考本文下部分,這裏瞭解就行:
static Date from(Instant instant); Instant toInstant();
此類是上述 java.util.Date 類的子類,因此它繼承了 java.util.Date 的全部方法和字段。通常在 JDBC API 中使用它,好比能夠在 PreparedStatement 上設置日期,或者從 ResultSet 獲取日期,
和 java.util.Date 最大的區別就是它只記日期,不記時間,即只有年月日,若是構造的時候包含了時間信息,那麼時間信息會被捨棄,若是要記時間,須要用到 java.sql.Timestamp 類。
此類也繼承了 java.util.Date,包含的信息有年月日時分秒納秒,是的,它還擴展了納秒,一個使用示例以下:
long time = System.currentTimeMillis(); java.sql.Timestamp timestamp = new java.sql.Timestamp(time); timestamp.setNanos(123456); int nanos = timestamp.getNanos(); // nanos = 123456
Calendar 抽象類用於執行日期和時間換算,沒法使用構造器實例化它,緣由是世界上有不止一個日曆。
可是其提供了一個 getInstance() 方法,能夠獲取對應當前時間的 Calendar 對象:
Calendar rightNow = Calendar.getInstance();
getInstance() 方法底層是以下這樣實現的:
public static Calendar getInstance() { return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT)); }
沒錯,很容易想到,此方法還有重載的,能夠提供部分指定初始化參數的版本,以下:
getInstance(TimeZone zone); getInstance(Locale aLocale); getInstance(TimeZone zone, Locale aLocale);
此外,通常能夠經過其子類 GregorianCalendar 來訪問日期時間信息,一個例子以下:
Calendar calendar = new GregorianCalendar(); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); // 一月 Jan = 0, 不是 1 int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); int weekOfYear = calendar.get(Calendar.WEEK_OF_YEAR); int weekOfMonth= calendar.get(Calendar.WEEK_OF_MONTH); int hour = calendar.get(Calendar.HOUR); // 12 小時制 int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY); // 24 小時制 int minute = calendar.get(Calendar.MINUTE); int second = calendar.get(Calendar.SECOND); int millisecond= calendar.get(Calendar.MILLISECOND); calendar.set(Calendar.YEAR, 2018); calendar.set(Calendar.MONTH, 11); // 11 = december,十二月 calendar.set(Calendar.DAY_OF_MONTH, 24); // 聖誕節
Calendar calendar = new GregorianCalendar(); // 加 1 天 calendar.add(Calendar.DAY_OF_MONTH, 1); // 當第二個參數爲負數時,表示減,下面就是減 1 天 calendar.add(Calendar.DAY_OF_MONTH, -1);
// Calendar to Date Calendar calendar = Calendar.getInstance(); java.util.Date date = calendar.getTime(); // Date to Calendar calendar.setTime(new java.util.Date()); // Calendar to String Calendar calendat = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String dateStr = sdf.format(calendar.getTime()); // String to Calendar String str = "2018-12-3"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf.parse(str); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); // Date to String SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String dateStr = sdf.format(new Date()); // String to Date String str = "2018-12-3"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date dateParse = sdf.parse(str);
TimeZone 是一個表示時區的類,在跨時區執行日曆計算時很是有用,通常和 Calendar 一塊兒使用。
注意:在 Java 8 日期時間 API 中,時區由 java.time.ZoneId 類表示。 若是使用的是 Java 8 日期時間類(如 ZonedDateTime 類)的話,則只須要使用 ZoneId 類就好了。 若是使用的是 Calendar (來自Java 7和更早的日期時間API),那麼仍然可使用 java.util.TimeZone 類。
Calendar calendar = new GregorianCalendar(); // 從 Calendar 獲取時區 TimeZone timeZone = calendar.getTimeZone(); // 爲 Calendar 設置時區 calendar.setTimeZone(timeZone);
// 獲取默認時區對象 TimeZone timeZone = TimeZone.getDefault(); // 獲取指定時區對象 TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); TimeZone timeZone = TimeZone.getTimeZone("Europe/Copenhagen");
TimeZone.getTimeZone() 方法的參數能夠是一個 zone ID ,能夠查看 JavaDoc 獲取所有 ID 。
注意:若是 getTimeZone(String zoneID);方法的 zoneID 設置錯誤(不匹配系統支持的任意值),好比 "Asiannn/Shanghai",那也不會拋出任何異常,而是默默地設置 zoneID 爲 GMT0 ,即格林威治時間。
咱們能夠查看給定時區的顯示名稱、ID和時間偏移量,以下所示
TimeZone timeZone = TimeZone.getDefault(); System.out.println(timeZone.getDisplayName()); System.out.println(timeZone.getID()); System.out.println(timeZone.getOffset(System.currentTimeMillis()));
以上代碼將輸出:
中國標準時間 Asia/Shanghai 28800000
getOffset() 方法以 int 類型返回該時區在指定日期的 UTC 偏移量(毫秒)。上例中的 28800000 毫秒,也就是 8 h ,咱們在東八區(+8)。
TimeZone timeZoneCN = TimeZone.getTimeZone("Asia/Shanghai"); TimeZone timeZone0 = TimeZone.getTimeZone("Etc/GMT0"); Calendar calendar = new GregorianCalendar(); calendar.setTimeZone(timeZoneCN); long timeCN = calendar.getTimeInMillis(); System.out.println(calendar.getTimeZone().getDisplayName()); System.out.println("timeCN = " + timeCN); System.out.println("hour = " + calendar.get(Calendar.HOUR_OF_DAY)); calendar.setTimeZone(timeZone0); System.out.println(calendar.getTimeZone().getDisplayName()); long time0 = calendar.getTimeInMillis(); System.out.println("time0 = " + time0); System.out.println("hour = " + calendar.get(Calendar.HOUR_OF_DAY));
以上程序將會輸出以下:
中國標準時間 timeCN = 1543850448183 hour = 23 格林威治時間 time0 = 1543850448183 hour = 15
能夠看到,以毫秒爲單位的時間在兩個時區是相同的,可是已從23點變成15點鐘了,由於中國標準時間比格林威治時間快 8 小時,如此,咱們設置不一樣時區獲取對應時區的正確時間,這樣就實現的換算的目的。
java.text.SimpleDateFormat 類能夠解析字符串中的日期,也能夠格式化字符串中的日期,本文將展現幾個例子:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); String dateString = format.format(new Date()); Date date = format.parse ("2018-12-03");
做爲參數傳遞給 SimpleDateFormat 類的字符串是一種模式(模板),用於說明如何解析和格式化日期。 在上面的示例中使用了模式「yyyy-MM-dd」,表示年份 4 位數(yyyy),月份 2 位數(MM)和日期 2 位數(dd)的表示形式,"2018-12-03"中使用‘-’分割是由於在模式中也是用‘-’分割字母的。
如下是常見模式字母列表,具體請看 JavaDoc :
y = year (yy or yyyy) M = month (MM) d = day in month (dd) h = hour (0-12) (hh) H = hour (0-23) (HH) m = minute in hour (mm) s = seconds (ss) S = milliseconds (SSS) z = time zone text (e.g. Pacific Standard Time...) Z = time zone, time offset (e.g. -0800) //一下是一些示例: yyyy-MM-dd HH:mm:ss (2018-12-3 23:59:59) HH:mm:ss.SSS (23:59.59.999) yyyy-MM-dd HH:mm:ss.SSS (2009-12-31 23:59:59.999) yyyy-MM-dd HH:mm:ss.SSS Z (2009-12-31 23:59:59.999 +0100)
若是指定 「dd」 來解析new SimpleDateFormat("yyyy-MM-dd");
那麼天數確定被表示爲 2 位,好比 3 號就是 03。
若是是指定 「d」 來解析new SimpleDateFormat("yyyy-MM-d");
那麼天數優先是 1 位,好比 3 號就是 3, 若是超出 1 位,那會自動擴展爲 2 位,好比 13 號,那麼就是 13 。
Java .time.Instant 類表示時間線上的一個特定時刻,被定義爲自原點起的偏移量,原點是1970年1月1日00點格林,也就是格林尼治時間。 時間以天天 86400 秒爲單位,從原點向前移動。
Java.time 這個包是線程安全的,而且和其餘大部分類同樣,是不可變類。Instant 也不例外。
使用 Instant 類的工廠方法之一建立實例。例如,要建立一個表示當前時刻的時間點,能夠調用 instance .now() ,以下所示:
Instant now = Instant.now();
Instant 對象包含秒和納秒,來表示其包含的時間, 自紀元以來的秒數是上完提到的自原點以來的秒數。 納秒是 Instant 的一部分,不到一秒鐘。分別能夠經過以下 2 個方法獲取:
long getEpochSecond(); int getNano();
Instant類還有幾種方法可用於相對於Instant進行計算。 這些方法中的一些(不是所有)是:
一個例子以下:
Instant now = Instant.now(); // 如今這一瞬間 Instant later = now.plusSeconds(3); // 3 秒後的瞬間 Instant earlier = now.minusSeconds(3); // 3 秒前的瞬間
由於 Instant 是不可變的,因此上面的計算方法,是返回一個表明計算結果的新的 Instant 對象。
java.time.Duration 表示兩個 Instant 之間的一段時間,Duration 實例是不可變的,所以一旦建立它,就不能更改它的值。但能夠基於一個 Duration 對象建立新的 Duration 對象。
可使用 Duration 類的工廠方法之一建立 Duration 對象,有 between()/ofDays()/ofSeconds()/from() 等方法,但其底層都是調用了同一個構造方法,其源碼以下:
private Duration(long seconds, int nanos) { super(); this.seconds = seconds; this.nanos = nanos; }
下面是一個使用 between() 方法建立的示例:
Instant first = Instant.now(); // 其餘耗時操做 Instant second = Instant.now(); Duration duration = Duration.between(first, second);
從上述構造器源碼可知,Duration 在內部維護兩個值:
請注意沒有單獨的毫秒部分,只有納秒和秒。但能夠能夠將整個時間間隔 Duration 轉換爲其餘時間單位,如納秒、分鐘、小時或天:
toNanos() 與 getNano() 的不一樣之處在於 getNano() 僅返回持續時間小於一秒的部分(即整個時間段中不到 1 秒的那部分)。 toNanos() 方法返回的是轉換爲納秒的整個時間段(即秒部分轉成納秒+納秒部分)。
沒有 toSeconds() 方法,由於 getSeconds() 方法已經能夠獲取 Duration 的秒部分。
Duration 類包含一組可用於基於 Duration 對象執行計算的方法。其中一些方法是:
這些方法的使用大同小異,一下是一個例子:
Duration start = ... Duration added = start.plusDays(3); // 加 3 天 Duration subtracted = start.minusDays(3); // 減 3 天
一樣,爲了使Duration對象保持不可變,全部計算方法都返回表示計算結果的新的 Duration 對象。
java.time.LocalDate 表示本地日期,沒有時區信息。當地的日期能夠是生日或法定假日等,與一年中的某一天有關,和一天中的某一時間無關。這個類對象也是不可變的,計算操做會返回一個新的 LocalDate 對象。
下面是一個建立 LocalDate 對象的例子:
LocalDate localDate1 = LocalDate.now(); LocalDate localDate2 = LocalDate.of(2018, 11, 11);
還有不少方法能夠建立 LocalDate 對象,我列出一部分下面,具體的請查看 JavaDoc 。
LocalDate 中一共有 3 個日期信息字段,分別是:
對應一些獲取信息的方法:
下面是一個例子:
LocalDate localDate = LocalDate.of(2018, 12, 12); LocalDate localDate1 = localDate.plusYears(3); // 加 3 年 LocalDate localDate2 = localDate.minusYears(3);
java.time.LocalTime 表示沒有任什麼時候區信息的特定時間,例如,上午 10 點。一樣,這是一個不可變類。
下面是一個建立 LocalTime 對象的例子:
LocalTime localTime1 = LocalTime.now(); LocalTime localTime2 = LocalTime.of(21, 30, 59, 11001);
LocalTime 內部維護了 4 個變量維護時間信息:
也包含了必要的計算時間的方法,例如 LocalTime plusHours(long hoursToAdd);
其餘的和 LocalDate 大同小異,就不展開講了。
java.time.LocalDateTime 類表示沒有任什麼時候區信息的本地日期和時間,一樣是不可變類。
查看其源碼發現其內部就是維護了一個 LocalDate 對象和一個 LocalTime 對象來表示日期時間信息。
final LocalDate date; final LocalTime time;
因此徹底能夠把它當作是 LocalDate 和 LocalTime 的結合。
下面是一個建立 LocalDateTime 對象的例子:
LocalDateTime localDateTime1 = LocalDateTime.now(); LocalDateTime localDateTime2 =LocalDateTime.of(2018, 11, 11, 10, 55, 36, 123);
上面第二行代碼使用 of() 工廠方法建立對象,其參數分別對應年月日時分秒納秒。
其餘獲取日期時間信息和計算請參考 LocalDate 和 LocalTime 的。
java.time.ZonedDateTime 能夠用來表明世界上某個特定事件的開始,好比會議、火箭發射等等。
它一樣是不可變類,下面是一個建立此類對象的例子:
ZonedDateTime zonedDateTime = ZonedDateTime.now(); ZoneId zoneId = ZoneId.of("UTC+1"); ZonedDateTime zonedDateTime2 = ZonedDateTime.of(2015, 11, 30, 23, 45, 59, 1234, zoneId);
時區由 ZoneId 類表示,如前面的示例所示。可使用 ZoneId.now() 方法建立 ZoneId 對象。也可使用 of() 方法指定時區信息,下面是一個例子:
ZoneId zoneId1 = ZoneId.of("UTC+1"); ZoneId zoneId2 = ZoneId.of("Europe/Paris");
傳遞給 of() 方法的參數是要爲其建立 ZoneId 的時區的ID。在上面的例子中,ID 是「UTC+1」,它是 UTC (格林威治)時間的偏移量。另外也能夠直接指定具體的時區 ID 字符串,這在本文開頭有介紹。
ZonedDateTime 相比 LocalDateTime 只是多了地區信息,其內部維護了下面這 3 個變量來表示日期信息和地區:
因此其餘的方法如獲取日期時間信息和計算時間,請參考上述。
java.time.DateTimeFormatter 類用於解析和格式化用 Java 8 日期時間 API 中的類表示的日期。
DateTimeFormatter 類包含一組預約義的(常量)實例,這些實例能夠解析和格式化來自標準日期格式的日期。這省去了爲 DateTimeFormatter 定義日期格式的麻煩。包含的部分預約義實例以下:
BASIC_ISO_DATE ISO_LOCAL_DATE ISO_LOCAL_TIME ISO_LOCAL_DATE_TIME ISO_OFFSET_DATE ISO_ZONED_DATE_TIME
這些預約義的 DateTimeFormatter 實例中的每個都預先配置爲格式化和解析不一樣格式的日期。 這裏不解釋全部這些預約義的 DateTimeFormatter 實例。 能夠在 JavaDoc 中查看。
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE; String formattedDate = formatter.format(LocalDate.now()); System.out.println(formattedDate); // 20181204 String formattedZonedDate = formatter.format(ZonedDateTime.now()); System.out.println("formattedZonedDate = " + formattedZonedDate);// 20181204+0800
最後一行輸出 20181204+0800
表明 UTC+8 時區的 2019 年、第 12 個月(12 月)和第 4 天(第 4 天)。