chapter 10 날짜와 시간& 형식화 - [1. 날짜와 시간]

byeol·2022년 7월 29일
0

Java의 정석

목록 보기
9/9
post-thumbnail

오늘 내 블로그를 읽으며 복습을 해봤는데
생각보다 설명이 자세하지 않은거 같다.

1. 날짜와 시간

1.1 Calendar와 Date

Data는 날짜와 시간을 다룰 목적의 클래스 But 빈약하여
--> new 새로운 Calendar라는 클래스 제공, 몇 가지 단점 발견
--> JDK1.8부터 java.time패키지로 새로운 클래스 추가

Calendar은 추상클래스이다.
chapter7에서 배우는 내용이지만 추상클래스는 "미완성 설계도" "미완성 메서드를 포함"한 클래스이다.
=> 따라서 추상클래스는 인스턴스를 생성할 수 없다.
=> 따라서 상속을 통해서 자손에 의해 완성된다.

Calendar와 GregorianCalendar

=> Calendar를 상속받아 완전히 구현한 클래스 : GregorianCalendar, BuddhistCalendar 이 있다.
=> 그러나 메서드를 통해서 완전히 구현된 클래스의 인스턴스를 얻어야 한다.
=>Why? 왜? 아래 코드에서 1번처럼 직접 생성하지 않고?
메서드를 통해 2번처럼 불러올까?

class MyApplication{
  public static void main(String[] args){
   Calendar cal = new GregorianCalendar();//1.
   Calendar cal =Calendar.getInstance(); // 2.
 
 }
}

getInstance라는 메서드를 시스템의 국가와 지역 설정을 통해
태국인 경우 BuddhistCalendar클래스의 인스턴스를 태국을 제외한 나라의 경우 GregorianCalendar클래스의 인스턴스를 생성한다. 이는 현재 시스템의 설정에 따라 두 클래스를 바꿔서 하지 않고 할 수 있다는 효율성이 있다. 즉 최소한의 변경으로 프로그램이 동작할 수 있도록 하는 것이다.

Date와 Calendar간의 변환

앞서 말했지만 Date의 빈약함으로 Calendar이 생겼다고 했다. 그러나
여전히 Date를 필요로 하는 메서드들이 있기 때문에
Calendar->Date, Date->Calendar 변환하는 방법을 알아보자
① Calendar->Date

Calendar cal = Calendar.getInstance();
...
Date d = new Date(cal.getTimeInMillis())

② Date->Calendar

 Date d = new Date();
 ...
 Calendar cal= Calendar.getInstance();
 cal.setTime(d);

몇 가지 예시를 통해 배워보자

EX1

import java.util.*;
public class CalendarEx1 {
  public static void main(String[] args) {
	  Calendar today = Calendar.getInstance();
	  //int get (int field)
	  // 우리는 get의 매개변수로 Calendar에 정의된 static 상수를 넣는다!
	  System.out.println("이 해의 년도: "+ today.get(Calendar.YEAR));
	  System.out.println("월(0~11, 0:1월): "+today.get(Calendar.MONTH));
	  System.out.println("이 해의 몇 째 주: "+today.get(Calendar.WEEK_OF_YEAR));
	  System.out.println("이 달의 몇 째 주: "+today.get(Calendar.WEEK_OF_MONTH));
	  System.out.println("이 달의 몇 일: "+today.get(Calendar.DAY_OF_MONTH));
	  // DAY_OF_MONTH == DATE
	  System.out.println("이 달의 몇 일: "+today.get(Calendar.DATE));
	  System.out.println("이 해의 몇 일: "+today.get(Calendar.DAY_OF_YEAR));
	  System.out.println("요일(1~7,1:일요일): "+today.get(Calendar.DAY_OF_WEEK));
	  System.out.println("이 달의 몇번째 요일: "+today.get(Calendar.DAY_OF_WEEK_IN_MONTH));
	  System.out.println("오전_오후( 0: 오전, 1: 오후): "+today.get(Calendar.AM_PM));
	  System.out.println("시간(0~11) :"+today.get(Calendar.HOUR));
	  System.out.println("시간(0~23) :"+today.get(Calendar.HOUR_OF_DAY));
	  System.out.println("분(0~59): "+today.get(Calendar.MINUTE));
	  System.out.println("초(0~59): "+today.get(Calendar.SECOND));
	  System.out.println("1000분의 1초(0~999): "+today.get(Calendar.MILLISECOND));
	  System.out.println("TimeZone(-12~+12):"+(today.get(Calendar.ZONE_OFFSET)/(60*60*1000)));
	  //이달의 마지막 날을 구하기 위한 함수 getActualMaximum
	  System.out.println("이 달의 마지막날 :"+today.getActualMaximum(Calendar.DATE));
	 
	  
  }
}

