1

아래의 코드를 사용하여 날짜가 잘못 지정된 경우가있는 것으로 나타났습니다. 그리고 그것을 더욱 이상하게 만들기 위해 때로는 타임 스탬프가 올바른 날짜를 가지며 timeStampCopy는 잘못된 날짜를 가지며 그 반대의 경우도 마찬가지입니다.Java Calendar.setTime()이 산발적으로 잘못된 시간을 설정하는 이유는 무엇입니까?

public static Timestamp method(String date, DateFormat dateFormat) throws Exception {   

     // date is always "2017-02-17"  

     // original 
     GregorianCalendar gCal = new GregorianCalendar(); 
     gCal.setTime(dateFormat.parse(date)); 
     Timestamp timeStamp = new Timestamp(gCal.getTimeInMillis()); 

     // copy 
     GregorianCalendar gCalCopy= new GregorianCalendar(); 
     gCalCopy.setTime(dateFormat.parse(date)); 
     Timestamp timeStampCopy = new Timestamp(gCalCopy.getTimeInMillis()); 

     if (!timeStamp.toString().contains("2017-02-17")) 
      System.out.println(timeStamp.toString()); 
     if (!timeStampCopy.toString().contains("2017-02-17")) 
      System.out.println(timeStampCopy.toString()); 

     return timeStamp; 

    } 

나는 무엇이 그 원인이 될지 모르겠지만 Date 객체를 사용하여이 문제를 시도했지만 동일한 문제가 있습니다. 나는 그것이 파싱 문제 일 수 있다고 생각했으나 같은 일을 두 번하고 있기 때문에 잘 모르겠습니다.

다음은 내가 갖는 값의 일부입니다 :

timeStamp is:  2017-02-17 00:00:00.0 
timeStampCopy is: 1700-02-17 00:00:00.0 
+0

입력 내용은 무엇입니까 –

+4

스레드간에'DateFormat' 인스턴스를 공유하고 있습니까? –

+0

@AndyTurner 예! 나는 갑자기 제대로 작동하는 것처럼 보이는 메서드에'synchronized'를 추가 할 때 어떤 이유로 추가하려고했습니다. 왜 그래도. – Overclock

답변

3

당신은 스레드 사이의 DateFormat 인스턴스를 공유하는 것을 말한다. Javadoc에 따르면

:

날짜 형식은 동기화되지 않습니다. 각 스레드에 대해 별도의 형식 인스턴스를 만드는 것이 좋습니다. 여러 스레드가 동시에 형식에 액세스하는 경우 외부에서 동기화해야합니다. 이것은 DateFormat 인스턴스에 액세스 외부 동기화 참조하는

참고 하지 방법. DateFormat 인스턴스를 다른 용도로 사용하지 않는 경우에만 synchronized 메서드를 만들면이 문제가 해결됩니다.

다음 중 하나를 수행 할 수 있습니다 :

  • 명시 적으로 DateFormat 인스턴스를 사용하는 모든 코드를 주위 동기화 (당신이 잠금을 기대하는 문서화하기 위해, 변수에 @GuardedBy 주석을 추가 할 가치가 그것을 사용하기 전에 개최되는);
  • 변수 유형을 ThreadLocal<DateFormat>으로 변경하고 (공유 변수를 적절하게 초기화하십시오.) 각 스레드가 DateFormat의 자체 사본을 갖도록합니다.

각 스레드가 다른 스레드와 독립적으로 진행할 수 있기 때문에 후자의 방법은 경합이 적습니다. 또한 우연히 동기화를 생략 할 수 없음을 의미합니다.

그러나 스레드 안전성이 부족한 DateFormat과 같은 문제가 발생한 날짜와 시간을 처리하기위한 더 나은 라이브러리가 있습니다. Java 8에는 java.time API가 있습니다. 이전 버전의 Java에는 Jodatime이 있습니다.

+0

모든 것이 좋지만 마지막 문장입니다. Joda-Time 프로젝트는 현재 유지 관리 모드에 있으며 java.time 클래스로의 마이그레이션을 권장합니다. 이전 버전의 Java (6 & 7)의 경우 [ThreeTen-Backport] (http://www.threeten.org/threetenbp/) 프로젝트를 사용하십시오. java.time 기능의 대부분은 거기에 다시 포팅됩니다. [ThreeTenABP] (https://github.com/JakeWharton/ThreeTenABP) 프로젝트에서 Android에 더욱 적합합니다. –

1

Answer by Turner은 정확하며 받아 들여야합니다.

java.time는 스레드 안전

클래스는 불변의 객체를 사용하여 그들에게 본질적으로 스레드 안전을함으로써이 문제를 해결할 java.time.

LocalDate ld = LocalDate.of("2017-02-17"); 
ZoneId z = ZoneId.of("America/Montreal"); 
ZonedDateTime zdt = ld.atStartOfDay(z); 

toString를 호출하여 표준 ISO 8601 형식의 문자열을 생성합니다. 다른 형식의 경우 DateTimeFormatter 클래스를 사용하십시오. 많은 예제와 토론을 위해 스택 오버플로를 검색하십시오. 스레드에 대해 걱정할 필요가 없습니다. 모든 스레드로부터 안전합니다.

UTC의 값은 Instant입니다.

Instant instant = zdt.toInstant() ; 

java.sql.Timestamp을 사용할 필요가 없습니다. 최신 JDBC 드라이버는 toObject 및 setObject 메소드를 통해 java.time 유형을 처리 할 수 ​​있습니다. 이전 드라이버의 경우 이전 클래스에 추가 된 새 메서드를 사용하여 변환합니다. java.time

소개


java.time 프레임 워크는 나중에 자바 8에 내장되어 있습니다. 이 클래스는 java.util.Date, Calendar, & SimpleDateFormat과 같이 문제가있는 이전 legacy 날짜 - 시간 클래스를 대체합니다.

maintenance mode에있는 Joda-Time 프로젝트는 java.time 클래스로의 마이그레이션을 권장합니다.

자세히 알아 보려면 Oracle Tutorial을 참조하십시오. 그리고 많은 예제와 설명을 위해 스택 오버플로를 검색하십시오. 사양은 JSR 310입니다.

어디에서 java.time 클래스를 얻을 수 있습니까?

  • Java SE 8SE 9 이후
    • 내장한다.
    • 번들로 구현 된 표준 Java API의 일부입니다.
    • Java 9에는 몇 가지 사소한 기능과 수정 사항이 추가되었습니다.
  • Java SE 6 및 java.time 기능의 대부분
  • Android

ThreeTen-Extra 프로젝트는 추가 클래스와 java.time를 확장합니다. 이 프로젝트는 향후 java.time에 추가 될 수있는 가능성을 입증합니다. 여기에 Interval, YearWeek, YearQuartermore과 같은 유용한 클래스가 있습니다.