摘要:時間與日期處理從屬于筆者的現代開發系列文章,涉及到的引用資料聲明在學習與實踐資料索引中。可以通過靜態構造方法很容易的創建,定義了與之間的轉化關系時差類以年月日來表示日期差,而以秒與毫秒來表示時間差適用于處理與機器時間。
Java 時間與日期處理Java 時間與日期處理 從屬于筆者的現代 Java 開發系列文章,涉及到的引用資料聲明在 Java 學習與實踐資料索引中。
在 Java 8 之前,我們最常見的時間與日期處理相關的類就是 Date、Calendar 以及 SimpleDateFormatter 等等。不過 java.util.Date 也是被詬病已久,它包含了日期、時間、毫秒數等眾多繁雜的信息,其內部利用午夜 12 點來區分日期,利用 1970-01-01 來計算時間;并且其月份從 0 開始計數,而且用于獲得年、月、日等信息的接口也是太不直觀。除此之外,java.util.Date 與 SimpleDateFormatter 都不是類型安全的,而 JSR-310 中的 LocalDate 與 LocalTime 等則是不變類型,更加適合于并發編程。JSR 310 實際上有兩個日期概念。第一個是 Instant,它大致對應于 java.util.Date 類,因為它代表了一個確定的時間點,即相對于標準 Java 紀元(1970年1月1日)的偏移量;但與 java.util.Date 類不同的是其精確到了納秒級別。另一個則是 LocalDate、LocalTime 以及 LocalDateTime 這樣代表了一般時區概念、易于理解的對象。
Class / Type | Description |
---|---|
Year | Represents a year. |
YearMonth | A month within a specific year. |
LocalDate | A date without an explicitly specified time zone. |
LocalTime | A time without an explicitly specified time zone. |
LocalDateTime | A combination date and time without an explicitly specified time zone. |
最新 JDBC 映射將把數據庫的日期類型和 Java 8 的新類型關聯起來:
SQL | Java |
---|---|
date | LocalDate |
time | LocalTime |
timestamp | LocalDateTime |
datetime | LocalDateTime |
GMT 即「格林威治標準時間」( Greenwich Mean Time,簡稱 G.M.T. ),指位于英國倫敦郊區的皇家格林威治天文臺的標準時間,因為本初子午線被定義為通過那里的經線。然而由于地球的不規則自轉,導致 GMT 時間有誤差,因此目前已不被當作標準時間使用。UTC 是最主要的世界時間標準,是經過平均太陽時(以格林威治時間 GMT 為準)、地軸運動修正后的新時標以及以「秒」為單位的國際原子時所綜合精算而成的時間。UTC 比 GMT 來得更加精準。其誤差值必須保持在 0.9 秒以內,若大于 0.9 秒則由位于巴黎的國際地球自轉事務中央局發布閏秒,使 UTC 與地球自轉周期一致。不過日常使用中,GMT 與 UTC 的功能與精確度是沒有差別的。協調世界時區會使用 “Z” 來表示。而在航空上,所有使用的時間劃一規定是協調世界時。而且 Z 在無線電中應讀作 “Zulu”(可參見北約音標字母),協調世界時也會被稱為 “Zulu time”。
TimeZone&UTC Offsets: 時區與偏移人們經常會把時區與 UTC 偏移量搞混,UTC 偏移量代表了某個具體的時間值與 UTC 時間之間的差異,通常用 HH:mm 形式表述。而 TimeZone 則表示某個地理區域,某個 TimeZone 中往往會包含多個偏移量,而多個時區可能在一年的某些時間有相同的偏移量。譬如 America/Chicago, America/Denver, 以及 America/Belize 在一年中不同的時間都會包含 -06:00 這個偏移。
時間戳Unix 時間戳表示當前時間到 1970 年 1 月 1 日 00:00:00 UTC 對應的秒數。注意,JavaScript 內的時間戳指的是當前時間到 1970 年 1 月 1 日 00:00:00 UTC 對應的毫秒數,和 Unix 時間戳不是一個概念,后者表示秒數,差了 1000 倍。
時間數字字符串格式 RFC2822YYYY/MM/DD HH:MM:SS ± timezone(時區用4位數字表示) // eg 1992/02/12 12:23:22+0800ISO 8601
國際標準化組織的國際標準 ISO 8601 是日期和時間的表示方法,全稱為《數據存儲和交換形式·信息交換·日期和時間的表示方法》。目前最新為第三版 ISO8601:2004,第一版為 ISO8601:1988,第二版為 ISO8601:2000。年由 4 位數組成,以公歷公元 1 年為 0001 年,以公元前 1 年為 0000 年,公元前 2 年為 -0001 年,其他以此類推。應用其他紀年法要換算成公歷,但如果發送和接受信息的雙方有共同一致同意的其他紀年法,可以自行應用。
YYYY-MM-DDThh:mm:ss ± timezone(時區用HH:MM表示) 1997-07-16T08:20:30Z // “Z”表示UTC標準時區,即"00:00",所以這里表示零時區的`1997年7月16日08時20分30秒` //轉換成位于東八區的北京時間則為`1997年7月17日16時20分30秒` 1997-07-16T19:20:30+01:00 // 表示東一區的1997年7月16日19時20秒30分,轉換成UTC標準時間的話是1997-07-16T18:20:30Z時間戳
在 Java 8 之前,我們使用 java.sql.Timestamp 來表示時間戳對象,可以通過以下方式創建與獲取對象:
// 利用系統標準時間創建 Timestamp timestamp = new Timestamp(System.currentTimeMillis()); // 從 Date 對象中創建 new Timestamp((new Date()).getTime()); // 獲取自 1970-01-01 00:00:00 GMT 以來的毫秒數 timestamp.getTime();
在 Java 8 中,即可以使用 java.time.Instant 來表示自從 1970-01-01T00:00:00Z 之后經過的標準時間:
// 基于靜態函數創建 Instant instant = Instant.now(); // 基于 Date 或者毫秒數轉換 Instant someInstant = someDate.toInstant(); Instant someInstant = Instant.ofEpochMilli(someDate.getTime()); // 基于 TimeStamp 轉換 Instant instant = timestamp.toInstant(); // 從 LocalDate 轉化而來 LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC) // 從 LocalDateTime 轉化而來 ldt.atZone(ZoneId.systemDefault()).toInstant(); // 獲取毫秒 long timeStampMillis = instant.toEpochMilli(); // 獲取秒 long timeStampSeconds = instant.getEpochSecond();
Clock 方便我們去讀取當前的日期與時間。Clock 可以根據不同的時區來進行創建,并且可以作為System.currentTimeMillis()的替代。這種指向時間軸的對象即是Instant類。Instants 可以被用于創建java.util.Date對象。
Clock clock = Clock.systemDefaultZone(); long millis = clock.millis(); Instant instant = clock.instant(); Date legacyDate = Date.from(instant); // legacy java.util.DateDate
// 默認創建 Date date0 = new Date(); // 從 TimeStamp 中創建 Date date1 = new Date(time); // 基于 Instant 創建 Date date = Date.from(instant); // 從格式化字符串中獲取 SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); java.util.Date dt=sdf.parse("2005-2-19"); // 從 LocalDateTime 中轉化而來 Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
基于 Date 的日期比較常常使用以下方式:
使用 getTime() 方法獲取兩個日期(自1970年1月1日經歷的毫秒數值),然后比較這兩個值。
使用方法 before(),after() 和 equals()。例如,一個月的12號比18號早,則 new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回true。
使用 compareTo() 方法,它是由 Comparable 接口定義的,Date 類實現了這個接口。
CalendarDate 用于記錄某一個含日期的、精確到毫秒的時間。重點在代表一剎那的時間本身。 Calendar 用于將某一日期放到歷法中的互動——時間和年、月、日、星期、上午、下午、夏令時等這些歷法規定互相作用關系和互動。我們可以通過 Calendar 內置的構造器來創建實例:
Calendar.Builder builder =new Calendar.Builder(); Calendar calendar1 = builder.build(); Date date = calendar.getTime();
在 Calendar 中我們則能夠獲得較為直觀的年月日信息:
// 2017,不再是 2017 - 1900 = 117 int year =calendar.get(Calendar.YEAR); int month=calendar.get(Calendar.MONTH)+1; int day =calendar.get(Calendar.DAY_OF_MONTH); int hour =calendar.get(Calendar.HOUR_OF_DAY); int minute =calendar.get(Calendar.MINUTE); int seconds =calendar.get(Calendar.SECOND);
除此之外,Calendar 還提供了一系列 set 方法來允許我們動態設置時間,還可以使用 add 等方法進行日期的加減。
SimpleDateFormatSimpleDateFormat 用來進行簡單的數據格式化轉化操作:
Date dNow = new Date( ); SimpleDateFormat ft = new SimpleDateFormat ("E yyyy.MM.dd "at" hh:mm:ss a zzz");LocalDateTime LocalDate
// 取當前日期: LocalDate today = LocalDate.now(); // 根據年月日取日期,12月就是12: LocalDate crischristmas = LocalDate.of(2017, 5, 15); // 根據指定格式字符串取 LocalDate endOfFeb = LocalDate.parse("2017-05-15"); // 嚴格按照ISO yyyy-MM-dd驗證,02寫成2都不行,當然也有一個重載方法允許自己定義格式 LocalDate.parse("2014-02-29"); // 無效日期無法通過:DateTimeParseException: Invalid date // 通過自定義時間字符串格式獲取 DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedDate(FormatStyle.MEDIUM) .withLocale(Locale.GERMAN); LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter); System.out.println(xmas); // 2014-12-24 // 獲取其他時區下日期 LocalDate localDate = LocalDate.now(ZoneId.of("GMT+02:30")); // 從 LocalDateTime 中獲取實例 LocalDateTime localDateTime = LocalDateTime.now(); LocalDate localDate = localDateTime.toLocalDate();日期操作
// 取本月第1天 LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth()); // 2014-12-01 // 取本月第2天 LocalDate secondDayOfThisMonth = today.withDayOfMonth(2); // 2014-12-02 // 取本月最后一天,再也不用計算是28,29,30還是31 LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth()); // 2014-12-31 // 取下一天 LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1); // 變成了2015-01-01 // 取2015年1月第一個周一 LocalDate firstMondayOf2015 = LocalDate.parse("2015-01-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2015-01-05LocalTime
// 獲取其他時區下時間 LocalTime localTime = LocalTime.now(ZoneId.of("GMT+02:30")); // 從 LocalDateTime 中獲取實例 LocalDateTime localDateTime = LocalDateTime.now(); LocalTime localTime = localDateTime.toLocalTime(); - 12:00 - 12:01:02 - 12:01:02.345LocalDateTime
// 通過時間戳創建 LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(1450073569l), TimeZone.getDefault().toZoneId()); // 通過 Date 對象創建 Date in = new Date(); LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); // 通過解析時間字符串創建 DateTimeFormatter formatter = DateTimeFormatter .ofPattern("MMM dd, yyyy - HH:mm"); LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter); String string = formatter.format(parsed); System.out.println(string); // Nov 03, 2014 - 07:13
獲取年、月、日等信息
LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59); DayOfWeek dayOfWeek = sylvester.getDayOfWeek(); System.out.println(dayOfWeek); // WEDNESDAY Month month = sylvester.getMonth(); System.out.println(month); // DECEMBER long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY); System.out.println(minuteOfDay); // 1439
時間格式化展示
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30); String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"時間操作
localDateTime.plusDays(1); localDateTime.minusHours(2);時區轉換
Timezones 以 ZoneId 來區分。可以通過靜態構造方法很容易的創建,Timezones 定義了 Instants 與 Local Dates 之間的轉化關系:
System.out.println(ZoneId.getAvailableZoneIds()); // prints all available timezone ids ZoneId zone1 = ZoneId.of("Europe/Berlin"); ZoneId zone2 = ZoneId.of("Brazil/East"); System.out.println(zone1.getRules()); System.out.println(zone2.getRules()); // ZoneRules[currentStandardOffset=+01:00] // ZoneRules[currentStandardOffset=-03:00]
LocalDateTime ldt = ... ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault()); Date output = Date.from(zdt.toInstant());
ZoneId losAngeles = ZoneId.of("America/Los_Angeles"); ZoneId berlin = ZoneId.of("Europe/Berlin"); // 2014-02-20 12:00 LocalDateTime dateTime = LocalDateTime.of(2014, 02, 20, 12, 0); // 2014-02-20 12:00, Europe/Berlin (+01:00) ZonedDateTime berlinDateTime = ZonedDateTime.of(dateTime, berlin); // 2014-02-20 03:00, America/Los_Angeles (-08:00) ZonedDateTime losAngelesDateTime = berlinDateTime.withZoneSameInstant(losAngeles); int offsetInSeconds = losAngelesDateTime.getOffset().getTotalSeconds(); // -28800 // a collection of all available zones Set時差allZoneIds = ZoneId.getAvailableZoneIds(); // using offsets LocalDateTime date = LocalDateTime.of(2013, Month.JULY, 20, 3, 30); ZoneOffset offset = ZoneOffset.of("+05:00"); // 2013-07-20 03:30 +05:00 OffsetDateTime plusFive = OffsetDateTime.of(date, offset); // 2013-07-19 20:30 -02:00 OffsetDateTime minusTwo = plusFive.withOffsetSameInstant(ZoneOffset.ofHours(-2));
Period 類以年月日來表示日期差,而 Duration 以秒與毫秒來表示時間差;Duration 適用于處理 Instant 與機器時間。
// periods LocalDate firstDate = LocalDate.of(2010, 5, 17); // 2010-05-17 LocalDate secondDate = LocalDate.of(2015, 3, 7); // 2015-03-07 Period period = Period.between(firstDate, secondDate); int days = period.getDays(); // 18 int months = period.getMonths(); // 9 int years = period.getYears(); // 4 boolean isNegative = period.isNegative(); // false Period twoMonthsAndFiveDays = Period.ofMonths(2).plusDays(5); LocalDate sixthOfJanuary = LocalDate.of(2014, 1, 6); // add two months and five days to 2014-01-06, result is 2014-03-11 LocalDate eleventhOfMarch = sixthOfJanuary.plus(twoMonthsAndFiveDays); // durations Instant firstInstant= Instant.ofEpochSecond( 1294881180 ); // 2011-01-13 01:13 Instant secondInstant = Instant.ofEpochSecond(1294708260); // 2011-01-11 01:11 Duration between = Duration.between(firstInstant, secondInstant); // negative because firstInstant is after secondInstant (-172920) long seconds = between.getSeconds(); // get absolute result in minutes (2882) long absoluteResult = between.abs().toMinutes(); // two hours in seconds (7200) long twoHoursInSeconds = Duration.ofHours(2).getSeconds();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67466.html
摘要:前言前面一篇文章寫了如何安全的使用里面介紹了如何處理日期時間,以及如何保證線程安全,及其介紹了在中的處理時間日期默認就線程安全的類。引入了全新的日期時間格式工具,線程安全而且使用方便。 前言 前面一篇文章寫了《SimpleDateFormat 如何安全的使用?》, 里面介紹了 SimpleDateFormat 如何處理日期/時間,以及如何保證線程安全,及其介紹了在 Java 8 中的處...
摘要:處理日期日歷和時間的不足之處將設定為可變類型,以及的非線程安全使其應用非常受限。最完整的日期時間,包含時區和相對或格林威治的時差。獲取當前的日期中的用于表示當天日期。 簡介 伴隨 lambda表達式、streams 以及一系列小優化,Java 8 推出了全新的日期時間API。 Java處理日期、日歷和時間的不足之處:將 java.util.Date 設定為可變類型,以及 SimpleD...
摘要:類是一個抽象類,它為特定瞬間與一組諸如等日歷字段之間的轉換提供了一些方法,并為操作日歷字段例如獲得下星期的日期提供了一些方法。是不變的日期時間對象代表一個日期時間,往往被視為年月日時分秒。獲得實例的日期和時間。 時區 GMT(Greenwich Mean Time):格林尼治時間,格林尼治標準時間的正午是指當太陽橫穿格林尼治子午線時(也就是在格林尼治上空最高點時)的時間。 UTC(U...
摘要:合并日期和時間這個復合類名叫,是和的合體。截至目前,我們介紹的這些日期時間對象都是不可修改的,這是為了更好地支持函數式編程,確保線程安全,保持領域模式一致性而做出的重大設計決定。 新的日期和時間API Java的API提供了很多有用的組件,能幫助你構建復雜的應用。不過,Java API也不總是完美的。我們相信大多數有經驗的程序員都會贊同Java 8之前的庫對日期和時間的支持就非常不理想...
摘要:上一篇文章,小樂給大家帶來了新特性之,接下來本文將會給大家介紹新特性之前言通過發布新的來進一步加強對日期與時間的處理。 上一篇文章,小樂給大家帶來了Java8新特性之Optional,接下來本文將會給大家介紹Java8新特性之Date API 前言: Java 8通過發布新的Date-Time API來進一步加強對日期與時間的處理。 舊版的 Java 中,日期時間 API 存在諸多問題...
閱讀 1370·2021-11-22 09:34
閱讀 2581·2021-11-12 10:36
閱讀 1111·2021-11-11 16:55
閱讀 2324·2020-06-22 14:43
閱讀 1457·2019-08-30 15:55
閱讀 1975·2019-08-30 15:53
閱讀 1764·2019-08-30 10:50
閱讀 1217·2019-08-29 12:15