👆 자, 여기서는 int get(int field)라는 메서드를 이용하여
원하는 필드 값을 얻어온다. 예를 들어 시간, 년, 월, 요일, 이 해의 몇 번째 요일, 이번 년도의 일, 현재의 일 등이 필드가 된다.
-> int get(Calendar.Calendar의 static 상수)

EX2

import java.util.*;

public class CalendarEx2 {
	public static void main(String[] args) {
		final String[] DAY_OF_WEEK = {"","일","월","화","수","목","금","토"};
		//요일 1~7,1:일요일
		
		Calendar date1= Calendar.getInstance();
		Calendar date2= Calendar.getInstance();
		
		//월은 0~11, 0:1월
		date1.set(2015,7,15);//date1.set(2015,Calendar.AUGUST,15);같음
		System.out.println("date1은 "+toString(date1)+DAY_OF_WEEK[date1.get(Calendar.DAY_OF_WEEK)]
				+"요일이고,");
		System.out.println("date2은 "+toString(date2)+DAY_OF_WEEK[date2.get(Calendar.DAY_OF_WEEK)]
				+"요일이다.");
		
		//두 날짜의 차이를 구해보자
		//getTimeInMillis는 1/1000 시간 단위 : 1초=>1000 , 2.3초=>2300 으로 결과가 나온다~!
		long difference =
				(date2.getTimeInMillis()-date1.getTimeInMillis())/1000;
		System.out.println("그 날(date1)부터 지금(date2)까지 "+difference+"초가 지났습니다.");
		System.out.println("일(day)로 계산하면 "+difference/(24*60*60)+"일입니다.");
		
	}
	
	private static String toString(Calendar date) {
		return date.get(Calendar.YEAR)+"년 "+ (date.get(Calendar.MONTH)+1)+"월 "
	            +date.get(Calendar.DATE)+"일 ";
	}

}

👆 여기서는 String배열을 이용하여 요일을 한글로 가져왔다.
Calendar의 static 상수는 일요일은 1 ~ 토요일은 7까지 나타내므로
숫자를 인덱스로 가져와서 String의 값을 매치하도록 설정하였다.

또한 Calendar 클래스에는 toString에 대한 함수가 없으므로
이름 별도의 메서드로 만들어서 main함수에서 사용하였다.

set 메서드를 통해서 현재 시간이 아닌 원하는 시간으로 설정이 가능하다
void set(int field, int value);
void set(int year, int month, int date);
void set(int year, int month, int date, int hourOfDay , int minute);
void set(int year, int month, int date, int hourOfDay, int minute, int second)

EX3

import java.util.*;
public class CalendarEx3 {
	public static void main(String[] args) {
     final int[] TIME_UNIT = {3600,60,1};//큰 단위를 앞에
     final String[] TIME_UNIT_NAME = {"시간","분","초"};
     
     Calendar time1= Calendar.getInstance();
     Calendar time2= Calendar.getInstance();
     
     //void set(int field, int value);
     time1.set(Calendar.HOUR_OF_DAY,10);
     time1.set(Calendar.MINUTE,20);
     time1.set(Calendar.SECOND,30);
     
     time2.set(Calendar.HOUR_OF_DAY,10);
     time2.set(Calendar.MINUTE,30);
     time2.set(Calendar.SECOND,10);
     
     System.out.println("time1 :"+time1.get(Calendar.HOUR_OF_DAY)+"시 "+
     time1.get(Calendar.MINUTE)+"분 "+time1.get(Calendar.SECOND)+"초");
     System.out.println("time2 :"+time2.get(Calendar.HOUR_OF_DAY)+"시 "+
    	     time2.get(Calendar.MINUTE)+"분 "+time2.get(Calendar.SECOND)+"초");
     
     long difference=
    		 Math.abs(time1.getTimeInMillis()-time2.getTimeInMillis())/1000;
     System.out.println("time1과 time2의 차이는 "+difference+"초 입니다.");
     
     String tmp="";
     for(int i=0;i<TIME_UNIT.length;i++) {
    	 tmp+=difference/TIME_UNIT[i]+ TIME_UNIT_NAME[i];
    	 difference%=TIME_UNIT[i];
     }
     System.out.println("시분초로 변환하면 "+tmp+"입니다" );
     
	}

}

