web-dev-qa-db-ja.com

Java GMT / UTCの現地時間への変換が期待どおりに機能しない

再現可能なシナリオを示すために、私は次のことをしています

  1. 現在のシステム時刻(ローカル時刻)を取得します

  2. 現地時間をUTCに変換する//ここまでうまく動作する

  3. UTC時間を逆にし、現地時間に戻します。 3つの異なるアプローチ(以下にリスト)に従いましたが、3つのアプローチはすべてUTCのみで時刻を保持します。

    {

    long ts = System.currentTimeMillis();
    Date localTime = new Date(ts);
    String format = "yyyy/MM/dd HH:mm:ss";
    SimpleDateFormat sdf = new SimpleDateFormat (format);
    
    // Convert Local Time to UTC (Works Fine) 
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date gmtTime = new Date(sdf.format(localTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Reverse Convert UTC Time to Locale time (Doesn't work) Approach 1
    sdf.setTimeZone(TimeZone.getDefault());        
    localTime = new Date(sdf.format(gmtTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Reverse Convert UTC Time to Locale time (Doesn't work) Approach 2 using DateFormat
    DateFormat df = new SimpleDateFormat (format);
    df.setTimeZone(TimeZone.getDefault());
    localTime = df.parse((df.format(gmtTime)));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Approach 3
    Calendar c = new GregorianCalendar(TimeZone.getDefault());
    c.setTimeInMillis(gmtTime.getTime());
    System.out.println("Local Time " + c.toString());
    

    }

29

前述のようにJodaを使用することもお勧めします。

標準のJavaDateオブジェクトを使用して問題を解決するには、次のようにします。

    // **** YOUR CODE **** BEGIN ****
    long ts = System.currentTimeMillis();
    Date localTime = new Date(ts);
    String format = "yyyy/MM/dd HH:mm:ss";
    SimpleDateFormat sdf = new SimpleDateFormat(format);

    // Convert Local Time to UTC (Works Fine)
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date gmtTime = new Date(sdf.format(localTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:"
            + gmtTime.toString() + "," + gmtTime.getTime());

    // **** YOUR CODE **** END ****

    // Convert UTC to Local Time
    Date fromGmt = new Date(gmtTime.getTime() + TimeZone.getDefault().getOffset(localTime.getTime()));
    System.out.println("UTC time:" + gmtTime.toString() + "," + gmtTime.getTime() + " --> Local:"
            + fromGmt.toString() + "-" + fromGmt.getTime());

出力:

Local:Tue Oct 15 12:19:40 CEST 2013,1381832380522 --> UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000
UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000 --> Local:Tue Oct 15 12:19:40 CEST 2013-1381832380000
56
trylimits

ジョーダタイム


更新: Joda-Time プロジェクトは現在 メンテナンスモード であり、チームは移行をアドバイスしています Java.time クラスへ。 Tutorial by Oracle を参照してください。

my other Answer 業界をリードするJava.timeクラスの使用を参照してください。


通常、StackOverflow.comでは、代替技術を提案して特定の質問に答えることは悪いと考えています。ただし、Java 7以前にバンドルされている日付、時刻、カレンダークラスの場合、これらのクラスは設計と実行の両方で非常に悪名高いため、3番目のクラスを使用することをお勧めします代わりにパーティライブラリ: Joda-Time

Joda-Timeは 不変オブジェクト を作成することで機能します。したがって、DateTimeオブジェクトのタイムゾーンを変更するのではなく、異なるタイムゾーンを割り当てて新しいDateTimeをインスタンス化します。

Joda-Timeでは、ローカル時間とUTC時間の両方を使用する際の中心的な関心事は非常に単純で、わずか3行のコードしか必要ありません。

_    org.joda.time.DateTime now = new org.joda.time.DateTime();
    System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
    System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
_

北米の西海岸で実行した場合の出力は次のとおりです。

_Local time in ISO 8601 format: 2013-10-15T02:45:30.801-07:00_

UTC (Zulu) time zone: 2013-10-15T09:45:30.801Z

これは、いくつかの例と追加のコメントを含むクラスです。 Joda-Time 2.5を使用します。

_/**
 * Created by Basil Bourque on 2013-10-15.
 * © Basil Bourque 2013
 * This source code may be used freely forever by anyone taking full responsibility for doing so.
 */
public class TimeExample {
    public static void main(String[] args) {
        // Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 8 and earlier.
        // http://www.joda.org/joda-time/

        // Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
        // JSR 310 was inspired by Joda-Time but is not directly based on it.
        // http://jcp.org/en/jsr/detail?id=310

        // By default, Joda-Time produces strings in the standard ISO 8601 format.
        // https://en.wikipedia.org/wiki/ISO_8601
        // You may output to strings in other formats.

        // Capture one moment in time, to be used in all the examples to follow.
        org.joda.time.DateTime now = new org.joda.time.DateTime();

        System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
        System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );

        // You may specify a time zone in either of two ways:
        // • Using identifiers bundled with Joda-Time
        // • Using identifiers bundled with Java via its TimeZone class

        // ----|  Joda-Time Zones  |---------------------------------

        // Time zone identifiers defined by Joda-Time…
        System.out.println( "Time zones defined in Joda-Time : " + Java.util.Arrays.toString( org.joda.time.DateTimeZone.getAvailableIDs().toArray() ) );

        // Specify a time zone using DateTimeZone objects from Joda-Time.
        // http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeZone.html
        org.joda.time.DateTimeZone parisDateTimeZone = org.joda.time.DateTimeZone.forID( "Europe/Paris" );
        System.out.println( "Paris France (Joda-Time zone): " + now.toDateTime( parisDateTimeZone ) );

        // ----|  Java Zones  |---------------------------------

        // Time zone identifiers defined by Java…
        System.out.println( "Time zones defined within Java : " + Java.util.Arrays.toString( Java.util.TimeZone.getAvailableIDs() ) );

        // Specify a time zone using TimeZone objects built into Java.
        // http://docs.Oracle.com/javase/8/docs/api/Java/util/TimeZone.html
        Java.util.TimeZone parisTimeZone = Java.util.TimeZone.getTimeZone( "Europe/Paris" );
        System.out.println( "Paris France (Java zone): " + now.toDateTime(org.joda.time.DateTimeZone.forTimeZone( parisTimeZone ) ) );

    }
}
_
4
Basil Bourque

tl; dr

Instant.now()                           // Capture the current moment in UTC.
.atZone( ZoneId.systemDefault() )       // Adjust into the JVM's current default time zone. Same moment, different wall-clock time. Produces a `ZonedDateTime` object.
.toInstant()                            // Extract a `Instant` (always in UTC) object from the `ZonedDateTime` object.
.atZone( ZoneId.of( "Europe/Paris" ) )  // Adjust the `Instant` into a specific time zone. Renders a `ZonedDateTime` object. Same moment, different wall-clock time.
.toInstant()                            // And back to UTC again.

Java.time

最新のアプローチでは、面倒な古いレガシー日時クラス(DateCalendarなど)に取って代わるJava.timeクラスを使用します。

Wordの「ローカル」の使用は、Java.timeクラスでの使用と矛盾します。 Java.timeでは、「ローカル」はany局所性またはall局所性を意味しますが、特定の1つの局所性を意味しません。名前が「Local…」で始まるJava.timeクラスには、すべてタイムゾーンまたはUTCからのオフセットという概念がありません。したがって、それらは特定の瞬間を表すnotであり、notのタイムライン上のポイントですが、あなたの質問はすべての瞬間、さまざまな壁時計の時間を通して見たタイムライン上のポイントです。

現在のシステム時刻(ローカル時刻)を取得します

UTCで現在の瞬間をキャプチャする場合は、Instantを使用します。 Instant クラスは、 [〜#〜] utc [〜#〜] のタイムライン上の瞬間を ナノ秒 (小数の9桁までの解像度)で表します)。

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

ZoneIdを適用してZonedDateTimeを取得することにより、タイムゾーンに調整します。同じ瞬間、タイムライン上の同じポイント、異なる壁時計時間。

continent/regionAmerica/Montreal 、またはAfrica/CasablancaなどのPacific/Aucklandの形式で 適切なタイムゾーン名 を指定します。 ESTISTなどの3〜4文字の略語は、notの真のタイムゾーンであり、標準化されておらず、一意(!)でもないため使用しないでください。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock time.

ショートカットとして、Instantの使用をスキップしてZonedDateTimeを取得できます。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;

現地時間をUTCに変換する//ここまでうまく動作する

InstantからZonedDateTimeを抽出することにより、ゾーン化された日時からUTCに調整できます。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Instant instant = zdt.toInstant() ;

UTC時間を逆にし、現地時間に戻します。

上記のように、ZoneIdを適用して、特定の地域の人々(タイムゾーン)が使用する別の実時間に同じ瞬間を調整します。

Instant instant = Instant.now() ;  // Capture current moment in UTC.

ZoneId zDefault = ZoneId.systemDefault() ;  // The JVM's current default time zone.
ZonedDateTime zdtDefault = instant.atZone( zDefault ) ;

ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ;  // The JVM's current default time zone.
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;

ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;  // The JVM's current default time zone.
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;

ゾーン日時からUTCに戻り、ZonedDateTime::toInstantを呼び出します。概念的には、ZonedDateTime = Instant + ZoneIdと考えてください。

Instant instant = zdtAuckland.toInstant() ;

これらのオブジェクト、Instantおよび3つのZonedDateTimeオブジェクトはすべて、まったく同じ瞬間、つまり履歴の同じ時点を表します。

3つの異なるアプローチ(以下にリスト)に従いましたが、3つのアプローチはすべてUTCのみで時刻を保持します。

これらのひどいDateCalendar、およびGregorianCalendarクラスを使用してコードを修正しようとすることを忘れてください。彼らは悪いデザインと欠陥の惨めな混乱です。二度と触れる必要はありません。まだJava.timeに更新されていない古いコードとのインターフェイスが必要な場合は、古いクラスに追加された新しい変換メソッドを介して前後に変換できます。


Java.timeについて

Java.time フレームワークはJava 8以降に組み込まれています。これらのクラスは、面倒な古い legacy のような日時クラスに取って代わります Java.util.DateCalendar 、& SimpleDateFormat

Joda-Time プロジェクトは、現在 メンテナンスモード であり、 Java.time クラスへの移行を推奨しています。

詳細については、 Oracle Tutorial を参照してください。また、多くの例と説明についてはStack Overflowを検索してください。仕様は JSR 31 です。

Java.timeオブジェクトをデータベースと直接交換できます。 JDBC 4.2 以降に準拠する JDBCドライバー を使用します。文字列もJava.sql.*クラスも必要ありません。

Java.timeクラスはどこで入手できますか?

ThreeTen-Extra プロジェクトは、Java.timeを追加のクラスで拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある証明の場です。 IntervalYearWeekYearQuarter 、および more

2
Basil Bourque

私は合唱団に参加していますが、今では長く古くなったクラスDateCalendarSimpleDateFormatおよび友人をスキップすることをお勧めします。特に、使用したDate(String)コンストラクターのように、Dateクラスの非推奨のメソッドとコンストラクターを使用することに対して警告します。タイムゾーンを越えて確実に機能しないため、使用しないでください。そして、はい、そのクラスのコンストラクタとメソッドのほとんどは非推奨です。

あなたが質問をしたとき、Joda-Timeは(私が知っているすべてから)明らかにより良い代替手段でしたが、時間が再び動きました。現在、Joda-Timeはほぼ完成したプロジェクトであり、その開発者は、代わりに_Java.time_、最新のJava日時APIを使用することを推奨しています。方法をお見せします。

_    ZonedDateTime localTime = ZonedDateTime.now(ZoneId.systemDefault());

    // Convert Local Time to UTC 
    OffsetDateTime gmtTime
            = localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
    System.out.println("Local:" + localTime.toString() 
            + " --> UTC time:" + gmtTime.toString());

    // Reverse Convert UTC Time to Local time
    localTime = gmtTime.atZoneSameInstant(ZoneId.systemDefault());
    System.out.println("Local Time " + localTime.toString());
_

手始めに、コードがあなたのコードの半分の長さであるだけでなく、読みやすいことにも注意してください。

私のコンピューターでは、コードが印刷されます:

_Local:2017-09-02T07:25:46.211+02:00[Europe/Berlin] --> UTC time:2017-09-02T05:25:46.211Z
Local Time 2017-09-02T07:25:46.211+02:00[Europe/Berlin]
_

エポックからのミリ秒を省きました。あなたはあなたの質問のようにSystem.currentTimeMillis();からいつでもそれらを取得することができ、それらはタイムゾーンから独立しているので、私はそれらがここで面白いとは思いませんでした。

私はためらいながら変数名localTimeを保持しました。いい名前だと思います。最新のAPIにはLocalTimeというクラスがあります。そのため、LocalTime型を持たないオブジェクトに対して、大文字ではなくその名前を使用すると、一部を混乱させる可能性があります(LocalTimeは適切な変換を行うためにここに保持する必要があるタイムゾーン情報を保持しません。また、日付ではなく時刻のみを保持します。

現地時間からUTCへの変換は不正確で不可能でした

古いDateクラスはタイムゾーン情報を保持していないため(内部的には常にUTCを使用していると言えます)、あるタイムゾーンから別のタイムゾーンにDateを変換するようなことはありません。 。コンピューターでコードを実行したとき、印刷された最初の行は次のとおりでした。

_Local:Sat Sep 02 07:25:45 CEST 2017,1504329945967 --> UTC time:Sat Sep 02 05:25:45 CEST 2017-1504322745000
_

_07:25:45 CEST_はもちろん正しいです。正しいUTC時間は_05:25:45 UTC_でしたが、再びCESTと表示されますが、これは正しくありません。

これでDateクラスは二度と必要なくなりますが、:-)に行くなら、必読は Jon Skeetのコーディングに関するすべてのJava.util.Dateについて ですブログ。

質問:Javaバージョンで最新のAPIを使用できますか?

少なくともJava6を使用している場合、可能です。

  • Java 8以降では、新しいAPIが組み込まれています。
  • Java 6および7 get the ThreeTen Backport では、新しいクラスのバックポート(最新のAPIが最初に定義されたJSR-310のThreeTen)。
  • Androidでは、AndroidエディションのThreeTen Backportを使用します。 ThreeTenABPと呼ばれ、 この質問:Android ProjectでThreeTenABPを使用する方法 には素晴らしい説明があると思います。
2
Ole V.V.

既知のタイムゾーンの日付があります(ここEurope/Madrid)、およびターゲットタイムゾーン(UTC

次の2つのSimpleDateFormatが必要です。

 long ts = System.currentTimeMillis(); 
 Date localTime = new Date(ts); 
 
 SimpleDateFormat sdfLocal = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss "); 
 sdfLocal.setTimeZone(TimeZone.getTimeZone(" Europe/Madrid ")); 
 
 SimpleDateFormat sdfUTC = new SimpleDateFormat(" yyyy/MM/dd HH:mm:ss "); 
 sdfUTC.setTimeZone(TimeZone.getTimeZone(" UTC ")); 
 
 //ローカル時間をUTC 
に変換日付utcTime = sdfLocal.parse(sdfUTC.format(localTime)); 
 System.out.println( "Local:" + localTime.toString()+ "、" + localTime.getTime()+ "- > UTC時間: "+ utcTime.toString()+"-"+ utcTime.getTime()); 
 
 // UTC時間をロケール時間に逆変換
 localTime = sdfUTC .parse(sdfLocal.format(utcTime)); 
 System.out.println( "UTC:" + utcTime.toString()+ "、" + utcTime.getTime()+ "->現地時間: "+ localTime.toString()+"-"+ localTime 。時間をもらう());

したがって、動作を確認したら、このメソッドをユーティリティに追加できます。

 public Date convertDate(Date dateFrom、String fromTimeZone、String toTimeZone)throws ParseException {
 String pattern = "yyyy/MM/dd HH:mm:ss"; 
 SimpleDateFormat sdfFrom = new SimpleDateFormat(パターン); 
 sdfFrom.setTimeZone(TimeZone.getTimeZone(fromTimeZone)); 
 
 SimpleDateFormat sdfTo = new SimpleDateFormat(パターン); 
 sdfTo.setTimeZone (TimeZone.getTimeZone(toTimeZone)); 
 
 Date dateTo = sdfFrom.parse(sdfTo.format(dateFrom)); 
 return dateTo; 
} 
2
albfan

Joda Timeの使用を強くお勧めします http://joda-time.sourceforge.net/faq.html

1
Tomasz Waszczyk