アプリケーションから取得したタイムスタンプ値があります。ユーザーは、任意のローカルタイムゾーンに参加できます。
この日付は、指定された時間が常にGMTであると想定するWebServiceに使用されるため、ユーザーのパラメーターを(EST)から(GMT)に変換する必要があります。キッカーは次のとおりです。ユーザーは自分のTZに気付いていません。彼はWSに送信する作成日を入力するので、必要なのは次のとおりです。
ユーザー入力: 5/1/2008 6:12 PM(EST)
WSへのパラメーターは次のとおりである必要があります:5/1/2008 6:12 PM(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分]
お返事ありがとうございます。さらなる調査の後、私は正しい答えを得ました。 Skip Headで述べたように、アプリケーションから取得したTimeStampedはユーザーのTimeZoneに合わせて調整されていました。ユーザーが6:12 PM(EST)と入力すると、2:12 PM(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()));
コードの出力は次のとおりです:(ユーザーが入力した5/1/2008 6:12 PM(EST)
現在のユーザーのタイムゾーン:EST
GMTからの現在のオフセット(時間単位):-4(通常-5、ただしDST調整済みを除く)
ACPからのTS:2008-05-01 14:12:00.0
GMTとUS_ENロケールを使用してTSから変換されたカレンダー日付:5/1/08 6:12 PM(GMT)
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;
}
以下は、現在の時刻(Calendar.getInstance()
からの「12:09:05 EDT」)を渡す場合の出力です。
デバッグ-入力カレンダーに日付があります[木10月23日12:09:05 EDT 2008]
DEBUG-オフセットは-14400000です
DEBUG-日付付きGMT calを作成[木10月23日08:09:05 EDT 2008]
12:09:05 GMTは8:09:05 EDTです。
ここで紛らわしい部分は、Calendar.getTime()
が現在のタイムゾーンでDate
を返すこと、またカレンダーのタイムゾーンを変更し、基礎となる日付をロールする方法がないことです。 Webサービスが使用するパラメーターのタイプに応じて、エポックからのミリ秒単位でWSを処理したい場合があります。
日付はWebサービスに関連して使用されると言うので、ある時点でそれが文字列にシリアル化されると仮定します。
この場合、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()));
Java 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"));
タイムスタンプが元のシステムのタイムゾーンに設定されているようです。
これは非推奨ですが、動作するはずです:
cal.setTimeInMillis(ts_.getTime() - ts_.getTimezoneOffset());
非推奨ではない方法は、
Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)
しかし、システムはそれがどのタイムゾーンにあるかを知っているので、それはクライアント側で行われる必要があります。
1つの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;
}
DateおよびTimestampオブジェクトはタイムゾーンを無視します。それらは、エポック以降の特定の秒数を表し、その瞬間の特定の解釈を時間と日としてコミットしません。タイムゾーンは、GregorianCalendar(このタスクには直接必要ありません)およびSimpleDateFormatにのみ画像を入力します。これらは、個別のフィールドと日付の間で変換するタイムゾーンオフセットが必要です(またはlong)値。
OPの問題は処理の開始直後です。ユーザーは時間を入力しますが、これはあいまいであり、ローカルの非GMTタイムゾーンで解釈されます。この時点で、値は "6:12 EST"であり、 "11.12 GMT"またはその他のタイムゾーンとして簡単に印刷できますが、 changeto "6.12 GMT"に移動します。
"06:12"を "HH:MM"として解析するSimpleDateFormatを作成する方法はありません(デフォルト現地のタイムゾーンへ)代わりにデフォルトのUTC SimpleDateFormatは、それ自体ではあまりにもスマートすぎます。
ただし、SimpleDateFormatインスタンスを入力に明示的に配置すると、正しいタイムゾーンを使用するように確信させることができます。受信した(そして適切に検証された)固定文字列を追加するだけです "06 :12 "を解析" 06:12 GMT "として" HH:MM z "。
GregorianCalendarフィールドを明示的に設定したり、タイムゾーンと夏時間のオフセットを取得して使用する必要はありません。
実際の問題は、デフォルトがローカルタイムゾーンに設定されている入力、デフォルトがUTCに設定されている入力、および明示的にタイムゾーンを明示的に指定する必要がある入力を分離することです。
過去に私のために働いていたのは、ユーザーのタイムゾーンとGMTの間のオフセット(ミリ秒単位)を決定することでした。オフセットを取得したら、どちらのタイムゾーンでも適切な時間を取得するために(変換の進行方法に応じて)単純に加算/減算できます。通常はCalendarオブジェクトのミリ秒フィールドを設定することでこれを実現しますが、タイムスタンプオブジェクトに簡単に適用できると確信しています。これがオフセットを取得するために使用するコードです
int offset = TimeZone.getTimeZone(timezoneId).getRawOffset();
timezoneIdは、ユーザーのタイムゾーンのID(ESTなど)です。
最新のアプローチでは、Java.timeクラスを使用します。これは、Javaの初期バージョンにバンドルされている面倒なレガシー日時クラスに取って代わるものです。
Java.sql.Timestamp
クラスは、これらのレガシークラスの1つです。不要になりました。代わりに、JDBC 4.2以降を使用して、Instant
または他のJava.timeクラスをデータベースで直接使用します。
Instant
クラスは、 UTC のタイムライン上のモーメントを ナノ秒小数部の最大9桁)で表します。
Instant instant = myResultSet.getObject( … , Instant.class ) ;
既存のTimestamp
と相互運用する必要がある場合は、古いクラスに追加された新しい変換メソッドを介して、すぐにJava.timeに変換します。
Instant instant = myTimestamp.toInstant() ;
別のタイムゾーンに調整するには、タイムゾーンをZoneId
オブジェクトとして指定します。 continent/region
、 America/Montreal
、またはAfrica/Casablanca
などの 適切なタイムゾーン名 をPacific/Auckland
の形式で指定します。 EST
やIST
などの3〜4文字の擬似ゾーンは、_(nottrueタイムゾーンであり、標準化されておらず、一意(!)でもないため、使用しないでください。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
Instant
に適用して、ZonedDateTime
オブジェクトを生成します。
ZonedDateTime zdt = instant.atZone( z ) ;
ユーザーに表示する文字列を生成するには、DateTimeFormatter
のStack Overflowを検索して、多くの議論と例を見つけます。
あなたの質問は、ユーザーのデータ入力から日時オブジェクトまで、他の方向に向かうことです。一般に、データ入力を日付と時刻の2つの部分に分けるのが最善です。
LocalDate ld = LocalDate.parse( dateInput , DateTimeFormatter.ofPattern( "M/d/uuuu" , Locale.US ) ) ;
LocalTime lt = LocalTime.parse( timeInput , DateTimeFormatter.ofPattern( "H:m a" , Locale.US ) ) ;
あなたの質問は明確ではありません。ユーザーが入力した日付と時刻をUTCであると解釈しますか?または別のタイムゾーンで?
UTCを意味する場合は、UTCの定数ZoneOffset.UTC
を使用して、オフセット付きのOffsetDateTime
を作成します。
OffsetDateTime odt = OffsetDateTime.of( ld , lt , ZoneOffset.UTC ) ;
別のタイムゾーンを意味する場合は、タイムゾーンオブジェクトZoneId
と組み合わせてください。しかし、どのタイムゾーンですか?デフォルトのタイムゾーンを検出する場合があります。または、重要な場合、ユーザーの意図を確認するためにユーザーに確認する必要があります。
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
定義により常にUTCである単純なオブジェクトを取得するには、Instant
を抽出します。
Instant instant = odt.toInstant() ;
…または…
Instant instant = zdt.toInstant() ;
データベースに送信します。
myPreparedStatement.setObject( … , instant ) ;
Java.time フレームワークはJava 8以降に組み込まれています。これらのクラスは、 Java.util.Date
、 Calendar
、& SimpleDateFormat
などの厄介な古い legacy date-timeクラスに取って代わります。
Joda-Time プロジェクトは、現在 メンテナンスモード であり、 Java.time クラスへの移行を推奨しています。
詳細については、 Oracle Tutorial を参照してください。また、Stack Overflowで多くの例と説明を検索してください。指定は JSR 31 です。
Java.timeクラスはどこで入手できますか?
ThreeTen-Extra プロジェクトは、追加のクラスでJava.timeを拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある証明の場です。 Interval
、 YearWeek
、 YearQuarter
、 more などの便利なクラスがあります。