👆 여기서 큰 틀은
밀리세컨즈*1000초로 구해서 가장 큰 단위(시)부터 나누고
나누고 난 후의 나머지는 다음 작은 단위(분)로 나눈다.
이후에 분으로 나는 나머지는 다음 작은 단위(초)로 나눈다.
따라서 시를 초의 단위인 36000초, 분의 60, 초의 1를 순서대로
갖는 배열(TIME_UNIT)을 만들었다.
String을 만들어 내용을 추가하기 위해
단위 배열의 값과 일치하는 단위, 인덱스를 갖도록 배열(TIME_UNIT_NAME)을 만들어 놓았다.

EX4

import java.util.*;
public class CalendarEx4 {
	public static void main(String[] args) {
	   Calendar date= Calendar.getInstance();
	   date.set(2015,7,31);
      
	   System.out.println(toString(date));
	   System.out.println("=1일 후=");
	   date.add(Calendar.DATE,1);
	   System.out.println(toString(date));
	   
	   System.out.println("=6달 전=");
	   date.add(Calendar.MONTH,-6);
	   System.out.println(toString(date));
	   
	   System.out.println("=31일 후(roll)=");
	   date.roll(Calendar.DATE,31);
	   System.out.println(toString(date));
	   
	   System.out.println("=31일 후(add)=");
	   date.add(Calendar.DATE,31);
	   System.out.println(toString(date));
	   
	}
	public static String toString(Calendar date) {
		return date.get(Calendar.YEAR)+"년 "+(date.get(Calendar.MONTH)+1)+"월 "
				+date.get(Calendar.DATE)+"일";
	}

}


👆 이 예시에서 배우는 것은 add(int field,int amout)와 roll(int field,int amout)의 차이를 이해하는 것이다.

자, 차이는 바로 다른 필드의 영향을 주는가 주지 않는가 이다.

  • add의 경우 다른 필드에 영향을 준다
    결과를 보자
    31일 add 한 후 3월 1일에서 4월 1일이 되었다.
    -> 다른 필드인 월(month)에 영향을 준다
  • roll의 경우 다른 필드에 영향을 주지 않는다.
    결과를 보자
    31일 roll한 후 3월 1일에서 3월 1일이 되었다.
    -> 다른 필드인 월(month)에 영향을 주지 않는다

EX5

import java.util.*;
public class CalendarEx5 {

	public static void main(String[] args) {
		Calendar date = Calendar.getInstance();
		
		date.set(2015,0,31);
		System.out.println(toString(date));
		date.roll(Calendar.MONTH,1);
		System.out.println(toString(date));
	}
  public static String toString(Calendar date) {
	   return date.get(Calendar.YEAR)+"년 "+(date.get(Calendar.MONTH)+1)+"월 "
               +date.get(Calendar.DATE)+"일";
  }
}

👆 그러나 위의 결과를 보면 roll을 통해 월에 한달을 더했다.
근데 다른 필드인 일에도 영향을 미친다. 그 이유는 일 필드가 말일이고 월을 더할 경우에는 일 필드에 영향을 미칠 수도 있다.
왜냐하면 1월은 말일이 31일이지만 2월은 28일이다.
3월은 말일이 31일이지만 4월은 30일이다.
이처럼 말일에서 월을 바꾸는 경우! 영향을 미친다.

EX6

import java.util.*;
public class CalendarEx6 {
  public static void main(String[] args) {
	if(args.length != 2) {
		System.out.println("Usage : java CalendarEx6 2015 9");
		return ;
	}  
	int year = Integer.parseInt(args[0]);
	int month = Integer.parseInt(args[1]);
	
	int START_DAY_OF_WEEK =0;
	int END_DAY = 0;
	
	Calendar sDay = Calendar.getInstance();
	Calendar eDay = Calendar.getInstance();
	//내가 agrs에 입력한 월이 12월이라면 
    //Calendar의 월 static 상수는 0~11, 0:1월 이므로
	//내가 입력한 month -1을 해줘야한다.
	sDay.set(year,month-1,1);
	eDay.set(year,month,1);
	eDay.add(Calendar.DATE,-1);//이로써 month-1의 말일을 만들어 준다.
	
	START_DAY_OF_WEEK=sDay.get(Calendar.DAY_OF_WEEK);//시작일의 요일을 저장
	END_DAY = eDay.get(Calendar.DATE);//마지막 일을 저장
	
	System.out.println("      "+args[0]+"년 "+args[1]+"월");
	System.out.println(" SU MO TU WE TH FR SA");
	
	//1일의 요일에 따라 앞에 공백을 찍는다.
	for(int i=1; i<START_DAY_OF_WEEK;i++) {
		System.out.print("   ");//공백 3칸
	}
	for(int i=1, n=START_DAY_OF_WEEK;i<=END_DAY;i++,n++) {
		System.out.print((i<10)? "  "+i:" "+i);// 일의 자리는 앞에 공백 하나 더 추가 됨.
		if(n%7==0)System.out.println();//요일이 일요일이면 그 다음은 다음줄에서 입력
		
	}
  }
  
}


