Date & Calender & Time

tabi·2023년 2월 25일

국비지원

목록 보기
1/3

Date 클래스

JDK 1.0 부터 생긴 - java.util.[Date 클래스] 는 deprecated 메소드(사라질 예정인 것)

1) 객체 생성 방법

//디폴트 생성자는 현재 시스템의 날짜, 시간 정보를 얻어온다.
//=> 생성할 때 현재 날짜, 시간을 가진 객체가 만들어진다는 것
Date d = new Date();

//원하는 날짜의 객체 생성도 가능
Date d = new Date(year-1900, month-1, date);

2) 출력

//아래 둘은 같은 시간정보 값을 갖는다.
//Thu Feb 23 14:32:02 KST 2023  (KST: 한국표준시)
System.out.println(d.toString()); //위에서 만들어진 객체정보를 출력하는게 toString()
System.out.println(d);

3) 날짜를 보기 쉽게 표시해주는 toGMTString(), toLocaleString()

System.out.println(d.toGMTString()); //23 Feb 2023 05:38:12 GMT (GMT: 그리니치 평균시간 = 세계표준시간)
System.out.println(d.toLocaleString()); //2023. 2. 23. 오후 2:38:12 (로컬, 해당지역에서 쓰는대로)

4) 메소드

  • 년, 월, 일, 요일, 시간, 분, 초 를 가져오는 메소드 get()
//년
System.out.println(d.getYear() + 1900); //1900 뺀 값으로 나오므로 더해줘야 제대로 나옴
//월
System.out.println(d.getMonth()+1); //1~12월이 0~11까지 나옴
//일
System.out.println(d.getDate()); //날짜 중 일 정보를 얻어옴
//요일
System.out.println(d.getDay()); //0은 일요일, 1은 월요일, ~ 6은 토요일 // 목(4)
System.out.println("일월화수목금토".charAt(d.getDay()) + "요일"); //문자열에서 원하는 위치의 한 문자를 얻어옴
//시간
System.out.println(d.getHours());
//분
System.out.println(d.getMinutes());
//초
System.out.println(d.getSeconds());
  • 년, 월, 일, 요일, 시간, 분, 초를 설정하는 메소드 set()
Date d2 = new Date();
d2.setYear(2010-1900);
d2.setMonth(5-1);
d2.setDate(1);
d2.setHours(0);
d2.setMinutes(0);
d2.setSeconds(0); 

5) 예제

  • 요일 구하기
System.out.println("일월화수목금토".charAt(d.getDay())); //토요일
  • 주민등록번호를 통해 생일이 지났는지/지나지 않았는지 여부를 체크하는 코드는?
String rrn = "890223-1";
Date today = new Date();
//시간, 분, 초를 맞춰주지 않으면 시간에 따라서도 지났다, 안 지났다 결과가 바뀌어서 set으로 맞춰줘야 한다.
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
//생일이 동일한지 알기 위해서는 밀리세컨드값까지 동일하게 맞춰야 한다.
//getTime() 을 이용하면 1970 1월 1일부터 지금까지의 밀리세컨드값을 long 형으로 반환해준다.
today.setTime(today.getTime() - today.getTime() % 1000);

Date b_day = new Date(2023-1900, 2-1, 23, 0, 0, 0); //생일 날짜 객체
b_day.setTime(b_day.getTime() - b_day.getTime() % 1000);
System.out.println(b_day); //Sat Feb 23 00:00:00 KST 2023 여기는 시간, 분, 초가 0!!

System.out.println(today.after(b_day)); //날짜가 지났는지 안 지났는지에 따라 boolean 값을 돌려준다. true: 생일 지남, false: 안 지남
System.out.println(today.before(b_day)); //오늘 날짜가 생일 이전인지에 따라 boolean 값을 돌려준다. true: 생일 안 지남, false: 지남
System.out.println(today.equals(b_day)); //오늘이 생일인지

Calender 클래스

JDK 1.1 부터 생긴 - java.util.Calendar, 추상 클래스 (자식클래스로 GregorianCalendar가 있다.)

1) 객체 생성 방법

  • Calender 클래스는 추상클래스로 바로 객체 생성이 안 된다. 그럼 Calender 객체는 어떻게 만드나?
  1. Calendar 클래스에는 추상 클래스로서 직접적으로 객체를 생성할 수는 없으나, getInstance()라는 정적 메소드를 통해 현재 운영체제의 시간대(Time Zone)와 로케일(Locale)에 맞는 구체적인 Calendar 클래스의 인스턴스를 생성하고 반환할 수 있다. getInstance() 메소드는 실제로 Calendar 클래스의 구현체 중 하나를 반환하며, 이 구현체는 Calendar 클래스의 추상 메소드를 구현하고 있으므로, 추상 클래스인 Calendar를 직접적으로 사용하는 것이 아니라 구현체를 통해 사용할 수 있도록 한다. 즉, Calendar.getInstance() 메소드를 이용해 구체적인 Calendar 클래스의 객체를 생성하고 반환하는 것이다.
  2. 추상클래스 Calender를 상속받은 자식 클래스(GregorianCalender, BuddhistCalender) 로 객체를 만들 수 있다.
    => BuddhistCalender는 태국에서 사용하는 달력으로 한국에서는 GregorianCalender를 사용하면 된다.
