diff --git "a/_posts/modern-java-in-action/chap12.-\354\203\210\353\241\234\354\232\264-\353\202\240\354\247\234\354\231\200-\354\213\234\352\260\204-API/2023-12-31-\353\205\270\352\262\275\353\257\274-chap12.-\354\203\210\353\241\234\354\232\264-\353\202\240\354\247\234\354\231\200-\354\213\234\352\260\204-API.md" "b/_posts/modern-java-in-action/chap12.-\354\203\210\353\241\234\354\232\264-\353\202\240\354\247\234\354\231\200-\354\213\234\352\260\204-API/2023-12-31-\353\205\270\352\262\275\353\257\274-chap12.-\354\203\210\353\241\234\354\232\264-\353\202\240\354\247\234\354\231\200-\354\213\234\352\260\204-API.md" new file mode 100644 index 0000000..e6c2845 --- /dev/null +++ "b/_posts/modern-java-in-action/chap12.-\354\203\210\353\241\234\354\232\264-\353\202\240\354\247\234\354\231\200-\354\213\234\352\260\204-API/2023-12-31-\353\205\270\352\262\275\353\257\274-chap12.-\354\203\210\353\241\234\354\232\264-\353\202\240\354\247\234\354\231\200-\354\213\234\352\260\204-API.md" @@ -0,0 +1,330 @@ +--- +title: "🦊 chap12. μƒˆλ‘œμš΄ λ‚ μ§œμ™€ μ‹œκ°„ API" +author: gengminy +date: 2023-12-31 18:40:00 +09:00 +categories: [λͺ¨λ˜ μžλ°” 인 μ•‘μ…˜, "chap12. μƒˆλ‘œμš΄ λ‚ μ§œμ™€ μ‹œκ°„ API"] +tags: [λͺ¨λ˜ μžλ°” 인 μ•‘μ…˜, JAVA, 11μ£Όμ°¨, λ…Έκ²½λ―Ό] +render_with_liquid: false +math: true +--- + +# chap12. μƒˆλ‘œμš΄ λ‚ μ§œμ™€ μ‹œκ°„ API + +# 12.1 LocalDate, LocalTime, Instant, Duration, Period 클래슀 + +### 12.1.1 LocalDate와 LocalTime μ‚¬μš© + +LocalDate μΈμŠ€ν„΄μŠ€λŠ” μ‹œκ°„μ„ μ œμ™Έν•œ λ‚ μ§œλ₯Ό ν‘œν˜„ν•˜λŠ” λΆˆλ³€ 객체닀. + +```java +LocalDate date = LocalDate.of(2017, 9, 21); +int year = date.getYear(); +Month month = date.getMonth(); +int day = date.getDayOfMonth(); +DayOfWeek dow = date.getDayOfWeek(); +int len = date.lengthOfMonth(); +boolean leap = date.isLeapYear(); +``` + +νŒ©ν† λ¦¬ λ©”μ„œλ“œ now λŠ” μ‹œμŠ€ν…œ μ‹œκ³„μ˜ 정보λ₯Ό μ΄μš©ν•΄μ„œ ν˜„μž¬ λ‚ μ§œ 정보λ₯Ό μ–»λŠ”λ‹€. + +```java +LocalDate today = LocalDate.now(); +``` + +get λ©”μ„œλ“œμ— TemporalField μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ „λ‹¬ν•΄μ„œ 정보λ₯Ό μ–»λŠ” 방법도 μžˆλ‹€. + +μ—΄κ±°μž ChronoField λŠ” TemporalField μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•œλ‹€. + +```java +int year = date.get(ChronoField.YEAR); +int month = date.get(ChronoField.MONTH_OF_YEAR); +int day = date.get(ChronoField.DAY_OF_MONTH); +``` + +λ‚΄μž₯ λ©”μ„œλ“œλ₯Ό μ΄μš©ν•΄ 가독성을 높일 μˆ˜λ„ μžˆλ‹€. + +```java +int year = date.getYear(); +int month = date.getMonthValue(); +int day = date.getDayOfMonth(); +``` + +μ‹œκ°„μ„ LocalTime 클래슀둜 ν‘œν˜„ν•  μˆ˜λ„ μžˆλ‹€. + +```java +LocalTime time = LocalTime.of(13, 45, 20); +int hour = time.getHour(); +int minute = time.getMinute(); +int second = time.getSecond(); +``` + +λ‚ μ§œμ™€ μ‹œκ°„ λ¬Έμžμ—΄λ‘œ parse 정적 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€ μˆ˜λ„ μžˆλ‹€. + +```java +LocalDate date = LocalDate.parse("2017-09-21"); +LocalTime time = LocalTime.parse("13:45:20"); +``` + +parse λ©”μ„œλ“œμ—λŠ” DateTimeFormatter λ₯Ό 전달할 μˆ˜λ„ μžˆλ‹€. + +### 12.1.2 λ‚ μ§œμ™€ μ‹œκ°„ μ‘°ν•© + +LocalDateTime 은 LocalDate 와 LocalTime 을 쌍으둜 κ°–λŠ” 볡합 ν΄λž˜μŠ€μ΄λ‹€. + +```java +LocalDateTime dt1 = LocalDateTime.of(2017, Month.SEPTEMBER, 21, 13, 45, 20); +LocalDateTime dt2 = LocalDateTime.of(date, time); +LocalDateTime dt3 = date.atTime(13, 45, 20); +LocalDateTime dt4 = date.atTime(time); +LocalDateTime dt5 = time.atDate(date); +``` + +LocalDateTime μ—μ„œ 각 μΈμŠ€ν„΄μŠ€λ₯Ό μΆ”μΆœν•  μˆ˜λ„ μžˆλ‹€. + +```java +LocalDate date1 = dt1.toLocalDate(); +LocalTime time1 = dt1.toLocalTime(); +``` + +### 12.1.3 Instant 클래슀 : κΈ°κ³„μ˜ λ‚ μ§œμ™€ μ‹œκ°„ + +κΈ°κ³„μ˜ κ΄€μ μ—μ„œλŠ” μ—°μ†λœ μ‹œκ°„μ—μ„œ νŠΉμ • 지점을 ν•˜λ‚˜μ˜ 큰 수둜 ν‘œν˜„ν•˜λŠ” 것이 κ°€μž₯ μžμ—°μŠ€λŸ¬μš΄ μ‹œκ°„ ν‘œν˜„ 방식이닀. + +`java.time.Instant` ν΄λž˜μŠ€μ—μ„œλŠ” 기계적인 κ΄€μ μ—μ„œ μ‹œκ°„μ„ ν‘œν˜„ν•œλ‹€. + +μ΄λŠ” μœ λ‹‰μŠ€ 에포크 μ‹œκ°„ `Unix epoch time, 1970-01-01 00:00:00 UTC`을 κΈ°μ€€μœΌλ‘œ νŠΉμ • μ‹œμ κΉŒμ§€μ˜ μ‹œκ°„μ„ 초둜 ν‘œν˜„ν•œλ‹€. + +```java +Instant.ofEpochSecond(3); +Instant.ofEpochSecond(3, 0); +Instant.ofEpochSecond(2, 1_000_000_000); +Instant.ofEpochSecond(4, -1_000_000_000); +``` + +Instant ν΄λž˜μŠ€λ„ μ‚¬λžŒμ΄ 확인할 수 μžˆλ„λ‘ μ‹œκ°„μ„ ν‘œμ‹œν•΄μ£ΌλŠ” 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œ now λ₯Ό μ œκ³΅ν•œλ‹€. + +Instant λŠ” μ΄ˆμ™€ λ‚˜λ…Έμ΄ˆ 정보λ₯Ό ν¬ν•¨ν•œλ‹€. + +이λ₯Ό 읽기 μœ„ν•΄ Duration κ³Ό Period 클래슀λ₯Ό ν™œμš©ν•  수 μžˆλ‹€. + +### 12.1.4 Duration κ³Ό Period μ •μ˜ + +μ§€κΈˆκΉŒμ§€μ˜ λͺ¨λ“  ν΄λž˜μŠ€λŠ” Temporal μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œλ‹€. + +Duration 클래슀의 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œ between 으둜 두 μ‹œκ°„ 객체 μ‚¬μ΄μ˜ μ§€μ†μ‹œκ°„μ„ λ§Œλ“€ 수 μžˆλ‹€. + +```java +Duration d1 = Duration.between(time1, time2); +Duration d2 = Duration.between(dateTime1, dateTime2); +Duration d3 = Duration.between(instant1, instant2); +``` + +λ‹€λ§Œ LocalDateTime 은 μ‚¬λžŒμ΄ μ‚¬μš©ν•˜λ„λ‘, Instant λŠ” 기계가 μ‚¬μš©ν•˜λ„λ‘ λ§Œλ“€μ–΄μ§„ 클래슀둜 + +두 μΈμŠ€ν„΄μŠ€λŠ” μ„œλ‘œ ν˜Όν•©ν•  수 μ—†λ‹€. + +λ…„, μ›” 일둜 μ‹œκ°„μ„ ν‘œν˜„ν•  λ•ŒλŠ” Period 클래슀λ₯Ό μ‚¬μš©ν•œλ‹€. + +```java +Period tenDays = Period.between(LocalDate.of(2017, 9, 11), + LocalDate.of(2017, 9, 21)); +``` + +Duration κ³Ό Period ν΄λž˜μŠ€λŠ” μžμ‹ μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€ 수 μžˆλ„λ‘ λ‹€μ–‘ν•œ νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€. + +```java +Duration threeMinutes = Duration.ofMinutes(3); +Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); + +Period tenDays = Period.ofDays(10); +Period threeWeeks = Period.ofWeeks(3); +Period twoYearsSixMonthsOnday = Period.of(2, 6, 1); +``` + +μ§€κΈˆκΉŒμ§€μ˜ λͺ¨λ“  ν΄λž˜μŠ€λŠ” λΆˆλ³€ ν΄λž˜μŠ€μ΄λ‹€. + +# 12.2 λ‚ μ§œ μ‘°μ •, νŒŒμ‹±, ν¬λ§€νŒ… + +`withArrtibute` λ©”μ„œλ“œλ‘œ 기쑴의 LocalDate λ₯Ό λ°”κΎΌ 버전을 직접 κ°„λ‹¨ν•˜κ²Œ λ§Œλ“€ 수 μžˆλ‹€. + +```java +LocalDate date1 = LocalDate.of(2017, 9, 21); +LocalDate date2 = date1.withYear(2011); +LocalDate date3 = date2.withDayOfMonth(25); +LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 2); +``` + +Temproal μΈν„°νŽ˜μ΄μŠ€λŠ” LocalDate, LocalTime, LocalDateTime, Instant 처럼 νŠΉμ • μ‹œκ°„μ„ μ •μ˜ν•˜κ³  + +get κ³Ό with λ©”μ„œλ“œλ‘œ Temporal 객체의 ν•„λ“œκ°’μ„ μ½κ±°λ‚˜ κ³ μΉ  수 μžˆλ‹€. + +λ§Œμ•½ μ§€μ •λœ ν•„λ“œλ₯Ό μ§€μ›ν•˜μ§€ μ•ŠμœΌλ©΄ `UnsupportedTemporalTypeException` 이 λ°œμƒν•œλ‹€. + +μ„ μ–Έν˜•μœΌλ‘œ LocalDate λ₯Ό μ‚¬μš©ν•˜λŠ” 방법도 μžˆλ‹€. + +```java +LocalDate date1 = LcoalDate.of(2017, 9, 21); +LocalDate date2 = date1.plusWeeks(1); +LocalDate date3 = date2.minusYears(6); +LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS); +``` + +plus, minus λ©”μ„œλ“œλ„ Temporal μΈν„°νŽ˜μ΄μŠ€μ— μ •μ˜λ˜μ–΄ μžˆλ‹€. + +이λ₯Ό ν™œμš©ν•΄ νŠΉμ • μ‹œκ°„λ§ŒνΌ μ•žλ’€λ‘œ μ΄λ™μ‹œν‚¬ 수 μžˆλ‹€. + +μˆ«μžμ™€ TemporalUnit 을 인수둜 ν™œμš©ν•  수 μžˆλ‹€. + +### 12.2.1 TemporalAdjusters μ‚¬μš©ν•˜κΈ° + +`TemporalAdjuster` λŠ” with λ©”μ„œλ“œμ— μ’€ 더 λ‹€μ–‘ν•œ λ™μž‘μ„ μˆ˜ν–‰ν•  수 μžˆλ„λ‘ ν•˜λŠ” κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€. + +```java +import static java.time.tempoal.TemporalAdjusters.*; + +LocalDate date1 = LocalDate.of(2014, 3, 18); +LocalDate date2 = date1.with(nextOfSame(DayOfWeek.SUNDAY)); +LocalDate date3 = date2.with(lastDayOfMonth()); +``` + +ν•„μš”ν•œ κΈ°λŠ₯이 μ •μ˜λ˜μ–΄ μžˆμ§€ μ•Šμ„ λ•ŒλŠ” 비ꡐ적 μ‰½κ²Œ μ»€μŠ€ν…€ TemporalAdjuster κ΅¬ν˜„μ„ λ§Œλ“€ 수 μžˆλ‹€. + +```java +@FunctionalInterface +public interface TemporalAdjuster { + Temporal adjustInto(Temporal temporal); +} +``` + +TemporalAdjuster μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„μ€ Temporal 객체λ₯Ό μ–΄λ–»κ²Œ λ‹€λ₯Έ Temporal 객체둜 λ³€ν™˜ν•  지 μ •μ˜ν•œλ‹€. + +### 12.2.2 λ‚ μ§œμ™€ μ‹œκ°„ 객체 좜λ ₯κ³Ό νŒŒμ‹± + +ν¬λ§€νŒ…κ³Ό νŒŒμ‹± μ „μš© νŒ¨ν‚€μ§€μΈ `java.time.format` 이 μΆ”κ°€λ˜μ—ˆλ‹€. + +κ°€μž₯ μ€‘μš”ν•œ ν΄λž˜μŠ€λŠ” `DateTimeFormatter` 이닀. + +이λ₯Ό μ΄μš©ν•΄μ„œ λ‚ μ§œλ‚˜ μ‹œκ°„μ„ νŠΉμ • ν˜•μ‹μ˜ λ¬Έμžμ—΄λ‘œ λ§Œλ“€ 수 μžˆλ‹€. + +```java +LocalDate date = LocalDate.of(2014, 3, 18); +String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); // 20140318 +String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2014-03-18 +``` + +λ°˜λŒ€λ‘œ λ¬Έμžμ—΄μ„ νŒŒμ‹±ν•΄μ„œ λ‚ μ§œ 객체λ₯Ό λ‹€μ‹œ λ§Œλ“€ 수 μžˆλ‹€. + +```java +LocalDate date1 = LocalDate.parse("20140318", DateTimeFormatter.BASIC_ISO_DATE); +LocalDate date2 = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE); +``` + +κΈ°μ‘΄ `java.util.DateFormat` κ³Ό 달리 λͺ¨λ“  `DateTimeFormatter` λŠ” μŠ€λ ˆλ“œ μ•ˆμ „ν•œ ν΄λž˜μŠ€μ΄λ‹€. + +λ˜ν•œ νŠΉμ • νŒ¨ν„΄μœΌλ‘œ 포맀터λ₯Ό λ§Œλ“€ 수 μžˆλŠ” 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ„ μ œκ³΅ν•œλ‹€. + +```java +DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); +LocalDate date1 = LocalDate.of(2014, 3, 18); +String formattedDate = date1.format(formatter); +LocalDate date2 = LocalDate.parse(formattedDate, formatter); +``` + +`ofPattern` λ©”μ„œλ“œλ„ Locale 둜 포맀터λ₯Ό λ§Œλ“€ 수 μžˆλ„λ‘ μ˜€λ²„λ‘œλ“œλœ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€. + +```java +DateTimeFormatter italianFormatter = + DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN); +... +``` + +DateTimeFormatterBuilder 클래슀둜 볡합적인 포맀터λ₯Ό μ •μ˜ν•΄μ„œ μ„ΈλΆ€μ μœΌλ‘œ μ œμ–΄ν•  μˆ˜λ„ μžˆλ‹€. + +```java +DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder() + .appendText(ChronoField.DAY_OF_MONTH) + .appendLiteral(". ") + .appendText(ChronoField.MONTH_OF_YEAR) + .appendLiteral(" ") + .appendText(ChronoField.YEAR) + .parseCaseInsensitive() + .toFormatter(Locale.ITALIAN); +``` + +# 12.3 λ‹€μ–‘ν•œ μ‹œκ°„λŒ€μ™€ μΊ˜λ¦°ν„° ν™œμš© 방법 + +기쑴의 `java.util.TimeZone` 을 λŒ€μ²΄ν•  수 μžˆλŠ” `java.time.ZoneId` ν΄λž˜μŠ€κ°€ μƒˆλ‘­κ²Œ λ“±μž₯ν–ˆλ‹€. + +이 클래슀λ₯Ό μ΄μš©ν•˜λ©΄ μ¨λ¨Ένƒ€μž„(DST) 같은 λ³΅μž‘ν•œ 사항이 μžλ™μœΌλ‘œ μ²˜λ¦¬λœλ‹€. + +ZoneId ν΄λž˜μŠ€λŠ” λΆˆλ³€μ΄λ‹€. + +### 12.3.1 μ‹œκ°„λŒ€ μ‚¬μš©ν•˜κΈ° + +`ZoneRules` ν΄λž˜μŠ€μ—λŠ” μ•½ 40개 μ •λ„μ˜ μ‹œκ°„λŒ€κ°€ 있고, getRules() λ₯Ό μ΄μš©ν•΄ ν•΄λ‹Ή μ‹œκ°„λŒ€μ˜ κ·œμ •μ„ νšλ“ν•  수 μžˆλ‹€. + +```java +ZoneId romeZone = ZoneId.of("Europe/Rome"); +``` + +지역 ID λŠ” `지역/λ„μ‹œ` ν˜•μ‹μœΌλ‘œ 이루어지고 `IANA Time Zone Datebase` μ—μ„œ μ œκ³΅ν•˜λŠ” 지역 집합 정보λ₯Ό μ‚¬μš©ν•œλ‹€. + +ZoneId 객체λ₯Ό 얻은 λ‹€μŒμ—λŠ” LocalDate, LocalDateTime, Instant λ₯Ό μ΄μš©ν•΄μ„œ ZoneDateTime μΈμŠ€ν„΄μŠ€λ‘œ λ³€ν™˜ν•  수 μžˆλ‹€. + +```java +LocalDate date = LocalDate.of(2014, Month.MARCH, 18); +ZonedDateTime zdt1 = date.atStartOfDay(romeZone); +LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); +ZonedDateTime zdt2 = dateTime.atZone(romeZone); +Instant instant = Instant.now(); +ZonedDateTime zdt3 = instant.atZone(romeZone); +``` + +ZoneId λ₯Ό μ΄μš©ν•΄μ„œ LocalDateTime 을 Instant 둜 λ°”κΏ€ 수 μžˆλ‹€. + +```java +Instant instant = Instant.now(); +LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone); +``` + +### 12.3.2 UTC/Greenwich κΈ°μ€€μ˜ κ³ μ • μ˜€ν”„μ…‹ + +λ•Œλ‘œλŠ” ν˜‘μ • μ„Έκ³„μ‹œ(UTC)/κ·Έλ¦¬λ‹ˆμΉ˜ ν‘œμ€€μ‹œ(GMT) λ₯Ό κΈ°μ€€μœΌλ‘œ ν‘œν˜„ν•˜κΈ°λ„ ν•œλ‹€. + +ZoneId 의 μ„œλΈŒν΄λž˜μŠ€μΈ ZoneOffset 클래슀둜 런던의 κ·Έλ¦¬λ‹ˆμΉ˜ 0도 μžμ˜€μ„ κ³Ό μ‹œκ°„κ°’μ˜ 차이λ₯Ό ν‘œν˜„ν•  수 μžˆλ‹€. + +```java +ZoneOffset newYorkOffset = ZoneOffset.of("-05:00"); +``` + +μ΄λŠ” μ„œλ¨Ένƒ€μž„μ„ μ œλŒ€λ‘œ μ²˜λ¦¬ν•  수 μ—†μ–΄ ꢌμž₯ν•˜μ§€ μ•ŠλŠ”λ‹€. + +ISO-8601 μΊ˜λ¦°λ” μ‹œμŠ€ν…œμ—μ„œ μ •μ˜ν•˜λŠ” UTC/GMT 와 μ˜€ν”„μ…‹μœΌλ‘œ λ‚ μ§œμ™€ μ‹œκ°„μ„ ν‘œν˜„ν•˜λŠ” OffsetDateTime 을 λ§Œλ“œλŠ” 방법도 μžˆλ‹€. + +```java +LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); +OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(date, newYorkOffset); +``` + +### 12.3.3 λŒ€μ•ˆ μΊ˜λ¦°λ” μ‹œμŠ€ν…œ μ‚¬μš©ν•˜κΈ° + +μžλ°” 8μ—μ„œλŠ” μΆ”κ°€λ‘œ 4개의 μΊ˜λ¦°λ” μ‹œμŠ€ν…œμ„ μ œκ³΅ν•œλ‹€. + +ThaiBuddhistDate, MinguoDate, JapaneseDate, HijrahDate 이닀. + +이 ν΄λž˜μŠ€μ™€ LocalDate ν΄λž˜μŠ€λŠ” ChronoLocalDAte μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ”λ° μ΄λŠ” μž„μ˜μ˜ μ—°λŒ€κΈ°μ—μ„œ νŠΉμ • λ‚ μ§œλ₯Ό ν‘œν˜„ν•  수 μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€μ΄λ‹€. + +```java +LocalDate date = LocalDate.of(2014, Month.MARCH, 18); +JapaneseDate japaneseDate = JapaneseDate.from(date); +``` + +νŠΉμ • Locale κ³Ό Locale 에 λŒ€ν•œ λ‚ μ§œ μΈμŠ€ν„΄μŠ€λ‘œ μΊ˜λ¦°λ” μ‹œμŠ€ν…œμ„ λ§Œλ“€μˆ˜λ„ μžˆλ‹€. + +```java +Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN); +ChronoLocalDate now = japaneseChronology.dateNow(); +``` + +λ‚ μ§œμ™€ μ‹œκ°„ API 의 μ„€κ³„μžλŠ” ChronoLocalDate λ³΄λ‹€λŠ” LocalDate λ₯Ό μ‚¬μš©ν•˜λΌκ³  κΆŒκ³ ν•œλ‹€.