👆 사실 여기서 집중해서 볼 부분은 총 3가지이다
하나, 우리는 Arguments를 입력하였고 월의 경우 Calendar의 static 상수가 0~11, 0:1월이므로 우리가 입력한 월의 값에 -1을 해줘야 한다.

, 우리는 여기서 STARTDAY_OF_WEEK와 END_DAY를 구하기 위한 방법을 보자. START_OF_DAY_OF_WEEK는 시작하는 1일의 요일을 구하는 것이므로 간단하다.
END_DAY의 경우 해달 월의 마지막 날을 간접적으로 구해야 한다.
그러기 위해서 다음달의 1일(_eDay.set(year,month,1);
)에 -1일(eDay.add(Calendar.DATE,-1);)을 하여 구하였다.
, 1일의 요일을 구했으니 그 앞 요일은 공백으로 채워야 한다.
이는 for문을 통해서 하였다.

EX7

import java.util.*;
public class CalendarEx7 {
	public static void main(String[] args) {
		if(args.length !=2) {
			System.out.println("Usage : java CalendarEx7 2015 11");
			return;
		}
		//String으로 저장된 값을 int정수형으로 변환
		int year = Integer.parseInt(args[0]);
		int month = Integer.parseInt(args[1]);
		
		Calendar sDay= Calendar.getInstance();
		Calendar eDay= Calendar.getInstance();
		
		sDay.set(year,month-1,1);
		//sDay.getActualMaximum(Calendar.DATE)을 통해 말일을 구한다.
		eDay.set(year,month-1,sDay.getActualMaximum(Calendar.DATE));
        //앞서 배운 예제는 공백을 채웠지만 이번 예제는 전 달의 이어지는 날짜와 다음 달의 이어지는 날짜를 채워본다.
		sDay.add(Calendar.DATE,-sDay.get(Calendar.DAY_OF_WEEK)+1);//1일이 속한 주의 일요일을 구해보자
	    eDay.add(Calendar.DATE,7-eDay.get(Calendar.DAY_OF_WEEK));//마지막이 속한 주의 토요일을 구해보자
        
	    System.out.println("       "+year+"년"+month+"월");
	    System.out.println(" SU MO TU WE TH FR SA");
	    
	    for(int n=1;sDay.before(eDay)||sDay.equals(eDay);sDay.add(Calendar.DATE,1),n++){
	    	//boolean equals(Object obj): 날짜의 값을 비교하여 그 결과를 반환한다.
	        // 전날까지 한다 또는 같을 때도 한다.
	    	
	    	System.out.print((sDay.get(Calendar.DATE)<10)?
	    			"  "+sDay.get(Calendar.DATE):" "+sDay.get(Calendar.DATE));
	    	if(n%7==0)System.out.println();
	    }
	
	}

}


👆이 예제는 앞 서 배웠던 달력과 차이가 있다.
앞 EX6는 1일부터 시작하고 달의 막일로 끝나지만
이 EX7은 전달의 일요일부터 다음 달의 토요일까지 이어지도록 만든 예제이다.
따라서 우리는
1일 속한 주인 전 달의 일요일의 날짜 : sDay.add(Calendar.DATE,-sDay.get(Calendar.DAY_OF_WEEK)+1);
막일이 속한 주인 다음 달의 토요일 날짜 : eDay.add(Calendar.DATE,7-eDay.get(Calendar.DAY_OF_WEEK));
로 하여 for문을 통해서 달력을 만들어간다.

EX8

