1. 소개
이 사용방법(예제)에서는 SimpleDateFormat 클래스 에 대해 자세히 살펴 보겠습니다 .
로케일 및 시간대를 처리 하기 위해 클래스가 노출하는 유용한 메서드뿐만 아니라 간단한 인스턴스화 및 형식 지정 스타일 을 살펴보겠습니다 .
2. 간단한 인스턴스화
먼저 새 SimpleDateFormat 개체 를 인스턴스화하는 방법을 살펴보겠습니다 .
4개의 가능한 생성자 가 있지만 이름에 따라 간단하게 유지하겠습니다. 시작하는 데 필요한 것은 원하는 날짜 패턴 의 문자열 표현입니다 .
다음과 같이 대시로 구분된 날짜 패턴부터 시작하겠습니다.
"dd-MM-yyyy"
이렇게 하면 해당 월의 현재 날짜, 해당 연도의 현재 월, 마지막으로 현재 연도로 시작하는 날짜 형식이 올바르게 지정됩니다. 간단한 단위 테스트로 새 포맷터를 테스트할 수 있습니다. 새로운 SimpleDateFormat 개체를 인스턴스화하고 알려진 날짜를 전달합니다.
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
assertEquals("24-05-1977", formatter.format(new Date(233345223232L)));
위의 코드에서 포맷터 는 밀리초를 사람 이 읽을 수 있는 날짜(1977년 5월 24일)로 변환합니다.
2.1. 팩토리 메소드
SimpleDateFormat 은 날짜 포맷터를 빠르게 빌드할 수 있는 편리한 클래스 이지만 DateFormat 클래스 getDateFormat() , getDateTimeFormat() , getTimeFormat() 에서 팩토리 메서드를 사용하는 것이 좋습니다 .
위의 예제는 이러한 팩토리 메서드를 사용할 때 약간 다르게 보입니다.
DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT);
assertEquals("5/24/77", formatter.format(new Date(233345223232L)));
위에서 알 수 있듯이 서식 옵션의 수는 DateFormat 클래스 의 필드에 의해 미리 결정됩니다 . 이는 서식 지정에 사용할 수 있는 옵션을 크게 제한하므로 이 문서에서 SimpleDateFormat 을 고수할 것 입니다.
2.2. 스레드 안전성
SimpleDateFormat 용 JavaDoc 은 명시적으로 다음과 같이 명시합니다.
날짜 형식은 동기화되지 않습니다. 각 스레드에 대해 별도의 형식 인스턴스를 만드는 것이 좋습니다. 여러 스레드가 형식에 동시에 액세스하는 경우 외부에서 동기화해야 합니다.
따라서 SimpleDateFormat 인스턴스는 스레드로부터 안전하지 않으며 동시 환경에서 주의해서 사용해야 합니다.
이 문제를 해결하는 가장 좋은 방법 은 ThreadLocal 과 함께 사용하는 것입니다 . 이렇게 하면 각 스레드가 자체 SimpleDateFormat 인스턴스로 끝나고 공유가 없기 때문에 프로그램이 스레드로부터 안전해집니다.
private final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal
.withInitial(() -> new SimpleDateFormat("dd-MM-yyyy"));
withInitial 메서드 의 인수 는 SimpleDateFormat 인스턴스 의 Provider입니다 . ThreadLocal 이 인스턴스를 생성해야 할 때마다 이 Provider를 사용합니다.
그런 다음 ThreadLocal 인스턴스 를 통해 포맷터를 사용할 수 있습니다 .
formatter.get().format(date)
ThreadLocal.get() 메서드 는 처음에 현재 스레드에 대한 SimpleDateFormat 을 초기화 한 다음 해당 인스턴스를 재사용합니다.
각 인스턴스의 사용을 하나의 특정 스레드로 제한하므로 이 기술을 스레드 제한 이라고 합니다.
동일한 문제를 해결하기 위한 두 가지 다른 접근 방식이 있습니다.
- 동기화된 블록 또는 ReentrantLock 사용
- 주문형 SimpleDateFormat 인스턴스 생성
이 두 가지 접근 방식은 모두 권장되지 않습니다. 전자는 경합이 높을 때 상당한 성능 저하를 일으키고 후자는 많은 개체를 생성하여 가비지 수집에 부담을 줍니다.
Java 8부터 새로운 DateTimeFormatter 클래스가 도입되었다는 점을 언급할 가치가 있습니다. 새로운 DateTimeFormatter 클래스는 변경할 수 없고 스레드로부터 안전합니다. Java 8 이상으로 작업하는 경우 새 DateTimeFormatter 클래스를 사용하는 것이 좋습니다.
3. 파싱 날짜
SimpleDateFormat 및 DateFormat 을 사용하면 날짜 형식을 지정할 수 있을 뿐만 아니라 작업을 되돌릴 수도 있습니다. parse 메소드를 사용하여 날짜의 문자열 표현을 입력하고 이에 상응하는 Date 객체 를 반환 할 수 있습니다.
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
Date myDate = new Date(233276400000L);
Date parsedDate = formatter.parse("24-05-1977");
assertEquals(myDate.getTime(), parsedDate.getTime());
여기 에서 생성자에 제공된 패턴은 parse 메서드 를 사용하여 구문 분석 된 날짜와 동일한 형식이어야 한다는 점에 유의하는 것이 중요합니다.
4. 날짜-시간 패턴
SimpleDateFormat 은 날짜 형식을 지정할 때 다양한 옵션을 제공합니다. JavaDocs 에서 전체 List을 사용할 수 있지만 일반적으로 사용되는 옵션 중 일부를 살펴보겠습니다.
편지 | 날짜 구성요소 | 예 |
---|---|---|
미디엄 | 월 | 12; 12월 |
와이 | 년도 | 94 |
디 | 요일 | 23; 월 |
시간 | 시간 | 03 |
미디엄 | 분 | 57 |
날짜 구성 요소가 반환 하는 출력은 String 내에서 사용되는 문자 수에 따라 크게 달라집니다 . 예를 들어 6월을 봅시다. 날짜 문자열을 다음과 같이 정의하면
"MM"
그러면 결과는 숫자 코드 – 06으로 나타납니다. 그러나 날짜 문자열에 다른 M을 추가하면 다음과 같습니다.
"MMM"
그런 다음 형식이 지정된 결과 결과 Jun 이라는 단어로 나타납니다 .
5. 로케일 적용
SimpleDateFormat 클래스는 또한 생성자 가 호출될 때 설정되는 광범위한 로케일을 지원합니다 .
프랑스어로 날짜 형식을 지정하여 이를 실제로 적용해 보겠습니다. Locale.FRANCE 를 생성자 에 제공하는 동안 SimpleDateFormat 개체를 인스턴스화합니다 .
SimpleDateFormat franceDateFormatter = new SimpleDateFormat("EEEEE dd-MMMMMMM-yyyy", Locale.FRANCE);
Date myFriday = new Date(1539341312904L);
assertTrue(franceDateFormatter.format(myFriday).startsWith("vendredi"));
주어진 날짜(수요일 오후)를 제공함으로써 francDateFormatter 가 날짜 형식을 올바르게 지정했다고 주장할 수 있습니다. 새 날짜는 Vendredi 로 올바르게 시작합니다 . 프랑스어는 금요일입니다!
생성자 의 로케일 버전 에서 약간의 문제점을 지적할 가치가 있습니다. 많은 로케일이 지원되지만 전체 범위가 보장되지는 않습니다 . Oracle은 DateFormat 클래스의 팩터리 메서드를 사용하여 로케일 범위를 보장할 것을 권장합니다.
6. 시간대 변경
SimpleDateFormat 은 DateFormat 클래스를 확장 하므로 setTimeZone 메서드 를 사용하여 시간대를 조작 할 수도 있습니다 . 이를 실제로 살펴보겠습니다.
Date now = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEE dd-MMM-yy HH:mm:ssZ");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Europe/London"));
logger.info(simpleDateFormat.format(now));
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
logger.info(simpleDateFormat.format(now));
위의 예에서는 동일한 SimpleDateFormat 개체 의 서로 다른 두 시간대에 동일한 날짜 를 제공합니다. 또한 시간대 차이를 나타내기 위해 패턴 문자열 끝에 'Z' 문자를 추가했습니다 . 그러면 format 메서드 의 출력 이 사용자에 대해 기록됩니다.
실행을 누르면 두 시간대를 기준으로 현재 시간을 볼 수 있습니다.
INFO: Friday 12-Oct-18 12:46:14+0100
INFO: Friday 12-Oct-18 07:46:14-0400
7. 요약
이 예제에서는 SimpleDateFormat 의 복잡성에 대해 자세히 살펴보았습니다 .
SimpleDateFormat 을 인스턴스화 하는 방법과 패턴 문자열 이 날짜 형식에 미치는 영향 을 살펴보았습니다 .
마지막 으로 시간대 사용을 실험하기 전에 출력 문자열 의 로케일을 변경하면서 놀았습니다 .
항상 그렇듯이 완전한 소스 코드는 GitHub 에서 찾을 수 있습니다 .