본문 바로가기
Programming Language 이해하기/Java 이해하기

알아야만 하는 Java.time API 총 정리[실무&고급편]

by simplify-len 2020. 9. 20.

1. 날짜와 시간 조작하기

1.1. 날짜와 시간을 더하거나 빼는 메소드

LocalDate, LocalDateTime, ZonedDateTime 클래스는 각각 년, 월, 일, 주를 더하거나 뺄 수 있습니다.

LocalDateTime currentDateTime = LocalDateTime.now();
LocalDateTime targetDateTime = currentDateTime
        .plusYears(long)       // 년도 더하기
        .minusYears(long)      // 년도 빼기
        .plusMonths(long)      // 월 더하기 
        .minusMonths(long)     // 월 빼기
        .plusDays(long)        // 일 더하기 
        .minusDays(long)       // 일 빼기
        .plusWeeks(long)       // 주 더하기
        .minusWeeks(long);     // 주 빼기

LocalTime, LocalDateTime, ZonedDateTime 클래스는 각각 시간, 분, 초, 나노초를 더하거나 뺄 수 있습니다.

LocalDateTime targetDateTime2 = currentDateTime
        .plusHours(long)       // 시간 더하기
        .minusHours(long)      // 시간 빼기
        .plusMinutes(long)     // 분 더하기 
        .minusMinutes(long)    // 분 빼기
        .plusSeconds(long)     // 초 더하기 
        .minusSeconds(long)    // 초 빼기
        .plusNanos(long)       // 나노초 더하기
        .minusNanos(long);     // 나노초 빼기

1.2. 날짜와 시간을 변경하는 메소드

LocalDate, LocalDateTime, ZonedDateTime 클래스는 각각 년, 월, 월의 몇 번째 일인지, 년의 몇번 번째 일인지를 변경할 수 있습니다.

LocalDateTime targetDateTime3 = currentDateTime
                .withYear(int)          // 년도를 변경
                .withMonth(int)         // 월 변경
                .withDayOfMonth(int)    // 월의 일을 변경
                .withDayOfYear(int);    // 년도의 일을 변경
                .with(TemporalAdjuster adjuster) // 현재 날짜를 기준으로 상대적인 날짜로 변경

마지막에 .with(TemporalAdjuster adjuster) 메소드를 사용하면 현재 날짜를 기준으로 년도의 첫 번째 일, 마지막 일, 월의 첫 번째 일, 마지막 일, 지난 요일 및, 돌아오는 요일 등 상대적인 날짜로 변경을 할 수 있습니다.

LocalDateTime targetDateTime4 = currentDateTime
        .with(TemporalAdjusters.firstDayOfYear())       // 이번 년도의 첫 번째 일(1월 1일)
        .with(TemporalAdjusters.lastDayOfYear())        // 이번 년도의 마지막 일(12월 31일)
        .with(TemporalAdjusters.firstDayOfNextYear())   // 다음 년도의 첫 번째 일(1월 1일)
        .with(TemporalAdjusters.firstDayOfMonth())      // 이번 달의 첫 번째 일(1일)
        .with(TemporalAdjusters.lastDayOfMonth())       // 이번 달의 마지막 일
        .with(TemporalAdjusters.firstDayOfNextMonth())  // 다음 달의 첫 번째 일(1일)
        .with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)) // 이번 달의 첫 번째 요일(여기서는 월요일)
        .with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY))  // 이번 달의 마지막 요일(여기서는 마지막 금요일)
        .with(TemporalAdjusters.next(DayOfWeek.FRIDAY))       // 다음주 금요일
        .with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY)) // 다음주 금요일(오늘 포함. 오늘이 금요일이라면 오늘 날짜가 표시 된다.)
        .with(TemporalAdjusters.previous(DayOfWeek.FRIDAY))     // 지난주 금요일
        .with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY));// 지난주 금요일(오늘 포함)

LocalTime, LocalDateTime, ZonedDateTime 클래스는 각각 시간, 분, 초, 나노초를 변경할 수 있습니다.

LocalDateTime localdatetime = currentDateTime
        .withHour(int)      // 시간 변경
        .withMinute(int)    // 분 변경
        .withSecond(int)    // 초 변경
        .withNano(int);     // 나노초 변경