public class CalendarEx8 {
    public static void main(String[] args) {
    	String date1="201508";
    	String date2="202207";
    	
    	int month1=Integer.parseInt(date1.substring(0,4))*12
    			+Integer.parseInt(date1.substring(4));
    	int month2=Integer.parseInt(date2.substring(0,4))*12
    			+Integer.parseInt(date2.substring(4));
    	System.out.println(date1+"과 "+date2+"의 차이는 "
    			+ Math.abs(month1-month2)+"개월입니다.");
    }
}

👆사실 년월 정도의 계산이라면 굳이 Calendar을 사용하지 않고도 저렇게 String의 substring의 통해 문자열을 뽑아낸 후 정수로 바꾼 후 계산하는 방법도 있다!

Ex9


public class CalendarEx9 {

	public static void main(String[] args) {
		System.out.println("1997. 1. 21. :"+getDayOfWeek(1997,1,21));
		System.out.println("2022. 7. 30. :"+getDayOfWeek(2022,7,30));
		System.out.println("1997. 1. 21.-2022. 7. 30. :"
		                       +dayDiff(1997,1,21,2022,7,30));
		
		System.out.println("2022. 7. 30. :"+convertDateToDay(2022,7,30));
		System.out.println("735778:"+convertDayToDate(735778));

	}

	public static int[] endOfMonth= {31,28,31,30,31,30,
			                         31,31,30,31,30,31};

    public static boolean isLeapYear(int year) {//윤달이냐 아니냐?
    	return ((year%4==0)&&(year%100!=0)||(year%400==0));
    	}
    
    public static int dayDiff(int y1, int m1, int d1, int y2, int m2, int d2) {
    	return convertDateToDay(y1,m1,d1)-convertDateToDay(y2,m2,d2);
    }
    public static int getDayOfWeek(int year, int month, int day) {//요일 반환
    	return convertDateToDay(year,month,day)%7+1;//1:일요일 ~7:토요일
    }
    public static String convertDayToDate(int day) {
    	//일수를 주면 년, 월, 일을 반환한다.
    	int year=1;
    	int month=0;
    	
    	while(true) {
    		int aYear = isLeapYear(year)? 366:365; // 윤달이면 이번 1년은 366일, 2월이 29일까지 있다.
    		if(day>aYear) {
    			day-=aYear;
    			year++;
    		}else {
    			break;
    		}
    	}//년도를 구함
    	//년도까지 구했다. 이제 day에는 366혹은 365보다 작은 값이 남아 있다.
    	
    	while(true) {
    		int endDay=endOfMonth[month];//월의 일수를 구함
    		
    		if(isLeapYear(year)&&month==1) endDay++;//만약 윤달이고 지금이 2월이면 일수는 28일이 아닌 29일
    		
    		if (day>endDay) {
    			day-=endDay;
    			month++;
    		}else {
    			break;
    		}
    	} // 월을 구했다.
    	
    	return year+"-"+(month+1)+"-"+day;
    	
    	
    }
    
    public static int convertDateToDay(int year, int month, int day) {
    	// 년, 월, 일을 주면 전체 일수를 반환한다.
    	int numOfLeapYear=0; // 윤달이 몇 번 있었는지에 대한 변수
    	
    	for(int i=1;i<year;i++) {
    		if(isLeapYear(year))
    			numOfLeapYear++;
    	}//여태까지 윤달이 있었다면 윤달이 몇 번 있는지 구한다.
    
    	int toLastYearDaySum = (year-1)*365+ numOfLeapYear;
    	// 전년도 까지의 일수에 대한 변수
    	
    	int thisYearDaySum=0;// 현재 년도의 일수에 대한 변수
    	
    	for(int i=0;i<month-1;i++) 
    		thisYearDaySum+=endOfMonth[i];// month를 바탕으로 이번년도의 일수를 구한다.
    	
    	if(isLeapYear(year)&&month>2)// 만약 설정한 month가 2월을 포함한다면, 즉 3월 이후라면
    		thisYearDaySum+=1; // 이번년도 일수에 1일을 더 더한다. 윤달의 경우, 2월이 29일까지 이므로
    	
    	thisYearDaySum+=day; 
    	
    	return toLastYearDaySum+thisYearDaySum;
    		
    	
    }
}


👆 이 예제는 날짜를 일수로 일수를 날짜로 바꾸는 함수,
두 날짜의 차이를 나타내는 함수 등이 등장한다.

profile
꾸준하게 Ready, Set, Go!

0개의 댓글