JDK 8에서 등장한 새로운 LocalDate, LocalTime, ChronoUnit을 이용해 시간을 나타내고 계산을 해보겠습니다. 이전에는 Date, Calendar 이런 애들이 쓰였는데요, 자바 초기에 등장한 것이라 문제가 많았다고 합니다. 여러 문제를 다 열거할 수 없지만 가장 큰 문제는 Calendar는 특이하게 월을 0 ~ 11로 기록해야 한다는 점이었습니다. 혹시 이런 점을 모르고 일반적인 상식으로 코드를 작성했다가 대형 사고로까지 이어질 수 있겠네요. 새로 등장한 기능들은 그런 여러 문제들을 해결한 것입니다.

기초 코드는 다음과 같습니다.

package com.apple.biorhythm;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;

public class CalculateDates {
  
  private static void p(Object o) {
    System.out.println(o);
  }
  
  public static void main(String[] args) {
    
  
  }
}

 

LocalDate는 연, 월, 일을 다룰 때 쓰입니다. 오늘 날짜를 구하려면 now()를 사용합니다.

LocalDate today = LocalDate.now();   // 오늘 날짜
p(today);   // 2019-01-20

 

두 개의 특정 날짜 사이의 차이를 연, 월, 일로 구하려면 다음과 같이 사용합니다. 아래 코드에서 월(month) 자리에 사용된 3은 말 그대로 3월을 뜻합니다. 특정 날짜를 수동으로 입력하려면 LocalDate.of(연, 월, 일)을 사용합니다.

LocalDate targetDate1 = LocalDate.of(1980, 3, 12);
p(today.until(targetDate1));  // P-38Y-10M-8D

 

두 개의 특정 날짜 사이의 차이를 일수(days)로 구하려면 ChronoUnit.DAYS.between(a, b)를 사용합니다. between 파라미터의 순서는 반드시 이른 시간이 앞에, 늦은 시간이 뒤에 위치해야 합니다.

long day1 = ChronoUnit.DAYS.between(today, targetDate1);
p(day1); // -14193
long day2 = ChronoUnit.DAYS.between(today, LocalDate.of(2020, 3, 12));
p(day2); // 417

 

만약 특정 날짜의 100일 전/후는 언제인지 알고 싶다면, ChronoUnit.DAYS.between(date, longAmount)를 사용합니다.

p(ChronoUnit.DAYS.addTo(today, 100)); // 2019-04-30
p(ChronoUnit.DAYS.addTo(today, -100)); // 2018-10-12
p(ChronoUnit.DAYS.addTo(LocalDate.of(1976, 1, 4), 10000)); // 2023-05-22

 

ChronoUnit을 응용해 보겠습니다. 첫 번째는 특정 날짜에서 몇 세기(CENTURIES.addTo) 전후를 구합니다. 두 번째는 특정 날짜에서 몇 달 전후(MONTHS.addTo)를 구합니다. 세 번째는 두 날짜 사이는 몇 년(YEARS.between)인지 구합니다.

p(ChronoUnit.CENTURIES.addTo(today, -1)); // 1919-01-20
p(ChronoUnit.MONTHS.addTo(today, 10));	// 2019-11-20
p(ChronoUnit.YEARS.between(today, targetDate1)); // -38

 

다른 날짜 포맷에 대해서도 알아보겠습니다. LocalTime은 시, 분 초를 표시하며 초 단위는 millisecond까지 소수점으로 표시하는 포맷입니다. LocalDateTimeLocalDateLocalTime이 합쳐진 포맷입니다.

LocalTime currentTime = LocalTime.now();
p(currentTime);	// 19:05:31.397

LocalDateTime currentDateTime = LocalDateTime.now();
p(currentDateTime);	// 2019-01-20T19:06:15.909

 

LocalDateTimeLocalDate를 서로 계산하려고 하면 예외가 발생합니다. 두 포맷의 타입을 일치시켜야 합니다. 여기서는 LocalDateTimeLocalDate로 바꾸는 방법에 대해 알아보겠습니다.

p(ChronoUnit.DAYS.between(currentDateTime.toLocalDate(), targetDate1));	// -14193
p(ChronoUnit.HOURS.between(LocalTime.of(12, 11), currentTime)); // 7

 


예제로 바이오리듬 프로그램을 만들어보겠습니다. 바이오리듬은 제가 어렸을 때 유행했던 운세의 일종인데요 자세한 설명은 진리의 나무위키에서 볼 수 있습니다. 여기에 따르면 지금은 과학적 근거는 전혀 없고 유행도 지난것 같지만 프로그래밍 연습에는 좋은 예제입니다. 다음에는 벤치마킹 사이트를 참고해 웹 페이지로 바이오리듬을 구현해보려 합니다. 벤치마킹 사이트의 확장자 php3에서 알 수 있듯이 굉장히 옛날 사이트입니다.

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Scanner;

public class BioRhythm {