1. getInstance() 이용
Calendar c = Calendar.getInstance();

2. 자식 클래스 이용
Calendar c1 = new GregorianCalendar();

3. 원하는 날짜의 객체 생성도 가능
Calendar c = new GregorianCalendar(2010, 5-1, 1); 

2) 출력

//아래 둘은 같은 시간정보 값을 갖는다.
System.out.println(c);
System.out.println(c.toString);
  • 왜 같은 값을 가질까?
    Java에서 Object 클래스는 toString() 메소드를 가지고 있다.
    그리고 모든 클래스는 Object 클래스를 상속받으므로 toString() 메소드를 사용할 수 있다.
    Calendar 클래스도 Object 클래스를 상속받고 있으며, Calendar 인스턴스를 출력하면 toString() 메소드가 호출된다.
    toString() 메소드는 Calendar 인스턴스의 현재 시간, 날짜, 시간대 등을 포함한 문자열을 반환한다.
    따라서 System.out.println(c)를 호출하면 c.toString()과 동일한 출력 결과를 얻을 수 있다.

3) 메소드

  • isLeapYear(year)
    • isLeapYear() 메소드는 추상 메소드가 아닌 구현 메소드이다. 따라서 Calendar 클래스를 직접 사용하여 isLeapYear() 메소드를 호출하는 것은 불가능!
//여기서는 사용불가(컴파일 오류)
Calendar c1 = new GregorianCalendar(); //Calendar 클래스를 상속받은 GregorianCalendar 클래스의 객체를 생성
//이렇게 생성한 객체는 참조 변수 c1이 Calendar 타입으로 선언되어 있기 때문에, Calendar 클래스에서 정의된 메소드만 사용가능하다.
c1.isLeapYear(2010);

//자식 -> 부모 로 참조시키는 건 가능하지만, 부모 -> 자식으로의 참조는 불가능

//여기서는 사용가능
GregorianCalendar c2 = new GregorianCalendar(); //구체적인 자식클래스로 생성한 객체를 참조 변수 c2로 선언
//c2는 GregorianCalendar 타입의 객체이므로, GregorianCalendar 클래스에서 정의된 isLeapYear() 메소드 사용가능
c2.isLeapYear(2010);
  • add() : 날짜에 년/월/일/시/분/초 필드를 더하거나 빼서 새로운 날짜를 만드는 메소드
    매개변수: 추가 혹은 삭제하고자 하는 필드 년/월/일/시/분/초, 수량
    리턴타입: void
//빼기 메소드는 없고, 더하기 메소드만 있다.
//하루 빼서 마지막 날짜를 구하기
Calendar c2 = new GregorianCalendar(2010, 6-1, 1); //2010-06-01
c2.add(Calendar.DATE, -1); //add 밖에 없어서 -1 더해줘서 5월의 마지막날 만듬
System.out.println(c.get(Calendar.DATE)); //31
  • roll() ??

  • 비교하는 메소드 after(), before(), equals(), compareTo()

    • Ex) 오늘 날짜를 기준으로 주민등록번호 앞자리 6자리가 "980220" 인 사람의 생일이 지났는지 여부를 체크
String rrn = "980226";
int month = Integer.parseInt(rrn.substring(2, 4));
int day = Integer.parseInt(rrn.substring(4));

Calendar t = Calendar.getInstance();
//이거 다 초기화 해줘야 제대로 결과 나옴
t.set(Calendar.HOUR_OF_DAY, 0);
t.set(Calendar.MINUTE, 0);
t.set(Calendar.SECOND, 0);
t.set(Calendar.MILLISECOND, 0);
//t.clear(Calendar.MILLISECOND); 밀리세컨드 초기화는 이거랑도 같음

Calendar b = new GregorianCalendar(t.get(Calendar.YEAR), month-1, day); //연도만 맞추고 월은 1 뺌
		
System.out.println(t.getTimeInMillis()); //밀리세컨드 초기화 확인, long형 1677164400000
System.out.println(b.getTimeInMillis()); //밀리세컨드 초기화 확인
		
System.out.println(t.after(b)); //true
System.out.println(t.before(b)); //false
System.out.println(t.equals(b)); //false
System.out.println(t.compareTo(b)); //동일하면 0, 지났으면 1, 지나지 않았으면 -1 을 반환한다.
  • 년, 월, 주, 일, 요일, 시간, 분, 초, 밀리세컨드 를 가져오는 메소드 get()
//Calender 클래스는 매개변수로 주어진 필드값이 존재한다.
//local fields (YEAR(1), MONTH(2), DATE(3), HOUR(4), MINUTE(5), etc.)

