ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java 8 - 5. Date & Time
    공부일기/Java 8 2020. 12. 23. 16:03

     

    자바 8에 새로운 날짜와 시간 API가 생긴 배경

    • 그전까지 사용하던 java.util.Date 클래스는 mutable 하기 때문에 thead safe하지 않다.

    • 클래스 이름이 명확하지 않다. Date인데 시간까지 다룬다.

    • 버그 발생할 여지가 많다. (타입 안정성이 없고, 월이 0부터 시작한다거나..)

    • 날짜 시간 처리가 복잡한 애플리케이션에서는 보통 Joda Time을 쓰곤했다.

     

    자바 8에서 제공하는 Date-Time API

    • JSR-310 스팩의 구현체를 제공한다.

    • 디자인 철학

      • Clear

      • Fluent

      • Immutable

      • Extensible

     

    주요 API

    • 기계용 시간 (machine time)과 인류용 시간(human time)으로 나눌 수 있다.

    • 기계용 시간은 EPOCK (1970년 1월 1일 0시 0분 0초)부터 현재까지의 타임스탬프를 표현한다.

    • 인류용 시간은 우리가 흔히 사용하는 연,월,일,시,분,초 등을 표현한다.

    • 타임스탬프는 Instant를 사용한다.

    • 특정 날짜(LocalDate), 시간(LocalTime), 일시(LocalDateTime)를 사용할 수 있다.

    • 기간을 표현할 때는 Duration (시간 기반)과 Period (날짜 기반)를 사용할 수 있다. 

    • DateTimeFormatter를 사용해서 일시를 특정한 문자열로 포매팅할 수 있다.

     

    지금 이 순간을 기계 시간으로 표현하는 방법

    • Instant.now(): 현재 UTC (GMT)를 리턴한다.

    package JavaStudy;
    
    import java.time.Instant;
    
    public class Main {
        public static void main(String[] args) {
            Instant instant = Instant.now();
            System.out.println(instant);
        }
    }
    

    실행결과 GMT를 리턴한다.

    GMT가 아닌 자신의 위치에 맞는 시간대를 보고 싶다면 어떻게 하면 될까?

    ZoneID를 사용해서 자신의 시스템 상의 시간을 확인할 수 있다.

    package JavaStudy;
    
    import java.time.Instant;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    
    public class Main {
        public static void main(String[] args) {
            Instant instant = Instant.now();
            System.out.println(instant);
    
            ZoneId zone = ZoneId.systemDefault();
            System.out.println(zone);
            
            ZonedDateTime zoneTime = instant.atZone(zone);
            System.out.println(zoneTime);
        }
    }
    

    컴퓨터 시간과 일치하는 것을 확인할 수 있다.

     

    여기까지 기계용 일시를 표현하는 방법


    인류용 일시를 표현하는 방법

    • LocalDateTime.now(): 현재 시스템 Zone에 해당하는(로컬) 일시를 리턴한다.

    package JavaStudy;
    
    import java.time.LocalDateTime;
    
    public class Main {
        public static void main(String[] args) {
            LocalDateTime now = LocalDateTime.now();
            System.out.println(now);
        }
    }
    

    현재 시간과 일치한다는 것을 확인할 수 있다.

    위와 같은 LocalDateTime.now의 문제는 서버 시스템의 시간을 LocalDate의 정보로 가진다는 점이다. 그래서 한국에 서버를 두고 있는 서비스를 미국의 유저가 사용한다면 시간이 맞지 않게 표시될 수 있는 문제점을 가지고 있다. 이 점을 주의해야 한다.

     

    • LocalDateTime.of(int, Month, int, int, int, int): 로컬의 특정 일시를 리턴한다. 

    package JavaStudy;
    
    import java.time.LocalDateTime;
    import java.time.Month;
    
    public class Main {
        public static void main(String[] args) {
            LocalDateTime birthday =
                    LocalDateTime.of(1992, Month.NOVEMBER, 30, 0,0,0);
            System.out.println(birthday);
        }
    }
    

    입력 받은 정보의 특정 일시를 리턴해준다.

     

     

    • ZonedDateTime.of(int, Month, int, int, int, int, ZoneId): 특정 Zone의 특정 일시를 리턴한다.

    package JavaStudy;
    
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    
    public class Main {
        public static void main(String[] args) {
            ZonedDateTime nowInKorea = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));
            System.out.println(nowInKorea);
        }
    }
    

    같은 시간을 출력하는 방법으로 instant에 atZone 메서드를 이용하는 방법도 있다.

    package JavaStudy;
    
    import java.time.Instant;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    
    public class Main {
        public static void main(String[] args) {
            Instant instant = Instant.now();
            ZonedDateTime seoulTime = instant.atZone(ZoneId.of("Asia/Seoul"));
            System.out.println(seoulTime);
        }
    }
    

    컴퓨터 시간과 비교하진 않았지만 작성 당시 시간과 일치하고 있다.

     

     

    기간을 표현하는 방법

    • Period / Duration . beteen()

    Period

    인간용 기간 비교

    package JavaStudy;
    
    import java.time.LocalDate;
    import java.time.Month;
    import java.time.Period;
    import java.time.temporal.ChronoUnit;
    
    public class Main {
        public static void main(String[] args) {
            LocalDate today = LocalDate.now();
            LocalDate nextYearBirthday = LocalDate.of(2021, Month.NOVEMBER, 30);
            System.out.println(today);
    
            Period period = Period.between(today, nextYearBirthday);
            System.out.println(period.getDays());
    
            Period until = today.until(nextYearBirthday);
            System.out.println(period.get(ChronoUnit.DAYS));
            
            long days = ChronoUnit.DAYS.between(today, nextYearBirthday);
            System.out.println(days);
        }
    }
    

    대략 340일 정도가 남았을텐데 7이 나온다. 연도를 2020으로 바꾸면 -23이 나오는 거로 봐선 최대 아마 같은 연도 정도만 구하는게 아닐까 싶다.. 이 부분은 좀 더 조사해봐야겠다.

    Period는 기간을 연,월,일로 표현하기 때문에 30일이 넘어간 정보는 월에 담기게 된다. 따라서 전체 일수를 계산하고 싶으시면 ChronoUnit이 제공하는 between을 사용할 수 있다. 예제도 수정했다.

     

     

    Duration

    머신용 기간 비교

    package JavaStudy;
    
    import java.time.*;
    import java.time.temporal.ChronoUnit;
    
    public class Main {
        public static void main(String[] args) {
            Instant now = Instant.now();
            Instant plus = now.plus(10, ChronoUnit.SECONDS);
            Duration between = Duration.between(now, plus);
            System.out.println(between.getSeconds());
        }
    }

    10초 차이가 나는 것을 확인

     

     

    파싱 또는 포매팅

    package JavaStudy;
    
    import java.time.*;
    import java.time.format.DateTimeFormatter;
    import java.time.temporal.ChronoUnit;
    
    public class Main {
        public static void main(String[] args) {
            LocalDate today = LocalDate.now();
            DateTimeFormatter MMddyyyy =
                    DateTimeFormatter.ofPattern("MM/dd/yyyy");
            System.out.println(today.format(MMddyyyy));
    
            LocalDate parse = LocalDate.parse("11/30/1992", MMddyyyy);
            System.out.println(parse);
        }
    }
    

     

     

    레거시 API 지원

    • GregorianCalendar와 Date 타입의 인스턴스를 Instant나 ZonedDateTime으로 변환 가능.

    package JavaStudy;
    
    import java.time.*;
    import java.util.Date;
    import java.util.GregorianCalendar;
    import java.util.TimeZone;
    
    public class Main {
        public static void main(String[] args) {
            Date date = new Date();
            Instant instant = date.toInstant(); //date 타입을 Instant 타입으로 변환
            Date newDate = Date.from(instant); // Instant로 데이터를 만들 수 있음
    
            GregorianCalendar gregorianCalendar = new GregorianCalendar();
            LocalDateTime dateTime = gregorianCalendar.toInstant()
                    .atZone(ZoneId.of("Asia/Seoul"))
                    .toLocalDateTime();
    
            ZonedDateTime zonedDateTime = gregorianCalendar.toInstant().atZone(ZoneId.of("Asia/Seoul"));
            GregorianCalendar.from(zonedDateTime);
    
            ZoneId zoneId = TimeZone.getTimeZone("PsT").toZoneId(); //예전 API to 최근 API
            TimeZone timeZone = TimeZone.getTimeZone(zoneId); // 최근 API ro 예전 API
        }
    }
    

     

     

     

     

    아무래도 자바 8의 최신 API들을 주로 많이 알아두는게 도움이 될 것 같다.

    '공부일기 > Java 8' 카테고리의 다른 글

    Java 8 - 7. Callable과 Future  (0) 2020.12.25
    Java 8 - 6. Concurrent 프로그래밍과 Executors  (0) 2020.12.24
    Java 8 - 4. Optional  (0) 2020.12.22
    Java 8 - 3. Stream  (0) 2020.12.16
    Java 8 - 2. 인터페이스의 변화  (0) 2020.12.07
Designed by Tistory.