[java] Java를 사용하여 캘린더 TimeZones를 처리하는 방법은 무엇입니까?

내 애플리케이션에서 가져온 타임 스탬프 값이 있습니다. 사용자는 지정된 로컬 TimeZone에있을 수 있습니다.

이 날짜는 주어진 시간이 항상 GMT라고 가정하는 WebService에 사용되기 때문에 사용자의 매개 변수를 say (EST)에서 (GMT)로 변환해야합니다. 키커는 다음과 같습니다. 사용자는 TZ를 알지 못합니다. 그는 WS에 보낼 생성 날짜를 입력하므로 필요한 것은 다음과 같습니다.

사용자 입력 : 2008 년 5 월 1 일 오후 6:12 (EST)
WS에 대한 매개 변수는 2008 년 5 월 1 일 오후 6:12 (GMT) 여야 합니다 .

TimeStamps는 기본적으로 항상 GMT로되어 있다는 것을 알고 있지만, 매개 변수를 보낼 때 TS (GMT로되어 있어야 함)에서 캘린더를 만들었더라도 사용자가 GMT에 있지 않는 한 시간은 항상 꺼져 있습니다. 내가 무엇을 놓치고 있습니까?

Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
...
private static java.util.Calendar convertTimestampToJavaCalendar(Timestamp ts_) {
  java.util.Calendar cal = java.util.Calendar.getInstance(
      GMT_TIMEZONE, EN_US_LOCALE);
  cal.setTimeInMillis(ts_.getTime());
  return cal;
}

이전 코드를 사용하면 결과적으로 다음과 같은 결과를 얻을 수 있습니다 (쉽게 읽을 수있는 짧은 형식).

[2008 년 5 월 1 일 오후 11:12]



답변

public static Calendar convertToGmt(Calendar cal) {

    Date date = cal.getTime();
    TimeZone tz = cal.getTimeZone();

    log.debug("input calendar has date [" + date + "]");

    //Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT 
    long msFromEpochGmt = date.getTime();

    //gives you the current offset in ms from GMT at the current date
    int offsetFromUTC = tz.getOffset(msFromEpochGmt);
    log.debug("offset is " + offsetFromUTC);

    //create a new calendar in GMT timezone, set to this date and add the offset
    Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    gmtCal.setTime(date);
    gmtCal.add(Calendar.MILLISECOND, offsetFromUTC);

    log.debug("Created GMT cal with date [" + gmtCal.getTime() + "]");

    return gmtCal;
}

다음은 현재 시간 ( “12:09:05 EDT”from Calendar.getInstance())을 전달한 경우의 출력 입니다.

DEBUG-입력 달력에 날짜가 있음 [Thu Oct 23 12:09:05 EDT 2008]
DEBUG-오프셋은 -14400000입니다. DEBUG-
생성 된 GMT cal과 날짜 [Thu Oct 23 08:09:05 EDT 2008]

12:09:05 GMT는 8:09:05 EDT입니다.

여기서 혼란스러운 부분 Calendar.getTime()Date 현재 시간대에서 하고 달력의 시간대를 수정하고 기본 날짜도 롤링하는 방법이 없다는 것입니다. 웹 서비스가 사용하는 매개 변수 유형에 따라 epoch에서 밀리 초 단위로 WS 처리를 원할 수 있습니다.


답변

응답 해 주셔서 감사합니다. 추가 조사 끝에 정답을 얻었습니다. Skip Head에서 언급했듯이 내 응용 프로그램에서 얻은 TimeStamped가 사용자의 TimeZone으로 조정되었습니다. 따라서 사용자가 오후 6시 12 분 (EST)을 입력하면 오후 2시 12 분 (GMT)이 표시됩니다. 내가 필요한 것은 사용자가 입력 한 시간이 내가 WebServer 요청에 보낸 시간이되도록 변환을 취소하는 방법이었습니다. 이 작업을 수행 한 방법은 다음과 같습니다.

// Get TimeZone of user
TimeZone currentTimeZone = sc_.getTimeZone();
Calendar currentDt = new GregorianCalendar(currentTimeZone, EN_US_LOCALE);
// Get the Offset from GMT taking DST into account
int gmtOffset = currentTimeZone.getOffset(
    currentDt.get(Calendar.ERA),
    currentDt.get(Calendar.YEAR),
    currentDt.get(Calendar.MONTH),
    currentDt.get(Calendar.DAY_OF_MONTH),
    currentDt.get(Calendar.DAY_OF_WEEK),
    currentDt.get(Calendar.MILLISECOND));
// convert to hours
gmtOffset = gmtOffset / (60*60*1000);
System.out.println("Current User's TimeZone: " + currentTimeZone.getID());
System.out.println("Current Offset from GMT (in hrs):" + gmtOffset);
// Get TS from User Input
Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
System.out.println("TS from ACP: " + issuedDate);
// Set TS into Calendar
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
// Adjust for GMT (note the offset negation)
issueDate.add(Calendar.HOUR_OF_DAY, -gmtOffset);
System.out.println("Calendar Date converted from TS using GMT and US_EN Locale: "
    + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
    .format(issueDate.getTime()));