//년
System.out.println(c.get(1)); //public static final int YEAR= 1; 상수이자 static으로 클래스명으로도 접근가능. 상수화 되어있는 것.
System.out.println(c.get(Calendar.YEAR));

//월
System.out.println(c.get(Calendar.MONTH) + 1); //2 
		
//주
System.out.println(c.get(Calendar.WEEK_OF_MONTH)); //4, 해당 월의 몇번째 주인지
System.out.println(c.get(Calendar.WEEK_OF_YEAR)); //8, 해당 년도의 몇번째 주인지
		
//일 , 밑에 둘은 동일한 값을 갖는다.
System.out.println(c.get(Calendar.DATE)); //23
System.out.println(c.get(Calendar.DAY_OF_MONTH)); //23, 해당 월의 며칠째인지
//365일 중 며칠째?
System.out.println(c.get(Calendar.DAY_OF_YEAR)); //54, 해당 년도의 며칠째인지
		
//요일
System.out.println(c.get(Calendar.DAY_OF_WEEK)); //5(목요일), 일:1, 월:2, 화:3, 수:4, 목:5, 금:6, 토:7
//Date 클래스는 0(일)~6(토)임
		
//시간
System.out.println(c.get(Calendar.HOUR_OF_DAY)); //16, 24시간 기준(0~23)
System.out.println(c.get(Calendar.HOUR)); //4, 12시간 기준(0~11)
System.out.println(c.get(Calendar.AM_PM)); //1, 오전/오후 (AM:0, PM:1)
		
//분
System.out.println(c.get(Calendar.MINUTE)); //36
		
//초
System.out.println(c.get(Calendar.SECOND)); //5
		
//밀리세컨드
System.out.println(c.get(Calendar.MILLISECOND)); //535
  • 시간, 분, 초, 밀리세컨드 를 초기화하는 메소드 set(), clear()
t.set(Calendar.HOUR, 0);
t.set(Calendar.MINUTE, 0);
t.set(Calendar.SECOND, 0);
t.set(Calendar.MILLISECOND, 0);
//t.clear(Calendar.MILLISECOND); 밀리세컨드 초기화는 이거랑도 같음

4) 예제

  • 요일 구하기
System.out.println("일월화수목금토".charAt(c.get(Calendar.DAY_OF_WEEK) -1 )); //토요일
  • 마지막 날짜 구하기
int lastDay = c.getActualMaximum(Calendar.DAY_OF_MONTH);
System.out.println(lastDay);
  • 년도 year를 입력받아 입력받은 년도의 1월~12월 까지의 마지막 날짜 출력하기
int year;
try (Scanner sc = new Scanner(System.in)){ 
	System.out.println("년도를 입력하세요. ");
	year = sc.nextInt();
	System.out.printf("%d년\n", year);
			
	Calendar c = new GregorianCalendar(year, 1-1, 1); //200n년 1월 1일 객체
	for (int i = 1; i <= 12; i++) {
	//캘린더의 월을 i 값으로 수정해야 한다.
		c.set(Calendar.MONTH, i-1);
		int lastDay = c.getActualMaximum(Calendar.DATE);
		System.out.printf("%d월 : %d일\n", 1, lastDay);	
	}
} catch (Exception e) {
	e.printStackTrace();
}//try - catch
  • 일수 계산하기
Calendar s = new GregorianCalendar(2023, 1-1, 26, 9, 0, 0);
Calendar t = Calendar.getInstance(); //시간, 분, 초, 밀리세컨드
		
long sTime = s.getTimeInMillis();
long tTime = t.getTimeInMillis();
		
long gab = tTime - sTime;//개강일~오늘(현재)까지 흐른 ms
System.out.println(gab); //2512782133 ms
System.out.println(gab/1000 + "초");
System.out.println(gab/(60*1000) + "분");
System.out.println(gab/(60*60*1000) + "시간");
System.out.println(gab/(24*60*60*1000) + "일");
		
int[] time = {
		24*60*60*1000,
		60*60*1000,
		60*1000,
		1000,
		1
};
for (int i = 0; i < time.length; i++) {
	System.out.print(gab/time[i]+"_");
	gab %=time[i];
}
  • 설문조사 기간 체크하기
Calendar today = Calendar.getInstance();
		
Calendar start = new GregorianCalendar();
start.set(2023, 2-1, 17, 0, 0, 0);
Calendar end = new GregorianCalendar();
end.set(2023, 2-1, 26, 18, 0, 0); //끝나는 날짜도 포함됨!!
		
long startTime = start.getTimeInMillis();
long endTime = end.getTimeInMillis();
long todayTime = today.getTimeInMillis();

if (startTime <= todayTime && todayTime <= endTime) {
     System.out.println("설문이 가능합니다.");
} else {
     System.out.println("설문조사 기간이 종료되었습니다.");
}