1.3 날짜와 시간을 비교하는 메소드

1.3.1. 날짜 비교하기

LocalDate, LocalDateTime은 아래와 같이 isBefore(ChronoLocalDate other), isEqual(ChronoLocalDate other), isAfter(ChronoLocalDate other) 메소드를 사용해 날짜를 비교할 수 있습니다. 리턴 타입은 boolean입니다.

LocalDateTime startDateTime = LocalDateTime.now();  // 2016-04-01T23:39:57.008
LocalDateTime endDateTime = LocalDateTime.of(2016, 4, 1, 23, 59, 59);

// startDateTime이 endDateTime 보다 이전 날짜 인지 비교
startDateTime.isBefore(endDateTime);    // true

// 동일 날짜인지 비교
startDateTime.isEqual(endDateTime); // false

// startDateTime이 endDateTime 보다 이후 날짜인지 비교
startDateTime.isAfter(endDateTime); // false

1.3.2. 시간 비교하기

LocalTime과 LocalDateTime은 isBefore(LocalTime other), isAfter(LocalTime other) 메소드를 사용해 시간을 비교할 수 있습니다. 리턴 타입은 boolean있습니다.

LocalTime startTime = LocalTime.now();  // 23:52:35.603
LocalTime endTime = LocalTime.of(23, 59, 59);

// startTime이 endTime 보다 이전 시간 인지 비교
startTime.isBefore(endTime);    // true

// startTime이 endTime 보다 이후 시간 인지 비교
startTime.isAfter(endTime); // false

1.4 날짜 차이 계산하기

LocalDate는 until(ChronoLocalDate endDate) 메소드를 사용해서 날짜 차이를 계산할 수 있으며, 리턴 타입은 java.time.Period 이다. Period 클래스의 getYears(), getMonths(), getDays() 메소드를 사용해서 년도 차이, 월의 차이, 일의 차이를 계산 할 수 있습니다.

LocalDate currentDate = LocalDate.now(); // 2016-04-02
LocalDate targetDate = LocalDate.of(2016,5,5);

Period period = currentDate.until(targetDate);

period.getYears();      // 0년
period.getMonths();     // 1개월
period.getDays();       // 3일 차이

또한 Period 클래스의 between(LocalDate startDate, LocalDate endDate) 메소드를 사용해서도 날짜 차이를 구할 수 있고, 리턴 타입은 Period입니다.

LocalDate startDate = LocalDate.now(); // 2016-04-02
LocalDate endDate = LocalDate.of(2016,5,5);

Period period = Period.between(startDate, endDate);

period.getYears();      // 0년
period.getMonths();     // 1개월
period.getDays();       // 3일 차이

1.5 시간 차이 계산하기

LocalDate, LocalTime, LocaDateTime은 until(Temporal end, TemporalUnit unit) 메소드를 사용하여 시간 차이를 계산 할 수 있고, 리턴 타입은 long 입니다. 두 번째 파라미터인 TemporalUnit 인터페이스의 구현체로 자바가 제공해주는 ChronoUnit을 사용하면 됩니다.

LocalTime startTime = LocalTime.now();  // 00:35:39
LocalTime endTime = LocalTime.of(12,59,00);

startTime.until(endTime, ChronoUnit.HOURS); 

또 다른 방법으로 java.time.Duration 클래스의 between(Temporal start, Temporal end) 메소드를 사용해서도 시간 차이를 구할 수 있다. 리턴 타입은 Duration 입니다.

LocalTime startTime = LocalTime.now();  // 00:35:39
LocalTime endTime = LocalTime.of(12,59,00);

Duration duration = Duration.between(startTime, endTime);
duration.getSeconds();      // 초의 차이
duration.getNano();         // 나노초의 차이

1.6 전체 시간을 기준으로 차이 계산하기

날짜 차이 계산하기에서 Period 클래스의 between() 메소드를 사용해서 계산하는 경우, getDays()의 결과는 3 이었습니다. 하지만 실제 1개월 3일 차이이므로 33일이 나오도록 전체 일을 구하는 방법은 ChronoUnit 클래스의 between(Temporal start, Temporal end) 메소드를 사용하면 되고, 리턴 타입은 long 입니다.