  public static void main(String[] args) {
    /*
     * 바이오리듬 공식: -100 to 100
     * 신체(23일) : sin(2πt/23)
     * 감성(28일) : sin(2πt/28)
     * 지성(33일) : sin(2πt/33)
     * 
     * 위험일 0 ~ -15
     * 고조기 1 ~ 100
     * 저조기 -16 ~ -100
     */
    
    LocalDate birthday;
    LocalDate today = LocalDate.now();
    
    try(Scanner s = new Scanner(System.in)){
      System.out.println("바이오리듬: 오늘 날짜는 " + today + " 입니다.");
      System.out.print("생년월일을 입력하세요.(예: 19700123) >> ");
      
      String input = s.nextLine();
      
      int year = Integer.parseInt(input.substring(0, 4));
      // System.out.println(year);
      int month = Integer.parseInt(input.substring(4, 6));
      int dayOfMonth = Integer.parseInt(input.substring(6, 8));
      birthday = LocalDate.of(year, month, dayOfMonth);
      
      long betweenDays = ChronoUnit.DAYS.between(birthday, today);
      System.out.println();
      System.out.println("생년월일로부터 오늘까지의 경과일수: " + betweenDays);
      
      double body = Math.sin( (2 * Math.PI * betweenDays) / 23 );
      double emotion = Math.sin( (2 * Math.PI * betweenDays) / 28 );
      double intelligence = Math.sin( (2 * Math.PI * betweenDays) / 33 );
      
      System.out.println();
      System.out.println("======== 결 과 ========");
      System.out.println("신체리듬: " + getPercentage(body) + " (" + getCurrentState(body) + ")");
      System.out.println("감성리듬: " + getPercentage(emotion) + " (" + getCurrentState(emotion) + ")");
      System.out.println("지성리듬: " + getPercentage(intelligence) + " (" + getCurrentState(intelligence) + ")");
      
      System.out.println();
      System.out.println("======== 해 석 ========");
      System.out.println(getTotalInterpretation(body, emotion, intelligence));
    }
    
  }
  
  private static int getPercentage(double num) {
    return (int)(num * 100);
  }
  
  private static String getCurrentState(double num) {
    int perc = getPercentage(num);
    if(perc <= 0 && perc >= -15) {
      return "위험";
    } else if(perc <= -16) {
      return "저조기";
    } else {
      return "고조기";
    }
  }
  
  // total 27 (=3*3*3)
  private static String getTotalInterpretation(double body, double emotion, double intelligence) {
    String bodyStat = getCurrentState(body);
    String emotStat = getCurrentState(emotion);
    String intelStat = getCurrentState(intelligence);
    
    if(bodyStat.equals("위험") && emotStat.equals("위험") && intelStat.equals("위험")) {
      return "오늘은 굉장히 위험한 날입니다. \n외출을 삼가시고 중요한 사업적 판단을 자제해 주십시오.";
    } else if(bodyStat.equals("고조기") && emotStat.equals("고조기") && intelStat.equals("저조기")) {
      return "신체리듬과 감성리듬이 고조기이므로 야외활동에서 좋은 성과가 있을 것입니다. \n"
          + "단 지성 리듬이 저조기이므로 지나치게 감성적인 판단이 나올 수 있으므로 \n이성이 필요한 판단은 삼가해주십시오.";
    } else if(bodyStat.equals("고조기") && emotStat.equals("저조기") && intelStat.equals("저조기")) {
      return "신체리듬이 고조기이므로 신체활동 컨디션이 좋고 성과가 나올 수 있습니다만, \n감성리듬과 지성리듬은 저조기이므로"
          + " 결과에 지나치게 낙담할 수 있고 \n그에 따른 비이성적인 판단이 나올 수 있으므로 조심하십시오.";
    } else if(bodyStat.equals("고조기") && emotStat.equals("저조기") && intelStat.equals("고조기")) {
      return "신체리듬과 지성리듬이 고조기이므로 과업에서 좋은 성과가 나올 수 있습니다.\n"
          + "다만 감성리듬이 저조기이므로 성과에 비해 그릇된 감성판단을 할 수 있으나 \n이에 연연하지 마십시오.";
    }
    
    return "ㅈㅅ 나중에..";
  }
  
  

}
바이오리듬: 오늘 날짜는 2019-01-20 입니다.
생년월일을 입력하세요.(예: 19700123) >> 19800123

생년월일로부터 오늘까지의 경과일수: 14242

======== 결 과 ========
신체리듬: 97 (고조기)
감성리듬: -78 (저조기)
지성리듬: -45 (저조기)

======== 해 석 ========
신체리듬이 고조기이므로 신체활동 컨디션이 좋고 성과가 나올 수 있습니다만, 
감성리듬과 지성리듬은 저조기이므로 결과에 지나치게 낙담할 수 있고 
그에 따른 비이성적인 판단이 나올 수 있으므로 조심하십시오.

문의 | 코멘트 또는 ayaysir0@naver.com  donaricano-btn

카테고리: Java

1개의 댓글

자바스크립트: 날짜 관련 함수 + 예제(바이오리듬) – BGSMM · 2019년 1월 26일 5:23 오후

[…] 작업했던 자바 프로그램을 기준으로 제작하였으며(링크), 바이오리듬을 계산하려면 조회 날짜와 생년월일 사이의 날짜 계산이 […]

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다