Date & Calender

  • Date -> Calender 날짜 객체를 캘린더 객체로 변환하는 setTime(Date 객체)
Date d2 = new Date();
Calendar c2 = Calendar.getInstance();
c2.setTime(d2);
  • Calender -> Date 캘린더 객체를 날짜 객체로 변환하는 Date(Calender 객체.getTimeInMillis())
Calendar c1 = Calendar.getInstance();
Date d1 = new Date(c1.getTimeInMillis());
  • 달력 만들기(마지막 날짜 앞뒤로 숫자 나오게끔)
int year = 2023;
int month = 2;
		
Calendar c = new GregorianCalendar(year, month-1, 1); //23년도 2월 1일
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);//1(일) ~ 7 (토)
		
c.add(Calendar.DATE, -(dayOfWeek)); //23년도 2월 1일에 dayOfWeek 만큼 빼자는거

System.out.println("-".repeat(55));
System.out.println("-".repeat(55));
		
for (int i = 1; i <= 42; i++) {
	c.add(Calendar.DATE, 1);
	int outMonth = c.get(Calendar.MONTH) + 1;
	/*
	if(outMonth == month) System.out.printf("%d\t", c.get(Calendar.DATE));
	else System.out.printf("(%d)\t", c.get(Calendar.DATE)); */
			
	System.out.printf(outMonth == month? "[%d]\t" : "(%d)\t", c.get(Calendar.DATE) );
			
	if(i % 7 == 0) System.out.println();
}//for
System.out.println("-".repeat(55));
		
// Calendar 객체를 Date 타입으로 형변환 하면 toLocaleString 쓸 수 있으니 형변환 해보자.
//Date d = new Date(c.getTimeInMillis()); //매개변수로 long형 입력가능하므로 입력
//System.out.println(d.toLocaleString());

Time 패키지

JDK 1.8 부터 생긴 - java.time 패키지 안에 있는 여러 하위패키지 + 다양한 클래스 추가

① 구조에 대한 이해

  • Date, Calendar는 불편하거나 단점이 있어 새로운 패키지가 만들어짐
  • [ java.time 패키지 + 하위 4개의 패키지 구성 ] - 날짜와 시간을 다루는 핵심 클래스 제공

하위 패키지명
모두 immutable!
1. j.t.chrono : 표준(ISO)가 아닌 달력 시스템을 위한 클래스들을 제공하는 패키지
2. j.t.format : 날짜와 시간의 형식화, 파싱 클래스 제공
3. j.t.temporal : 날짜와 시간의 필드(field)와 단위(unit) 클래스 제공
4. j.t.zone : 시간대(time-zone) 클래스 제공

② j.t 패키지의 핵심클래스(주로 사용되는 클래스)

  • 날짜와 시간을 다루는 클래스를 분리(구별) (기존의 Calender나 Date는 날짜, 시간을 둘 다 한 클래스로 다뤘었다.)
  • j.t. 핵심클래스(LocalDate, LocalTime, LocalDateTime, ZonedDateTime(LocalDateTime + 시간대))는 모두 'Temporal, TemporalAccessor, TemperalAdjuster'라는 인터페이스를 구현한 클래스이다.

j.t 패키지의 핵심클래스
1. 날짜를 다루는 LocalDate 클래스
2. 시간을 다루는 LocalTime 클래스
3. 날짜 + 시간을 다루는 LocalDateTime 클래스
4. 날짜 + 시간 + 시간대를 다루는 ZonedDateTime 클래스

  1. 날짜를 다루는 LocalDate 클래스
//객체 생성
LocalDate d = LocalDate.now();
System.out.println(d); //2023-02-27

//년
int year = d.get(ChronoField.YEAR); //2023
//Temporal field를 달라고 하는데 이게 Chrono를 구현한거니까
d.getYear(); //2023
		
//월
int month = d.get(ChronoField.MONTH_OF_YEAR); //1~12 반환
System.out.println(month); //2
Month month2 = d.getMonth(); //열거형 enum
System.out.println(month2); //FEBRUARY
int month3 = d.getMonthValue(); //1~12 반환
System.out.println(month3); //2
		
//일
int day = d.getDayOfMonth();
System.out.println(day); //27
day = d.get(ChronoField.DAY_OF_MONTH);
System.out.println(day); //27
		
//요일
DayOfWeek dof = d.getDayOfWeek(); //enum
System.out.println(dof); //MONDAY
System.out.println(dof.getValue()); //1, 월(1) 화(2) ~ (6)토, (7)일

Ex) 생일이 오늘 기준으로 지났는지 여부를 체크한다면?

//시간: LocalTime은 안됨
LocalDate t = LocalDate.now(); //2023-02-27
int tyear = t.getYear();
LocalDate d = LocalDate.of(tyear, 2, 26);
//1998.02.26

System.out.println(t.isAfter(d)); //true
System.out.println(t.isBefore(d)); //false
System.out.println(t.isEqual(d)); //false
  1. 시간을 다루는 LocalTime 클래스