클래스 설명
ChronoUnit.YEARS 전체 년 차이
ChronoUnit.MONTHS 전체 월 차이
ChronoUnit.WEEKS 전체 주 차이
ChronoUnit.DAYS 전체 일 차이
ChronoUnit.HOURS 전체 시간 차이
ChronoUnit.SECONDS 전체 초 차이
ChronoUnit.MILLIS 전체 밀리초 차이
ChronoUnit.NANOS 전체 나노초 차이
LocalDate startDate = LocalDate.now(); // 2016-04-02
LocalDate endDate = LocalDate.of(2016,5,5);

ChronoUnit.DAYS.between(startDate, endDate); // 결과 : 33 (1개월 3일)

 

2. 실제로 사용되지는 않지만, 알아야 하는 API

2.1 TemporalField

This set of fields provide field-based access to manipulate a date, time or date-time

: 시간 관련 객체에서 어떤 필드의 값에 접근할지 정의하는 인터페이스

TemporalField 의 구현자는 Enum 객체인 ChronoField 로서 아래 코드에서 보여주는 것처럼 ChronoField의 Enum 요소를 이용해서 원하는 정보를 쉽게 얻을 수 있습니다. LocalDateTime, LocalDate, LocalTime, Instant 에 사용될 수 있습니다.

