首頁 > Java > java教程 > 主體

讓我們正確地進行日期

王林
發布: 2024-08-05 20:17:30
原創
1066 人瀏覽過

Lets do dates properly

作為初學者,您將學到的最有趣的主題之一是日期。儘管這聽起來有點無聊,但這是您可能知道的關鍵事情之一;從資料庫、API、GUI 等中學習如何正確處理日期是編寫優秀應用程式的關鍵因素。

在我們開始之前

您必須記住的一個重要細節是 Java 被急於應用於網絡,因此您有很多奇怪且有些愚蠢的方法來做事情。本指南的目標是教您如何使用最新的 Java API 編寫高效的優質程式碼。我的目標是教您如何不編寫任何困難的程式碼,而是使用標準化 ISO 來實現可擴展的應用程式。

如何使用日期

日期 API

從 Java 8 SE 開始,Date 類別的發布定義為:

Date 類別表示特定的時刻,精確度為毫秒。

此外,我們還加入了抽像日曆類別(JDK 1.1 進行了重大升級),定義為:

Calendar 類別是一個抽象類,它提供了在特定時刻和一組日曆字段(如YEAR、MONTH、DAY_OF_MONTH、HOUR 等)之間進行轉換的方法,以及操作日曆字段的方法,如獲取下週的日期。時間上的瞬間可以用毫秒值來表示,該值是距 紀元,1970 年 1 月 1 日 00:00:00.000 GMT(公曆)的偏移量。

如 Oracle 文件所述,原因是缺乏國際化能力。

值得注意的是,如果您有興趣的話,上面連結的相同文件會進一步詳細介紹其時間安排。

本地日期 API

Java 8 SE 也引進了 LocalDate 類,稱為:

ISO-8601 日曆系統中沒有時區的日期,例如 2007-12-03。
LocalDate 是一個不可變的日期時間對象,表示日期,通常被視為年月日。還可以存取其他日期字段,例如一年中的某一天、一周中的某一天和一年中的星期。例如,值「2nd October 2007」可以儲存在 LocalDate 中。

需要注意的一個重要規範是:

此類不儲存或表示時間或時區。相反,它是對日期的描述,如生日。如果沒有偏移量或時區等附加資訊,它無法表示時間線上的瞬間。

區別以及使用哪一個

日期/時間 API 的問題

進一步閱讀 LocalDate 類別將引導您了解「這個類別是不可變的且線程安全的。」這給我們帶來了第一個問題。

  • 日期和日曆都不是線程安全的。如果您對它的含義有點困惑 - 線程是一種與計算機應用程式實現並發的方法,它允許多個任務同時運行而不是順序運行(如果您有編程背景,您可以將其視為非同步) 。
  • 日期/日曆 API 的另一個問題是其糟糕的設計。後來,它被修改為以 ISO 為中心(如果您對 ISO 的整個內容有點困惑;簡而言之,ISO 代表「國際標準化組織」),稍後我們將看到這樣做的額外好處。
  • 最後回到缺乏國際化的問題,開發人員需要編寫處理不同時區的邏輯,這可能會導致許多錯誤匹配。

實際差異

最大的區別是,儘管名稱 Date 會同時存儲時間和日期(正如文檔中提到的,自紀元以來有毫秒偏移量 - 我們稍後會討論紀元)。借助更新的 API,我們可以更輕鬆地格式化/解析/操作日期。

如果我也想儲存時間怎麼辦?

Java 也透過其 LocalDateTime 類別為您提供了幫助

ISO-8601 日曆系統中沒有時區的日期時間,例如 2007-12-03T10:15:30。
LocalDateTime 是一個不可變的日期時間對象,表示日期時間,通常被視為年-月-日-時-分-秒。還可以存取其他日期和時間字段,例如一年中的某一天、一周中的某一天和一年中的星期。時間以奈秒精度表示。例如,值「2nd October 2007 at 13:45.30.123456789」可以儲存在 LocalDateTime 中。

如果您只想儲存時間那麼您可以使用LocalTime類別

快速回顧和用例

Name Should Use Use Case Example
LocalDate Interprating Dates 2021-02-28
LocalTime Interprating Time 19:32:25.457826
LocalDateTime Interprating Time/Date 2021-02-28T19:32:25.457826.
Date prefer not Represents Time Tue Jul 12 18:35:37 IST 2016

本地日期

現在我們已經很好地了解了它的背景以及我們可以用它做什麼,讓我們來探索 LocalDate 類別的方法。

ISO 8601

ISO-8601 日曆系統是當今世界大部分地區使用的現代民用日曆系統。它相當於提前公曆系統,其中今天的閏年規則始終適用。對於當今編寫的大多數應用程式來說,ISO-8601 規則完全適合。然而,任何使用歷史日期並要求它們準確的應用程式都會發現 ISO-8601 方法不適合。

這是標準 Java 將用來正確格式化我們的日期。

在我們深入研究之前,我想向您介紹 Temporal 對象,所有新的 Date API 都實現了該對象(LocalDate、LocalTime、LocalDateTime),它的定義為:

這是日期、時間和偏移量物件的基本介面類型,它們足夠完整,可以使用加號和減號進行操作。它由那些可以提供和操作資訊作為字段或查詢的類別來實現。請參閱 TemporalAccessor 以了解此介面的唯讀版本。

不深入討論這個只是想讓大家在遇到它時明白這是什麼。

方法

其格式將按順序排列 - 方法名稱、指定的返回描述以及其功能的快速摘要/或深入研究。如果你想跟著我做,我將使用 jshell 只需導入 java.time.LocalDate 並複製相同的方法。

LocalDate.now(): > >使用系統時鐘和預設時區的目前日期,不為空

正如描述中所提到的,它將根據時區返回當前日期格式,例如我給出的日期格式為2024-07-10,但是格式可能會根據您的區域設置時間而變化,具體取決於為您所在國家/地區分配的ISO 8601 格式(例如,可能是2024.07.10 或24/7/10,這是您所在國家/地區使用的標準)

LocalDate.now() 方法可以接受一個參數。要使用它,我們將導入 java.time.ZoneId.

什麼是ZoneId?

ZoneId 隨 Java 8SE 一起發布,定義為:

ZoneId 用來識別 Instant 和 LocalDateTime 之間轉換的規則。 ID 有兩種不同類型:

  • 固定偏移量 - 與 UTC/格林威治的完全解析偏移量,對所有本地日期時間使用相同的偏移量
  • 地理區域 - 應用一組特定規則來尋找與 UTC/格林威治的偏移量的區域

簡單來說,這解決了我們使用舊 Date API 時遇到的國際化問題。導入 java.time.ZoneId 後,我們可以看看它提供的一些方法。

  • ZoneId.getAvailableZoneIds() 向我們顯示所有可用區域,例如:歐洲/伊斯坦布爾、美洲/埃魯內佩、Etc/GMT-4、美洲/密克隆群島(請記住,其中每一個都是唯一的,我們有兩種類型的唯一ID上面列出了)。
  • ZoneId.systemDefault() 方法定義為「如果系統預設時區更改,則該方法的結果也會更改。」這是不言自明的。
  • ZoneId.of("") 我們使用它來從 .getAvailableZoneIds() 清單中定義一個自訂的 ZoneId 集。請記住,此處作為範例的 UTC 和歐洲/伊斯坦堡都可以使用。
  • ZoneId.from() 還記得我們之前討論過的時間嗎?在這裡我們將它作為參數傳遞。如果您不完全確定它的作用,給定一個代表任意日期和時間資訊集的 ==TemporalAccessor,我們將獲得一個 ZoneId。如果您仍然有點無能為力,您可以匯入 java.time.ZoneId 並使用 ZonedDateTime.now() ,這將為您提供精確的時間/日期,然後您可以將其解析為 ZoneId 。基本上 ZoneId.from(ZonedDateTime.now())

您也可以傳遞一個 Clock 參數,但當您可以只使用 ZoneID 但可以隨意探索時,我認為它沒有任何用處

現在我們知道了 - 讓我們獲取巴黎的當前日期!

LocalDate.now(ZoneId.of("Europe/Paris")) // 2024-07-10
登入後複製
LocalDate.of(年、月、日): >從年、月、日取得 LocalDate 的實例。

記住 LocalDate 代表一個日期。在這種情況下,我們可以將該日期設定為我們想要的任何年、月、日!

LocalDate.ofYearDay(year, dayOfYear): >

在很多情況下,你可能只有一年零一天。此函數將其轉換為年-月-日格式。下面是一個範例 LocalDate.ofYearDay(2023, 234) 將會是 2023-08-22。閏年也將被考慮。

LocalDate.parse(text) : > Obtains an instance of LocalDate from a text string using a specific format.

Basically, if we generate a String Date format like 2023-08-22 this will convert it into a LocalDate format

Epoch

There’s one last method I would like to talk about and even though it might not be that useful, it's a good thing to know.
Epoch in simple terms is a date set to the start. Virtually means it's “The start of time”. For example UNIX epoch is set to 1970/1/1 This is when all Unix time is calculated. Now this is not standardized between all computers. Most programming languages will use the UNIX epoch but not between all devices. For example NTP epoch is 1900/1/1. Now you might be asking why. And the simple answer is I don’t know. Unix was developed in 1969 and released in 71 but I guess the developers found 70 easier to work with and now that’s what we use!

LocalDate.ofEpochDay(epochDay) : >

Just going based on the description I gave about the output of this would be X days from 1970/1/1. Given epochDay is 1 => 1970-01-02 and so on.

~
~
~

By now I think you guys are all bored of all this nerdy talk and most likely lost interest completely but we are going to make it way more interesting in just a bit.
LocalDate methods have a ton of subsets to modify dates as we talked about above. Explaining all of these is just a waste of time. The best thing is for you guys to check ’em out and play around a bit, they are all very simple to understand.

Locale

The main idea of this whole article was to teach you internationalization and we are going to start that now.
If you recall above I talked about how LocalDate.now() might show different numbers/punctuations and this is standardized by Locale Class