//객체 생성
LocalTime t = LocalTime.now();
System.out.println(t);
		
//시간
System.out.println(t.getHour());

//분
System.out.println(t.getMinute());

//초
System.out.println(t.getSecond());

//밀리초
System.out.println(t.get(ChronoField.MILLI_OF_SECOND));

//나노초
System.out.println(t.getNano());
System.out.println(t.get(ChronoField.NANO_OF_SECOND)); //위에랑 같은거

Ex) 1시간 10분 후?

t = t.plusHours(1);
t = t.plusMinutes(10);
System.out.println(t);

LocalDate & LocalTime 의 특징 정리
1. 특정 필드값을 변경하려면 with()으로 시작하는 메소드를 사용(항상 새로운 객체 반환)
2. 날짜와 시간의 비교 - isAfter(), isBefore(), isEqual(), compareTo()
3. 월의 범위는 1 ~ 12, 요일은 월요일(1) ~ 일요일(7)

  • Date 클래스에서는 1(일) ~ 7(토)
  • Calender 1(일) ~ 7(토)
  • LocalDate & LocalTime 1(월) ~ 7(일) : ISO 표준은 월요일이 1
  1. 날짜 + 시간을 다루는 LocalDateTime 클래스
  • LocalDate + LocalTime = LocalDateTime
//객체 생성
LocalDateTime dt = LocalDateTime.now();
System.out.println(dt); //2023-02-27T10:40:03.704389300 날짜, 시간 정보가 모두 들어감
  • LocalDate + LocalTime 둘을 합쳐서 -> LocalDateTime 객체를 만들 수 있다.
LocalDate d = LocalDate.now(); //날짜만
LocalTime t = LocalTime.now(); //시간만
		
방법1) 메소드 of()에 인자값으로 of(date, time) 주기
LocalDateTime dt = LocalDateTime.of(d, t);
		
방법2) 메소드 atTime(t)
LocalDateTime dt2 = d.atTime(t);
		
방법3) 메소드 atDate(d)
LocalDateTime dt3 = t.atDate(d);
		
방법4) 메소드 atStartOfDay()
LocalDateTime dt4 = d.atStartOfDay(); //00:00:00.000으로 셋팅
  • 절삭하는 메소드 truncatedTo()
    → 이전에는 set을 이용해 0으로 초기화 하던 것들을 truncate로 절삭하면 set과 같이 초기화 된다.
    → 매개변수: Temporal Unit 이므로 아래 예시에서는 매개변수로 ChronoUnit을 주었다.
//시간 밑으로 모두 절삭(분, 초, 나노초)
dt = dt.truncatedTo(ChronoUnit.HOURS);
System.out.println(dt); //2023-02-27T10:00
		
//날짜 밑으로 모두 절삭
dt = dt.truncatedTo(ChronoUnit.DAYS); 
System.out.println(dt); //2023-02-27T00:00
  1. 날짜 + 시간 + 시간대를 다루는 ZonedDateTime 클래스

③ j.t 패키지의 메소드
1) 객체를 생성하는 now(), of()
→ 핵심클래스(LocalDate, LocalTime, LocalDateTime, ZonedDateTime)는 모두 new 연산자로 객체를 생성하지 않는다.
→ 객체를 생성하기 위해서는 now()를 이용한다.(현재 시스템 날짜, 시간 기준으로 된 객체 생성)
→ 그럼 내가 원하는 날짜나 시간대의 객체를 생성하려면? of() 사용.
→ 이렇게 객체를 생성하는 메소드에는 now(), of() 두가지 방법 밖에 없다.

2) 특정 필드값(년, 월, 일, 시, 분, 초, 밀리세컨드, 요일 등)을 가져오는 두가지 메소드 get(), getXXX()
두가지 메소드를 쓸 수 있다. => 필드 설정 시 필드에 해당되는 값을 가져옴

  • get(필드): 범용 메소드, TemporalField 인터페이스를 매개변수로 받는다. 이 때 매개변수로는 ChronoField 열거형 상수를 사용하여 필드를 지정할 수 있다. Ex) get(TemporalField) 이므로 get(ChronoField.YEAR) 이런식으로 줘야 된다.
LocalDateTime now = LocalDateTime.now();
int year = now.get(ChronoField.YEAR);
  • get필드(): 단일 메소드, 해당 필드의 값을 반환하고, 필드 이름에 해당하는 getYear(), getMonth(), getDayOfMonth() 등의 메소드로 제공된다.
LocalDate now = LocalDate.now();
Month month = now.getMonth(); //getMonth() 메소드를 호출하여 Month 열거형 객체를 반환
int monthValue = now.getMonthValue(); //getMonthValue() 메소드를 호출하여 해당 월의 값을 정수로 반환