코드 출력은 다음과 같습니다. (사용자가 입력 한 2008 년 5 월 1 일 오후 6:12 (EST)

현재 사용자의 시간대 :
GMT에서 EST 현재 오프셋 (시간) :-4 (일반적으로 -5, DST 조정 제외)
ACP의 TS : 2008-05-01 14 : 12 : 00.0
GMT 및 US_EN 로케일을 사용하여 TS에서 변환 된 달력 날짜 : 2008 년 5 월 1 일 오후 6:12 (GMT)


답변

날짜가 웹 서비스와 관련하여 사용된다고 말 했으므로 어느 시점에서 문자열로 직렬화되었다고 가정합니다.

이 경우 DateFormat 클래스 의 setTimeZone 메서드 를 살펴보아야합니다 . 이것은 타임 스탬프를 인쇄 할 때 사용할 시간대를 나타냅니다.

간단한 예 :

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

Calendar cal = Calendar.getInstance();
String timestamp = formatter.format(cal.getTime());


답변

Joda Time으로 해결할 수 있습니다 .

Date utcDate = new Date(timezoneFrom.convertLocalToUTC(date.getTime(), false));
Date localDate = new Date(timezoneTo.convertUTCToLocal(utcDate.getTime()));

자바 8 :

LocalDateTime localDateTime = LocalDateTime.parse("2007-12-03T10:15:30");
ZonedDateTime fromDateTime = localDateTime.atZone(
    ZoneId.of("America/Toronto"));
ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant(
    ZoneId.of("Canada/Newfoundland"));


답변

TimeStamp가 원래 시스템의 시간대로 설정되어있는 것 같습니다.

이것은 더 이상 사용되지 않지만 작동합니다.

cal.setTimeInMillis(ts_.getTime() - ts_.getTimezoneOffset());

더 이상 사용되지 않는 방법은

Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)

그러나 시스템이 어떤 시간대에 있는지 알고 있기 때문에 클라이언트 측에서 수행해야합니다.


답변

한 timeZone에서 다른 timeZone으로 변환하는 방법 (아마도 작동합니다 :)).

/**
 * Adapt calendar to client time zone.
 * @param calendar - adapting calendar
 * @param timeZone - client time zone
 * @return adapt calendar to client time zone
 */
public static Calendar convertCalendar(final Calendar calendar, final TimeZone timeZone) {
    Calendar ret = new GregorianCalendar(timeZone);
    ret.setTimeInMillis(calendar.getTimeInMillis() +
            timeZone.getOffset(calendar.getTimeInMillis()) -
            TimeZone.getDefault().getOffset(calendar.getTimeInMillis()));
    ret.getTime();
    return ret;
}


답변

DateTimestamp 객체는 시간대를 알지 못합니다. 즉, 해당 순간을 시간과 일로 특정 해석하지 않고 에포크 이후 특정 시간 (초)을 나타냅니다. 시간대는 GregorianCalendar (이 작업에 직접 필요하지 않음) 및 SimpleDateFormat으로 만 입력되며 , 별도의 필드와 날짜 (또는 long ) 값 간에 변환하려면 시간대 오프셋이 필요 합니다.

OP의 문제는 처리 시작 부분에 있습니다. 사용자가 모호한 시간을 입력하고 GMT가 아닌 지역 시간대로 해석됩니다. 이 시점에서 값은 “동부 표준시 06시 12분” 쉽게로 인쇄 할 수 있습니다, “11.12 GMT” 또는 다른 시간대하지만 결코 예정되지 않은 변경“6.12 GMT” .

“06:12”“HH : MM” (기본값은 현지 표준 시간대) 로 구문 분석 하는 SimpleDateFormat 을 대신 UTC로 기본 설정하는 방법은 없습니다 . SimpleDateFormat 은 그 자체로는 너무 똑똑합니다.

그러나 입력에 명시 적으로 입력하면 모든 SimpleDateFormat 인스턴스가 올바른 시간대를 사용 하도록 설득 할 수 있습니다 . 수신 된 (그리고 적절하게 검증 된) “06:12” 에 고정 문자열을 추가하여 “06:12 GMT”를 다음 과 같이 구문 분석 합니다. “HH : MM z” .

GregorianCalendar 필드 를 명시 적으로 설정 하거나 시간대 및 일광 절약 시간 오프셋을 검색하고 사용할 필요가 없습니다 .

실제 문제는 기본적으로 로컬 시간대로 설정되는 입력, UTC로 기본 설정되는 입력, 명시적인 시간대 표시가 실제로 필요한 입력을 분리하는 것입니다.