int y = date.get(ChronoField.YEAR);
int m = date.get(ChronoField.MONTH_OF_YEAR);
int d = date.get(ChronoField.DAY_OF_MONTH);
NANO_OF_SECOND("NanoOfSecond", NANOS, SECONDS, ValueRange.of(0, 999_999_999)),
NANO_OF_DAY("NanoOfDay", NANOS, DAYS, ValueRange.of(0, 86400L * 1000_000_000L - 1)),
MICRO_OF_SECOND("MicroOfSecond", MICROS, SECONDS, ValueRange.of(0, 999_999)),
MICRO_OF_DAY("MicroOfDay", MICROS, DAYS, ValueRange.of(0, 86400L * 1000_000L - 1)),
MILLI_OF_SECOND("MilliOfSecond", MILLIS, SECONDS, ValueRange.of(0, 999)),
MILLI_OF_DAY("MilliOfDay", MILLIS, DAYS, ValueRange.of(0, 86400L * 1000L - 1)),
SECOND_OF_MINUTE("SecondOfMinute", SECONDS, MINUTES, ValueRange.of(0, 59), "second"),
SECOND_OF_DAY("SecondOfDay", SECONDS, DAYS, ValueRange.of(0, 86400L - 1)),
MINUTE_OF_HOUR("MinuteOfHour", MINUTES, HOURS, ValueRange.of(0, 59), "minute"),
MINUTE_OF_DAY("MinuteOfDay", MINUTES, DAYS, ValueRange.of(0, (24 * 60) - 1)),
HOUR_OF_AMPM("HourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(0, 11)),
CLOCK_HOUR_OF_AMPM("ClockHourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(1, 12)),
HOUR_OF_DAY("HourOfDay", HOURS, DAYS, ValueRange.of(0, 23), "hour"),
CLOCK_HOUR_OF_DAY("ClockHourOfDay", HOURS, DAYS, ValueRange.of(1, 24)),
AMPM_OF_DAY("AmPmOfDay", HALF_DAYS, DAYS, ValueRange.of(0, 1), "dayperiod"),
DAY_OF_WEEK("DayOfWeek", DAYS, WEEKS, ValueRange.of(1, 7), "weekday"),
ALIGNED_DAY_OF_WEEK_IN_MONTH("AlignedDayOfWeekInMonth", DAYS, WEEKS, ValueRange.of(1, 7)),
ALIGNED_DAY_OF_WEEK_IN_YEAR("AlignedDayOfWeekInYear", DAYS, WEEKS, ValueRange.of(1, 7)),
DAY_OF_MONTH("DayOfMonth", DAYS, MONTHS, ValueRange.of(1, 28, 31), "day"),
DAY_OF_YEAR("DayOfYear", DAYS, YEARS, ValueRange.of(1, 365, 366)),
EPOCH_DAY("EpochDay", DAYS, FOREVER, ValueRange.of((long) (Year.MIN_VALUE * 365.25), (long) (Year.MAX_VALUE * 365.25))),
ALIGNED_WEEK_OF_MONTH("AlignedWeekOfMonth", WEEKS, MONTHS, ValueRange.of(1, 4, 5)),
ALIGNED_WEEK_OF_YEAR("AlignedWeekOfYear", WEEKS, YEARS, ValueRange.of(1, 53)),
MONTH_OF_YEAR("MonthOfYear", MONTHS, YEARS, ValueRange.of(1, 12), "month"),
PROLEPTIC_MONTH("ProlepticMonth", MONTHS, FOREVER, ValueRange.of(Year.MIN_VALUE * 12L, Year.MAX_VALUE * 12L + 11)),
YEAR_OF_ERA("YearOfEra", YEARS, FOREVER, ValueRange.of(1, Year.MAX_VALUE, Year.MAX_VALUE + 1)),
YEAR("Year", YEARS, FOREVER, ValueRange.of(Year.MIN_VALUE, Year.MAX_VALUE), "year"),
ERA("Era", ERAS, FOREVER, ValueRange.of(0, 1), "era"),
INSTANT_SECONDS("InstantSeconds", SECONDS, FOREVER, ValueRange.of(Long.MIN_VALUE, Long.MAX_VALUE)),
OFFSET_SECONDS("OffsetSeconds", SECONDS, FOREVER, ValueRange.of(-18 * 3600, 18 * 3600));

2.2 TemporalUnit

: 날짜 관련 객체에서 어떤 필드의 값에 접근할지 정의하는 인터페이스

TemporalUnit 의 구현자는 Enum 객체인 ChronoUnit 로서 동작합니다. TemporalUnit의 경우, Duration과 Period와 함께 사용할 수 있습니다.

Duration duration = Duration.between(LocalTime.MIDNIGHT, LocalTime.NOON);
duration.get(ChronoUnit.SECONDS);
Period period = Period.of(0,0,5);
period.get(ChronoUnit.DAYS);
NANOS("Nanos", Duration.ofNanos(1)),
MICROS("Micros", Duration.ofNanos(1000)),
MILLIS("Millis", Duration.ofNanos(1000_000)),
SECONDS("Seconds", Duration.ofSeconds(1)),
MINUTES("Minutes", Duration.ofSeconds(60)),
HOURS("Hours", Duration.ofSeconds(3600)),
HALF_DAYS("HalfDays", Duration.ofSeconds(43200)),
DAYS("Days", Duration.ofSeconds(86400)),
WEEKS("Weeks", Duration.ofSeconds(7 * 86400L)),
MONTHS("Months", Duration.ofSeconds(31556952L / 12)),
YEARS("Years", Duration.ofSeconds(31556952L)),
DECADES("Decades", Duration.ofSeconds(31556952L * 10L)),
CENTURIES("Centuries", Duration.ofSeconds(31556952L * 100L)),
MILLENNIA("Millennia", Duration.ofSeconds(31556952L * 1000L)),
ERAS("Eras", Duration.ofSeconds(31556952L * 1000_000_000L)),
FOREVER("Forever", Duration.ofSeconds(Long.MAX_VALUE, 999_999_999));

2.3 TemporalAmount

 6시간, 8일, 2년, 3개월 등의 시간의 양을 정의하기 위해서 사용됩니다. 즉, TemporalAmount 는 Period, Duration 인터페이스가 됩니다. 이는 mutability 하게 구현되는 것에 제한은 없지만, Immutability하게 사용하는 것이 권장사항입니다.

2.4 query(TemporalQuery<R> query)

docs.oracle.com/javase/8/docs/api/java/time/temporal/TemporalQuery.html
@FunctionalInterface
public interface TemporalQuery<R> 
{
    R queryFrom(TemporalAccessor temporal);
}

시간 개체 쿼리 전략. 쿼리는 시간 개체에서 정보를 추출하기위한 핵심 도구입니다. 이들은 전략 설계 패턴에 따라 쿼리 프로세스를 외부화하고 다양한 접근 방식을 허용하기 위해 존재합니다. 예를 들어 날짜가 윤년의 2 월 29 일 전날인지 확인하거나 다음 생일까지의 일 수를 계산하는 쿼리가 있습니다.

@Test
void query_테스트() {

	LocalTime now = LocalTime.now();
	LocalTime localTime = now.query(TemporalQueries.localTime());
	assertEquals(now, localTime);

	LocalDate query = this.now.query(TemporalQueries.localDate());
	assertEquals(LocalDate.now(), query);

	Chronology query1 = this.now.query(TemporalQueries.chronology());
	assertEquals(this.now.getChronology(), query1);

	TemporalUnit temporalUnit = now.query(TemporalQueries.precision());
	assertEquals("PT0.000000001S", temporalUnit.getDuration().toString());

}	

LocalTime now = LocalTime.now();
 
System.out.println("Currently Working :: " + now.query(WorkingHoursQuery));
 
private static final TemporalQuery<Boolean> WorkingHoursQuery = temporal -> {
    LocalTime t = LocalTime.from(temporal);
    return t.compareTo(LocalTime.of(9, 0)) >= 0
            && t.compareTo(LocalTime.of(17, 0)) < 0;
};

쿼리를 작성하듯 사용할 수 있습니다,

2.5 Epoch 이란?

ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%89%EC%8A%A4_%EC%8B%9C%EA%B0%84

 시간을 나타내는 많은 함수 중에, 사람이 이해할 수 있는(human readable) 형태 - 연도/월/일/시/분/초 등, 시간을 나타내는 기능이 있습니다.

반대로, 사람이 이해할 수 없는 난해한 긴 숫자로 표현된 형태도 있습니다.

Unix Time, Posix Time, Epoch Time 도 이에 해당합니다. 여기서 epoch 이란 단어를 번역하면, 중요한 사건이나 변화가 있었던 시대(era)를 의미하는데, Unix와 Posix 등의 시스템에서 날짜와 시간의 흐름을 나타낼 때 기준을 삼는 시간입니다. 

00:00:00 UTC on January 1, 1970 부터 지금까지의 경과 시간을 초로 환산하여 정수로 나타낸 것입니다.

stackoverflow.com/questions/1090869/why-is-1-1-1970-the-epoch-time

2.6 TimeStamp 이란?

타임스탬프(영어: timestamp) 또는 시간 표기(時間標記)는 특정한 시각을 나타내거나 기록하는 문자열이다. 둘 이상의 시각을 비교하거나 기간을 계산할 때 편리하게 사용하기 위해 고안되었으며, 일관성 있는 형식으로 표현된다. 실제 정보를 타임스탬프 형식에 따라 기록하는 행위를 타임스탬핑(timestamping)이라 한다. 파일시스템에서 타임스탬프는 저장된 파일이 생성되거나 변경된 시각을 뜻하기도 한다.

 타임스탬프는 어느 시점에 데이터가 존재했다는 사실을 증명하기 위해 특정 위치에 표시하는 시각

allenjeon.tistory.com/235

타임스탬프를 Instant(epoch)로 나타내기

Instant epoch = Instant.EPOCH; // Instant.ofEpochSecond(0); 와 동일
System.out.println("epoch = " + epoch); // 1970-01-01T00:00:00Z

Instant epochInFuture = Instant.ofEpochSecond(1_000_000_000);
System.out.println("epochInFuture = " + epochInFuture); // 2001-09-09T01:46:40Z

Instant epochInPast = Instant.ofEpochSecond(-1_000_000_000);
System.out.println("epochInPast = " + epochInPast); // 1938-04-24T22:13:20Z
Instant current = Instant.now();
System.out.println("Current Instant = "+ current);

long epochSecond = current.getEpochSecond();
System.out.println("Current Timestamp in seconds = " + epochSecond);

long epochMilli = current.toEpochMilli();
System.out.println("Current Timestamp in milli seconds = " + epochMilli);

 

[참고 자료]

www.daleseo.com/java8-instant/

howtodoinjava.com/java/date-time/temporalquery/

ko.wikipedia.org/wiki/%ED%83%80%EC%9E%84%EC%8A%A4%ED%83%AC%ED%94%84

댓글