④ 인터페이스 TemporalField

  • 년, 월, 일 등의 날짜와 시간의 '필드'를 정의해 놓은 것
  • 이게 바로 인터페이스 'TemporalField' => 그리고 이 인터페이스를 구현한 클래스가 (열거형) 'ChronoField' 이다.

⑤ 인터페이스 TemporalUnit

  • TemporalUnit은 java.time 패키지에서 날짜와 시간의 단위를 정의하는 인터페이스이다.
  • 시간의 단위를 정의하고, 시간 차이를 계산하거나 특정 시간 단위로 시간을 조정하는 작업에 사용한다.
  • 인터페이스 'TemporalUnit'을 구현한 클래스가 (열거형) 'ChronoUnit' 이다.
  • 메소드 between() : 두 시간 사이의 시간 차이를 반환
  • 메소드 addTo() : 시간 값을 특정 시간 단위만큼 증가
LocalDate start = LocalDate.of(2022, Month.JANUARY, 1);
LocalDate end = LocalDate.of(2022, Month.JANUARY, 10);
long daysBetween = ChronoUnit.DAYS.between(start, end);

Ex) ChronoUnit.SECONDS, ChronoUnit.MINUTES, ChronoUnit.HOURS, ChronoUnit.DAYS 등

TemporalField와 TemporalUnit

  • java.time 패키지에서 날짜와 시간을 다루기 위한 인터페이스
  • TemporalField는 년, 월, 일 등 날짜와 시간의 특정 필드를 정의하는 인터페이스로 시간 값에서 필드 값을 가져오거나 필드 값을 설정하는 데 사용
  • TemporalUnit은 날짜와 시간의 단위를 정의하는 인터페이스로 시간 차이를 계산하거나 특정 시간 단위로 시간 값을 조정하는 데 사용

ChronoField와 ChronoUnit

  • ChronoField와 ChronoUnit은 각각 TemporalField와 TemporalUnit을 구현한 열거형 상수
  • ChronoField: 시간의 특정 필드를 정의,
  • ChronoUnit: java.time.temporal 패키지 소속, 시간의 기본 단위를 정의, 경우에 따라 총 며칠의 차이가 있는지 계산하고 싶을 때 사용(특정 날짜와 시간에서 지정된 단위의 값을 더하거나 뺄 때는 plus() 또는 minus()의 값과 함께 열거형 ChronoUnit 사용)

⑥ 간격을 다루는 클래스 Period & Duration

  • 날짜, 시간의 간격 클래스(Period, Duration)는 TemporalAmount 인터페이스를 구현한 클래스이다.

Period & Duration의 차이점

  • Duration 클래스는 특정 시간 간격을 나타내는 클래스로, 초와 나노초 단위의 값을 갖는다. 따라서 Duration 객체는 초와 나노초를 기준으로만 시간 간격을 표현할 수 있으며, 일, 시간, 분 단위의 값을 직접 가져올 수 없다.
  • 이와 달리 Period 클래스는 날짜를 기준으로 한 시간 간격을 나타내는 클래스로, 년, 월, 일 단위의 값을 직접 가져올 수 있다.
  • Period 클래스는 범용메소드 get()을 갖지만, Duration 클래스는 get()을 사용할 수 없다.

Duration 클래스에 get() 메소드가 없는 이유?

  • Period 클래스는 날짜를 기준으로한 시간 간격을 나타내는 클래스로, 년, 월, 일 단위의 값을 직접 가져올 수 있으므로 get 메소드를 사용하여 년, 월, 일 등의 값을 가져올 수 있다.
  • Duration 클래스는 초와 나노초 단위의 값을 기준으로 시간 간격을 표현하기 때문에 분, 시간 등의 단위를 직접 가져올 수 없다.
  • 따라서 Duration 객체를 사용하여 시간 간격을 구할 때는 toXXX 메소드를 통해 초와 나노초 값을 다른 단위로 변환하여 사용해야 한다.
  • 이 때문에 LocalTime 객체를 활용하여 Duration 객체의 값을 LocalTime 객체로 변환한 다음, LocalTime 객체에서 단일 메소드인 getXXX 메소드를 이용해 각 단위 값을 가져와 사용하는 것이다.

▶ Duration 클래스에서 toXXX 메소드를 이용해 초와 나노초 값을 다른 단위로 변환하는 예시

Duration duration = Duration.ofHours(3).plusMinutes(30); //3시간 30분

long minutes = duration.toMinutes(); // 시간을 분으로 변환
System.out.println(minutes); // 210

long seconds = duration.getSeconds(); // 초를 가져옴
System.out.println(seconds); // 12600

long millis = duration.toMillis(); // 밀리초로 변환
System.out.println(millis); // 12600000

long days = duration.toDays(); // 일 단위로 변환
System.out.println(days); // 3시간 30분은 하루 이상이므로 0

long hours = duration.toHours(); // 시간 단위로 변환
System.out.println(hours); // 3

