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 에서 찾을 수 있습니다 .

Generic footer banner