A Locale object represents a specific geographical, political, or cultural region. An operation that requires a Locale to perform its task is called locale-sensitive and uses the Locale to tailor information for the user. For example, displaying a number is a locale-sensitive operation— the number should be formatted according to the customs and conventions of the user's native country, region, or culture.

Again the Locale class uses pre-defined ISO standards that Oracle has included.

As I said - In the early days of the web Java was rushed and we can really see that here.

Locale Constant & Why not to use

If you import java.util.Locale you can see that we have a few constants. Locale.CANADA as an example. If you're wondering what these are and why they were picked, it's quite simple. Due to the reason mentioned above, the Java devs just picked some Locale constants to support and you should not use them. Now if you look closely we have a Locale.French
and a Locale.France and you might be confused, what is this?? I’ll talk about it in a second

Now how do we go about using these Locales? By using Locale.availableLocales() you will see a large subset of available locales that you can use. you might see it in the format of en_US and you might get confused. Following up on the question listed above Java has accounted for different language subsets used in different countries. In this France/French example, We can refer to Canadian French versus French used in France. They are not entirely the same. Now a country like Iran will use Persian no matter the place, so you could just use Locale.of(language) to specify the Locale, But for French you could rather use Locale.of(language, country). Remember if you split what you got from Locale.availableLocales() by the _ you can just format it like that. An example would be Locale.of("en", "US") for en_US

Now let's implement this into our time for our different users around the globe.

System.out.println(
LocalDate.now().format(

DateTimeFormatter

.ofLocalizedDate(FormatStyle.SHORT)

.localizedBy(Locale.GERMAN)));
登入後複製

Now don’t mind the extra code, and even me using Locale.GERMAN cause honestly I was looking for a different output and the easiest way was to use Javas constants. I landed on Locale.GERMAN which outputs 10.07.24 as opposed to what Japan uses (2024/07/10).

Why not use Locale.of()

Now it might sound weird, but first of all i tell you not to use constants and prefer Locale.of(), then I tell you not to use Locale.of() an important factor to consider is Locale.of() was introduced with Java 19 which goes against supporting Java 8 SE to combat that from now on we will use

new Locale.Builder()
            .setLanguage("language")
            .setRegion("country").build();
登入後複製

Now another thing you could use is Locale.setDefault(language, country) which will work as well.

DateTimeFormatter

Now looking at the code above you might recall seeing DateTimeFormatter defined by docs as:

Formatter for printing and parsing date-time objects.

LocalDate.format()

As I mentioned above, one of the key features of Date/Time Local* APIs was formatting. And this is how we are going to format our dates. Looking at the code above - everything should make sense apart from the .ofLocalizedDate() method. The easiest way to show what it does is with an example.

LocalDate anotherSummerDay = LocalDate.of(2016, 8, 23);
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(anotherSummerDay));
登入後複製

will output the following in order:

Tuesday, August 23, 2016
August 23, 2016
Aug 23, 2016
8/23/16
登入後複製

Now sometimes you might want to have your own pattern which you can easily do with

System.out.println(

LocalDate.now().format(

DateTimeFormatter

.ofPattern("dd-uuuu-MMM")));
登入後複製

Now I’ll quickly go over what these symbols mean:
dd => It's a representation of the day field, now you could also use d and the difference would be that d would not have a fixed character limit since it represents 1 digit from 0-9 and 2 from 10-31. if you're lost it basically means dd would show 01; however d would show 1. However, going into two digits it's the same
uuuu/yyyy => Is the field for years. And you could also do yy which would show only the last two digits of the year. ex. 24 rather than 2024
MMM => The last field as you might have guessed is for the month - MMM tells Java that we want a String representation rather than a number. If you want a number use MM

So by now, you should’ve guessed that the output would be:

10-2024-Jul
登入後複製

Instants

Now that you have a firm grip on Dates. I want to introduce you to Instants defined as:

An instantaneous point on the timeline.

Basically, this refers to Instants showing a specific moment.
By now you might have assumed that LocalDateTime has a timezone, but it doesn’t as the docs mentioned they are just a representation of date/time. However a ZoneId is in fact a representation of a time zone. Also, note that Instants don’t provide methods for altering our dates and times.

Instants capture the current moment in UTC with the Instant.now() . Now based on this, you realize that Local* really doesn’t have a meaning unless it's applied, that’s the reason why you would actually refrain from using it in a business-level application. So prefer using ZonedDateTime and Insant. As a general rule of thumb use Locale When it's a specific point on a timeline rather than a moment in time. A moment would be like the moment I publish this, or the moment you read this, or the moment the world explodes, however a point in a timeline would be like an appointment there’s no moment for an appointment. If you're going on a vacation it's a specific time on a timeline, not a moment.

Whats ZonedDateTime?

Quite simple. It's just an Instant with a ZoneId and this is why we say Insant has a timezone. No matter what you do it's not going to represent a time but rather be a specific moment.

Aigh’t well that was quite the thing, wasn’t it? phew
have a nice one >#

以上是讓我們正確地進行日期的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!