1) 날짜와 날짜 사이의 간격을 다루는 Period 클래스

  • LocalDate, LocalDateTime 등의 차이는 계산할 수 있으나 LocalTime 같이 날짜 정보가 없는 객체의 시간차는 계산할 수 없음
  • 간격을 구하는 메소드 between(시작날짜, 끝 날짜) (끝 날짜는 포함 안 됨)

Ex1)

// 개강일
LocalDate o = LocalDate.of(2023, 1, 26);
		
// 오늘일
LocalDate t = LocalDate.now();
		
//날짜 - 날짜 간격(차)
Period p = Period.between(o, t); //앞의 날짜는 포함이 되고, 마지막 날짜는 포함이 안 됨, 포함하려면 +1 해준다.

// 개강일(2023-01-26) - 오늘일(2023-02-28) 간격
long year = p.get(ChronoUnit.YEARS);
System.out.println(year); //0
long month = p.get(ChronoUnit.MONTHS);
System.out.println(month); //1
long day = p.get(ChronoUnit.DAYS);
System.out.println(day); //2

Ex2)

LocalDate d1 = LocalDate.of(2014, 1, 1);
LocalDate d2 = LocalDate.of(2015, 12, 31);
		
//두 날짜 사이의 간격 체크: Period
Period p = Period.between(d1, d2); //d1은 포함 O, d2는 포함 x
System.out.println(d1); //2014-01-01
System.out.println(d2); //2015-12-31
System.out.println(p); //P1Y11M30D 1년, 11월, 30일의 간격이 있다는 것

//toTotalMonths()는 일은 무시하고 년과 월을 월 단위로 변환해 반환하는 메소드
System.out.println(p.toTotalMonths()); //총 23개월
  • 간격을 구하는 메소드 until()
    • between()과 같은 의미
    • Period 클래스는 이 클래스의 객체와 LocalDate, LocalTime, LocalDateTime 등의 객체 사이에 until() 메소드를 사용할 수 있다
LocalDate t = LocalDate.now();
LocalDate b = LocalDate.of(2023, 5, 26);
Period p = t.until(b);

2) 시간과 시간 사이의 간격을 다루는 Duration 클래스

  • 초/나노초 기반, 나노초 단위까지 계산하기에 특정 작업의 소요시간 계산에 유리함
  • 몇시몇분몇초 지났는지는 어떻게 계산하나? 메소드 between()

Ex1) 9:00:00 ~ 현재시간?

LocalTime oTime = LocalTime.of(9, 0, 0);
LocalTime tTime = LocalTime.now();
		
Duration d = Duration.between(oTime, tTime);

/*그런데 여기서 long hour = d.get(ChronoUnit.HOURS); 는 오류가 발생해 사용할 수 없다. 왜?
Duration 클래스에는 get 메소드가 없고, Duration의 객체 d 는 초와 나노초만 가져올 수 있기 때문!
Duration 클래스는 특정 시간 간격을 나타내는 클래스로,
이 클래스는 특정 단위 (예: 시간, 분, 초)의 값을 직접 가져올 수 있는 get 메서드를 제공하지 않는다.
long min = d.get(ChronoUnit.MINUTES); X
long sec = d.get(ChronoUnit.SECONDS); X
*/

//따라서 이렇게 만들어야 한다.
LocalTime diff = LocalTime.of(0, 0).plusSeconds(d.getSeconds());
int hour = diff.getHour();
int min = diff.getMinute();
int second = diff.getSecond();
int nano = d.getNano();

Ex2) 수업 시작한지 몇 초 지났는지?

LocalTime s = LocalTime.of(9, 0);
LocalTime t = LocalTime.now();
		
Duration d = Duration.between(s, t);
System.out.println(d); //PT3H7M4.3628194S
		
System.out.println(d.toHours()); //3hr
System.out.println(d.toMinutes()); //187m = 3*60 +5
System.out.println(d.toSeconds()); //11277
  • Duration 클래스 에서의 until()
    • Duration 클래스는 특정 시간 간격을 나타내는 클래스이므로, 이 클래스의 객체와 LocalDate, LocalTime, LocalDateTime 등의 객체 사이에 until() 메소드를 사용할 수 없다.
    • 그러나 Temporal 인터페이스를 구현한 LocalDate, LocalTime, LocalDateTime 등의 클래스에서만은 사용가능하다.

⑦ 메소드 정리

  • 객체 생성: now(), of()
  • 특정 필드값 가져오기: get(), getXXX()
  • 필드값 변경: plusXXX(), minusXXX(), plus(), minus(), with()
  • 날짜와 시간 비교: isAfter(), isBefore(), isEqual(), compareTo()

⑧ Instant 클래스(java.time.Instant)

  • 에포크 타임(1970-01-01 00:00:00 UTC) 부터 경과된 시간을 나노초 단위로 표현
  • 객체 선언 & 에포크 타임 출력하기
