글에 들어가기 앞서 알아두면 좋을 지식
UNIX time이란?
UNIX time 이란, 1970년 1월 1일 00:00:00 UTC 로부터 현재까지의 누적된 초(seconds) 값을 의미한다.
UNIX time 은 UNIX 운영체제를 개발한 벨 연구소에서 정의한 개념이다.
POSIX 시간이나 Epoch 시간이라고 부르기도 한다.
time zone 이란?
영국의 그리니치 천문대를 기준으로 한 국제적인 표준 시간의 기준으로 쓰이는 시각을 UTC라고 한다.
시간대(time zone)는 UTC를 기준으로 지역에 따른 시간의 차이,
다시 말해 지구의 자전에 따른 지역 사이에 생기는 낮과 밤의 차이를 인위적으로 조정하기 위해 고안된 시간의 구분선을 일컫는다.
서울의 경우는 +9, 그러니까 UTC시간에 +9를 해주면 된다.
다른 시간대의 컴퓨터를 이용해 데이터를 주고 받을 때에는 시간대의 차이를 생각해야한다.대부분의 프로그래밍 언어에선 time zone과 관련된 기능들을 제공한다.
JAVA의 정석 10장 - 날짜와 시간
java는 날짜와 시간을 다루는 클래스들을 제공해왔다.
JDK1.0에서부터 Date 클래스를 제공했는데 JDK 1.0이 제공하는 클래스의 기능은 지금과 비교할 수 없을 정도로 빈약했다.
Data 클래스 역시 기능이 부족했기 때문에 Calendar라는 새로운 클래스를 그 다음 버전인 JDK1.1부터 제공하기 시작했다.
그러나 Calendar 클래스 역시 단점들이 존재했고,
그리고 1.8부터는 java.time 패키지로 기존의 단점들을 개선한 새로운 클래스들이 추가되었다.
java.util.Date
날짜와 시간을 다룰 목적으로 JDK1.0부터 제공되어온 클래스이다.
현재는 Java Api 문서에 ‘deprecated’가 붙어있다.
(더 이상 지원하지 않거나, 문제점이 있어 사용을 권장하지 않음)
그러나 Date는 자바의 탄생부터 지금까지 계속해서 사용되어 왔고,
몇몇 라이브러리나 메서드 함수에선 Date 타입을 계속해서 사용하고 있으므로 간단히 배워보자.
생성자
Date()
Date(long mesc)
//JWT 만료시간 설정 코드
Date expiration = new Date(System.currentTimeMillis() + ACCESS_EXP_TIME);
기본 생성자는 현재 시간으로 Date 객체를 생성한다.
long 타입 숫자를 넣으면 Posix 시간부터 mesc를 1/1000초 단위로 경과한 시간으로 생성한다.
메소드
boolean after(Date when) //Date인스턴스의 날짜가 인자의 날짜 이후면 true, 아니면 false 반환
boolean before(Date when) //after()과 반대로 작동
int compareTo(Date anotherDate) //다른 Date 객체와 비교하여 음수, 양수, 0 반환
boolean equals(Object obj) //날짜가 같은지 비교
long getTime() //mesc 값을 반환한다.
void setTime() //time 시간을 재설정한다.
java.util.Calendar
Date의 문제점을 해결하기 위해 JDK1.1에 바로 추가된 클래스이다.
Calendar는 추상클래스이기 때문에 직접 객체를 생성할 수 없다.
메서드를 통해서 환전히 구현된 클래스의 인스턴스를 얻어야 한다.
Calendar cal = Calendar.getInstance();
Calendar 클래스는 다음과 같은 static 상수들을 가진다.
int year = Calendar.YEAR //연도
int month = Calendar.MONTH //월 (0~11)
int date = Calendar.DATE //날짜 (1~31)
int woy = Calendar.WEEK_OF_YEAR; // 이번 연도의 몇번째 주인지
int wom = Calendar.WEEK_OF_MONTH;// 이번 달의 몇번째 주인지
int doy = Calendar.DAY_OF_YEAR; // 이번 연도의 몇번째 날인지
int dom = Calendar.DAY_OF_MONTH;// 이번 달의 몇번째 날인지
int dow = Calendar.DAY_OF_WEEK;// 이번 주의 몇번째 날인지 = 요일 (일요일 1, 토요일 7)
int hour12 = Calendar.HOUR; // 현재시간 (12시간 단위)
int hour24 = Calendar.HOUR_OF_DAY;// 현재시간 (24시간 단위)
int minute = Calendar.MINUTE; // 현재 분
int second = Calendar.SECOND; // 현재 초
int milliSecond = Calendar.MILLISECOND; // 현재 초 (1/1000 단위)
int timeZone = Calendar.ZONE_OFFSET; // gmt기준 시차(천분의 일초 단위), 한국은 +9
주의해야할 점은 월(month)의 범위가 0~11이라는 점이다..
(근데 날짜는 1부터 시작한다..)
boolean after(Object when)
boolean before(Object when)
boolean equals(Object obj)
Calendaer 클래스 역시 after(), before(), equals()과 같은 메서드를 오버라이딩해 제공한다.
Calendar cal = Calendar.getInstance();
int second = cal.get(Calendar.SECOND); // 현재 초 가져오기
long timeInMillis = cal.getTimeInMills(); // POSIX 표준시간 가져오기
void set(int field, int value) // 현재 객체의 특정 필드를 새롭게 설정한다.
void set(int year, int month, int date) // 현재 객체의 연도, 월, 일 값을 새롭게 설정한다.
void set(int year, int month, int date, int hour, int minute, int second)
// 현재 객체의 연도, 월, 일, 시, 분, 초 값을 새롭게 설정한다.
get() 메서드에 위의 상수를 넣어 호출할 경우 해당 상수값을 반환한다.
또한 getTimeInMills()를 사용해 표준시간을 가져올 수 있다.
set() 메서드를 이용하면 특정 필드의 값 또는 날짜나 시간을 바꿀 수 있다.
Date와 Calendar간의 변환
// Calendar를 Date로 변환
Calendar cal = Calendar.getInstance();
Date d = cal.getTime(); // 1
Date d = new Date(cal.getTimeInMills()); // 2
// Date를 Calendar로 변환
Date d = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(d);
다음과 같은 메서드들을 이용해 Date와 Calender 객체를 서로 변환할 수 있다.
형식화 클래스
날짜를 형식에 맞춰 출력하려면 Calendar를 이용해서
년, 월, 일, 시, 분, 초를 각각 별도로 얻어 조합하는 복잡한 과정을 거쳐야 한다.
자바에서는 이런 문제를 쉽게 해결할 수 있도록 형식화 클래스를 제공한다.
java.text.DecimalFormat
숫자를 형식화하는데 사용된다.
숫자 데이터를 정수, 부동소수점, 금액 등의 다양한 형식으로 표현해준다.
반대로 일정한 형식의 텍스트 데이터를 숫자로도 변환해준다.
double number = 1234567.89;
DecimalFormat df = new DecimalFormat("#.#E0");
String result = df.format(number); //1.2E6
String money = "1,000,000";
DecimalFormat df = new DecimalFormat("#,###,###");
try{
Number result = df.parse(money);
int m = result.intValue();
System.out.println(m); //1000000
} catch(Exception e){}
원하는 출력 형식의 패턴을 작성해 인스턴스를 생성하고 format 메서드를 호출하면 된다.
텍스트를 숫자로 변환할때도 똑같이 인스턴스를 생성한 후 parse()메서드를 호출하면 된다.
java.text.SimpleDateFormat
날짜를 출력할때 사용한다.
Date today = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String result = df.format(today);
String date = "2024-05-21";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
try{
Date d = df.parse(date);
System.out.println(d.getTime()); //
} catch(Exception e){}
원하는 출력형식의 패턴을 작성하여 SimpleDateFormat 인스턴스를 생성한 다음,
출력하고자 하는 Date 인스턴스를 가지고 format(Date d)를 호출하면 변환된 문자열을 얻을 수 있다.
역시 마찬가지로 parse() 메서드를 이용해 문자열을 Date 인스턴스로 바꿀 수 있다.
책에선 ChoiceFormat, MessageFormat 등의 클래스도 다뤘지만
지금은 그냥 이런게 있다. 정도만 알고 필요할때 찾아서 사용할 수 있으면 될 것 같다.
java.time 패키지
JDK1.8에서 Date와 Calendar의 단점을 해소하기 위해 추가된 클래스이다.
다음과 같은 4개의 하위 패키지를 가지고 있다.
- java.time.chrono
- java.time.format
- java.time.temporal
- java.time.zone
위의 패키지에 속한 클래스들의 가장 큰 특징은 String 클래스처럼 불변(immutable)이라는 것이다.
그래서 날짜나 시간을 변경하는 메서드들은 기존의 객체를 변경하는 것이 아닌, 새로운 객체를 반환한다.
java.time의 핵심 클래스
- LocalDate : 날짜
- LocalTime : 시간
- LocalDateTime : 날짜 + 시간
- ZonedDateTime : 날짜 + 시간 + 시간대(time-zone)
LocalDate와 LocalTime
java.time 패키지의 가장 기본이 되는 클래스이며, 나머지 클래스드은 이들의 확장이다.
객체 생성하기
LocalDate date = LocalDate.now();
LocalDate date = LocalDate.of(2024,5,21);
java.time 패키지에 속한 클래스의 객체를 생성하는 가장 기본적 방법은 now()와 of()를 사용하는 것이다.
now()는 현재 날짜와 시간을 저장하는 객체를 생성한다.
of()는 해당 필드의 값을 순서대로 지정해주기만 하면 된다.
static LocalDate of(int year, Month month, int dayOfMonth)
static LocalDate of(int year, int month, int datOfMonth)
static LocalTime of(int hour, int min)
static LocalTime of(int hour, int min, int sec)
static LocalTime of(int hour, int min, int sec, int nanoOfSecond)
둘다 static 메서드이며, of()는 위와 같은 여러 가지 버전이 제공된다.
LocalDate date = LocalDate.parse("2024-05-21");
LocalTime time = LocalTime.parse("00:00:00");
parse()를 이용하면 문자열을 날짜와 시간으로 변환할 수도 있다.
특정 필드의 값 가져오기
LocalDate와 LocalTime 객체에서 특정 필드의 값을 가져올 때는 다음과 같은 메서드를 사용한다.
getYear(), getMonthValue(), getMonth() 등의 메서드가 존재한다.
(monthValue의 달은 1부터 12로 표현된다.)
그 외에도 get()과 getLong()이 있는데 원하는 필드를 인자로 넣어 직접 지정할 수 있다.
필드의 값 변경하기
특정 필드 값을 변경하려면, 다음과 같이 with로 시작하는 메서드를 사용하면 된다.
date = date.withYear(2000);
time = time.withHour(12);
앞서 언급한것처럼 필드를 변경하는 메서드들은 항상 새로운 객체를 생성해서 반환하므로
대입 연산자를 사용해야 한다는 사실을 잊으면 안된다.
이외에도 특정 필드에 값을 더하거나 빼는 plus(), minus() 메서드 함수도 존재한다.
날짜와 시간의 비교
LocalDate와 LocalTime도 compareTo()가 적절히 오버라이딩되어 있어서 compareTo()로 비교할 수 있다.
그리고 isAfter(), isBefore(), isEqual()과 같은 보다 편리하게 비교할 수 있는 메서드도 존재한다.
Instant 클래스
Instant now = Instant.now();
long epochSec = now.getEpochSecond();
int nano = now.getNano();
Instant 클래스는 POSIX 시간으로부터 경과된 시간을 나노초 단위로 표현한다.
Instant 클래스는 시간을 초 단위와 나노초 단위로 나누어 저장하며,
항상 UTC를 기준으로 하기 때문에 LocalTime과 차이가 있을 수 있다.
시간대를 고려해야 하는 경우 OffsetDateTime을 사용하는 것이 더 나은 선택일 수 있다.
LocalDateTime과 ZonedDateTime
LocalDate와 LocalTime을 합쳐 놓은 것이 LocalDateTime 이고,
LocalDateTime에 시간대(time zone)을 합친 것이 ZonedDateTime이다.
LocalDate date = LocalDate.of(2024,05,21);
LocalTime time = LocalTime.of(00,00,00);
LocalDateTime dt = LocalDateTime.of(date,time);
LocalDateTime dt2 = date.atTime(time);
LocalDateTime dt3 = time.atDate(date);
LocalDateTime dateTime = LocalDateTime.of(2024,05,21,00,00,00);
LocalDateTime today = LocalDateTime.now();
위 코드처럼 LocalDate와 LocalTime을 만든 후 합치면서 생성하거나
of(), now()를 통해 직접 생성할 수도 있다.
LocalDateTime dt = LocalDateTime.of(2024,05,21,00,00,00);
LocalDate date = dt.toLocalDate();
LocalTime time = dt.toLocalTime();
혹은 위 코드처럼 LocalDateTime을 만든 후 LocalDate와 LocalTime으로 변환할 수도 있다.
LocalDateTime으로 ZonedDateTime 만들기
ZoneId zid = ZoneId.of("Asia/Seoul");
ZonedDateTime zdt = dateTime.atZone(zid);
다음처럼 ZoneId를 이용해 시간대 정보를 추가하면 ZonedDateTime을 얻을 수 있다.
OffsetDateTime
ZonedDateTime은 ZoneId로 구역을 표현하는데,
ZoneId가 아닌 ZoneOffset을 사용하는 것이 OffsetDateTime이다.
ZoneId는 일광절약시간처럼 시간대와 관련된 규칙을을 포함하는데,
ZoneOffSet을 이용해 시간의 차이로만 구현하는 것이 더 안전하다.
서로 다른 시간대에 존재하는 컴퓨터간의 통신에는 OffsetDateTime을 사용하자.
TemporalAdjusters
앞서 plus(), minus()와 같은 메서드로 날짜와 시간을 계산할 수 있다고 배웠다.
그러나 지난 주 토요일, 이번달의 3번째 금요일은 며칠인지와 같은 계산을
plsu(), minus()로 구현하는것은 불편하다.
그래서 자주 쓰일만한 날짜 계산들을 대신 해주는 메서드를 정의해놓은 것이
TemporalAdjusters 클래스이다.
이 클래스들은 TemporalAdjuster 인터페이스를 상속받는다.
LocalDate with(TemporalAdjuster adjuster)
그런데 with() 메서드는 이 인터페이스가 들어가도록 구현되어 있으므로
이 인터페이스를 직접 구현하면 날짜계산 메서드를 직접 만들 수도 있다.
파싱과 포맷
형식화와 관련된 클래스들은 java.time.format 패키지에 들어있는데,
이 중 DateTimeFormatter가 핵심이다.
이 클래스에는 자주 쓰이는 다양한 형식들을 기본적으로 정의하고 있으며,
또 직접 정의해서 사용할 수도 있다.
LocalDate date = LocalDate.of(2024,05,21);
String yyyymmdd = DateTimeFormatter.ISO_LOCAL_DATE.format(date); // 2024-05-21
String ymd = date.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2024-05-21
String dateString
LocalDate date = LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
format() 메소드를 이용해 문자열을 시간으로 변환할 수 있으며
parse() 메소드를 이용해 원하는 형태의 문자열로도 바꿀 수 있다.
ofPattern() 메소드를 이용해 형태를 직접 지정해줄 수도 있다.
'JAVA' 카테고리의 다른 글
JAVA의 정석 정독하기 #12.1 - Generices (0) | 2023.11.18 |
---|---|
JAVA의 정석 정독하기 #11 - Collection Framework (0) | 2023.11.17 |
JAVA의 정석 정독하기 #9 - java.lang 패키지와 유용한 클래스 (1) | 2023.11.14 |
JAVA의 정석 정독하기 #8 - 예외처리 (1) | 2023.11.14 |
JAVA의 정석 정독하기 #7 - 상속과 다형성 (1) | 2023.11.12 |