Instant now = Instant.now();
System.out.println(now); //2023-02-27T01:28:50.211084100Z
System.out.println(now.getNano()); //211084100 이게 에포크 타임
  • Date → Instant 변환하는 메소드: toInstant()
Date d = new Date();
Instant n = d.toInstant();
  • Instant → Date 변환
Instant instant = Instant.now(); // 현재 시각을 Instant 객체로 생성
Date date = Date.from(instant); // Instant 객체를 Date 객체로 변환

//Java 8 이후 Date 클래스 대신 LocalDateTime, ZonedDateTime 등을 사용하는 경우 Instant 객체를 변환하면?
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());

⑨ TemporalAdjusters 클래스(java.time.temporal)

  • 시간상의 조정자, 시간을 조정하는 클래스
  • 자주 사용되는 날짜, 시간을 계산(변경)하는 메소드를 미리 구현해놓은 클래스
//객체선언
LocalDate d = LocalDate.now();
System.out.println(d); //2023-02-27
  • 다음달 첫날을 나타내는 메소드: TemporalAdjusters.firstDayOfNextMonth()
System.out.println(d.with(TemporalAdjusters.firstDayOfNextMonth())); //2023-03-01
  • 이번달 첫날을 나타내는 메소드: TemporalAdjusters.firstDayOfMonth()
System.out.println(d.with(TemporalAdjusters.firstDayOfMonth())); //2023-02-01
  • 올해 첫날을 나타내는 메소드 : TemporalAdjusters.firstDayOfYear()
System.out.println(d.with(TemporalAdjusters.firstDayOfYear())); //2023-01-01
  • 내년 첫날을 나타내는 메소드 : TemporalAdjusters.firstDayOfNextYear()
System.out.println(d.with(TemporalAdjusters.firstDayOfNextYear())); //2024-01-01
  • 이번 달의 첫번째 월요일? TemporalAdjusters.firstInMonth(DayOfWeek.요일)
System.out.println(d.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY))); //2023-02-06
  • 이번 달 마지막 토요일? TemporalAdjusters.lastInMonth(DayOfWeek.요일)
System.out.println(d.with(TemporalAdjusters.lastInMonth(DayOfWeek.SATURDAY))); //2023-02-25
  • 다음에 올 금요일? TemporalAdjusters.next(DayOfWeek.요일)
System.out.println(d.with(TemporalAdjusters.next(DayOfWeek.FRIDAY))); //2023-03-03
  • 이번 달 세번째 토요일? TemporalAdjusters.dayOfWeekInMonth(몇번째인지 숫자, DayOfWeek.요일)
System.out.println(d.with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.SATURDAY))); //2023-02-18
  • 지난번 화요일은 며칠이었는지? TemporalAdjusters.previous(DayOfWeek.요일) & TemporalAdjusters.previousOrSame(DayOfWeek.요일)
System.out.println(d.with(TemporalAdjusters.previous(DayOfWeek.SATURDAY))); //당일 미포함 2023-02-25
		System.out.println(d.with(TemporalAdjusters.previousOrSame(DayOfWeek.SATURDAY))); //당일 포함 2023-02-25
  • 3일 뒤에 만난다면 3일 뒤는 언제일까?
    TemporalAdjuster 인터페이스와 Temporal 인터페이스를 사용하여 날짜를 수정하는 방법을 보여주는 예제로
    adjustInto() 메소드를 오버라이드하여 날짜를 수정하는 로직을 구현한다.
    adjustInto() 메소드는 Temporal 인터페이스를 반환하고, 이를 통해 plus 메소드를 이용하여 날짜를 수정한다.

참고
TemporalAdjuster 인터페이스를 구현하려면 adjustInto() 메소드를 오버라이드하여 날짜와 시간을 수정하는 로직을 구현해야 한다. 즉, adjustInto() 메소드는 주어진 Temporal 객체를 수정하고, 수정된 Temporal 객체를 반환해야 한다.
따라서, DayAfter3일 클래스에서 adjustInto() 메소드를 오버라이드하여 날짜를 수정하는 로직을 통해 TemporalAdjuster 인터페이스를 구현한 것. DayAfter3일 클래스의 adjustInto() 메소드에서는 입력으로 받은 Temporal 객체를 3일 뒤로 이동시킨 후, 수정된 Temporal 객체를 반환한다.
이와 같이 TemporalAdjuster 인터페이스를 구현하여 날짜를 수정할 수 있다. 이 방법을 사용하면 일정한 패턴에 따라 날짜를 자동으로 수정할 수 있으며, 코드의 가독성을 높이고 유지보수를 쉽게 할 수 있다.

//d = d.plusDays(3); 해도 되지만, 클래스를 만들어보자
d = d.with(new DayAfter3());
System.out.println(d);

class DayAfter3implements TemporalAdjuster{
	@Override
	public Temporal adjustInto(Temporal temporal) {
		return temporal.plus(3, ChronoUnit.DAYS);
	}	
}
profile
개발 공부중

0개의 댓글