Bir java.util.Date saat dilimi nasıl ayarlanır?


225

Bir ayrıştırdığınızda java.util.Datebir gelen Stringancak zaman dilimine yerel saat dilimini ayarlıyor datenesne.

Zaman dilimi olarak belirtilmemiş Stringolan Dateayrıştırılır. dateNesnenin belirli bir saat dilimini ayarlamak istiyorum .

Bunu nasıl yapabilirim?


8
Sorunuza gerçekten bir cevap olmasa da, burada birkaç kez bahsedildiğini gördükten sonra Joda Time'ı kullandım. Benim için standart API'lerden daha mantıklı görünüyor ve bu tür şeyleri oldukça kolay bir şekilde yapabilir.
clstrfsck

2
@ msandiford Günümüzde Joda-Time yerine java.time sınıflarını kullanın . Joda-Time projesi artık bakım moduna geçişi danışmanlık ekibi ile, java.time sınıfları. Oracle'ın Öğreticisine bakın .
Basil Bourque

Yanıtlar:


316

DateFormat kullanın. Örneğin,

SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = isoFormat.parse("2010-05-23T09:01:02");

6
tarih Takvim sınıfından oluşturulmuşsa, Takvim için saat dilimini ayarlayabilirsiniz.
lwpro2

19
@ lwpro2 bu ifade yanıltıcıdır; Bir Calendar nesnesinin saat dilimini ayarlayabilirsiniz, ancak getTime () yöntemini kullanarak bir Date nesnesi almak, ana bilgisayarın saat dilimi ile bir Date nesnesi döndürür.
BrDaHa

02/20/15 14:44 ayrıştırmada sorun yaşıyorum. Herkes yardım edebilir
MS

@BrDaHa doğru.Yeni tarih nesnesinin istediğiniz saat diliminde olması için TimeZone.setDefault()aramadan önce getTime()yapmanız gerekir. JDK 1.8'de Calendar.getTime()arar return new Date(getTimeInMillis());.
jpllosa

183

Unutmayın java.util.Datenesneler kendi başlarına herhangi bir zaman dilimi bilgi içermez - Bir üzerinde saat dilimi ayarlayın olamaz Datenesne. Bir Datenesnenin içerdiği tek şey , "çağ" dan bu yana geçen milisaniye sayısıdır - 1 Ocak 1970, 00:00:00 UTC.

ZZ Coder'ın gösterdiği gibi, DateFormatnesnede saat dilimini, tarih ve saati hangi saat diliminde görüntülemek istediğinizi bildirmek için ayarlarsınız.


77
Bir Googling, deneme ve tamamlamanın ardından bunun cevaba kesin ve yararlı bir ekleme olduğunu fark ettim - ve vurgulamaya değer: Tarih yalnızca milisaniye değerini içerir . Kaynağa bakarsanız, hemen hemen sadece bir longalan denir fastTime. Date.toString()aslında Calendarbu milisaniyeyi yorumlamak için a kullanır . Bir yazdırmadan Yani Dateyapar o görünen o zaman dilimini nasıl ayarlanacağı konusunda anlaşılabilir sorulara yol açan bir (varsayılan) Saat dilimini olması.
David Carboni

3
Date nesnelerinin içinde saat dilimi bilgileri vardır. Ama bu doğru olabilir, değiştiremezsiniz.
lwpro2

3
@ Iwpro2, Jesper, Date nesnesinin bir saat dilimi saklamadığını iddia ediyor (ve kabul ediyorum). Saat diliminin java.util.Date içinde saklandığını iddia ediyorsanız, lütfen bir referans sağlayın.
Jim

6
@Jim, bu java.util.Date için kaynak kodudur. FastTime unix dönemi kadar tanımlanmışsa fastTime lehine kullanılan BaseCalendar.Date cdate öğesini içerir. Bu saat dilimi bilgilerini içerir. Bir Date örneğinin saat dilimi bilgileri içerebileceğini anlıyorum, ancak olmayabilir.
eis

2
@Jim Ayarlayabileceğini söylemedim, bu bilgiyi içerdiğini söylüyordum. Ben de kullanıcı olarak ayarlamak için hiçbir yolu görmüyorum. Ben her zaman kullanıcı / sistem varsayılan saat dilimi gibi görünüyor OP doğru olduğunu düşünüyorum.
eis

95

tl; Dr.

… Parsed… bir String'den… saat dilimi belirtilmedi… Belirli bir saat dilimi ayarlamak istiyorum

LocalDateTime.parse( "2018-01-23T01:23:45.123456789" )  // Parse string, lacking an offset-from-UTC and lacking a time zone, as a `LocalDateTime`.
    .atZone( ZoneId.of( "Africa/Tunis" ) )              // Assign the time zone for which you are certain this date-time was intended. Instantiates a `ZonedDateTime` object.

JuDate'de Saat Dilimi Yok

Diğer doğru cevapların belirttiği gibi, java.util.Date'in saat dilimi yoktur . UTC / GMT'yi temsil eder (saat dilimi farkı yok). toStringYöntemi bir String temsili oluştururken JVM'nin varsayılan saat dilimini uyguladığı için çok kafa karıştırıcı .

JuDate'den kaçının

Bu ve diğer birçok nedenden dolayı, yerleşik java.util.Date & .Calendar & java.text.SimpleDateFormat'ı kullanmaktan kaçınmalısınız. Onlar çok zahmetlidir.

Bunun yerine Java 8 ile birlikte gelen java.time paketini kullanın .

java.time

Java.time sınıfları zaman çizelgesindeki bir anı üç şekilde temsil edebilir:

  • UTC ( Instant)
  • Bir ofset ile ( OffsetDateTimeile ZoneOffset)
  • Bir saat dilimi ile ( ZonedDateTimeile ZoneId)

Instant

In java.time , temel yapı taşıdır InstantUTC olarak zaman tablosunda bir an. Instantİş mantığınızın çoğunda nesneler kullanın .

Instant instant = Instant.now();

OffsetDateTime

Bir yerin duvar saati zamanına ayarlamak için UTC'den ofset uygulayın .

Bir uygula ZoneOffsetbir olsun OffsetDateTime.

ZoneOffset zoneOffset = ZoneOffset.of( "-04:00" );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset );

ZonedDateTime

Daha iyi bir zaman dilimi , bir ofset artı Yaz Saati Uygulaması (DST) gibi anormalliklerin ele alınması için kurallar uygulamaktır .

Bir uygula ZoneIdbir etmek Instantde zaten ona ZonedDateTime. Her zaman uygun bir saat dilimi adı belirtin . Hiçbir zaman gibi 3-4 kısaltmalar kullanmak ESTveya ISTne eşsiz ne de standardize olması.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

LocalDateTime

Giriş dizgisinde ofset veya bölgenin herhangi bir göstergesi yoksa, olarak ayrıştırın LocalDateTime.

İstediğiniz saat diliminden eminseniz, a ZoneIdüretmek için a atayın ZonedDateTime. Yukarıdaki tl; dr bölümünde yukarıdaki kod örneğine bakınız .

Biçimlendirilmiş Dizeler

toStringStandart ISO 8601 biçiminde tarih-saat değerini temsil eden bir String oluşturmak için bu üç sınıftan herhangi birinde yöntemi çağırın . ZonedDateTimeSınıf parantez içinde zaman dilimi adı ekleyerek standart bir biçim uzanır.

String outputInstant = instant.toString(); // Ex: 2011-12-03T10:15:30Z
String outputOdt = odt.toString(); // Ex: 2007-12-03T10:15:30+01:00
String outputZdt = zdt.toString(); // Ex: 2007-12-03T10:15:30+01:00[Europe/Paris]

Diğer formatlar için DateTimeFormattersınıfı kullanın . Genellikle, sınıfın kullanıcının beklenen insan dilini ve kültürel normlarını kullanarak yerelleştirilmiş formatlar oluşturmasına izin vermek en iyisidir. Veya belirli bir biçim belirleyebilirsiniz.


Java'da hem modern hem de eski tüm tarih-saat türlerinin tablosu


Hakkında java.time

Java.time çerçevesi daha sonra Java 8 ve yerleşiktir. Bu sınıflar zahmetli eski yerini mirası gibi tarih-saat sınıfları java.util.Date, Calendar& SimpleDateFormat.

Artık bakım modunda olan Joda-Time projesi java.time sınıflarına geçişi tavsiye ediyor .

Daha fazla bilgi için Oracle Eğiticisine bakın . Ve birçok örnek ve açıklama için Stack Overflow'da arama yapın. Spesifikasyon JSR 310'dur .

Sen değiştirebilir java.time sizin veritabanı ile doğrudan nesneleri. JDBC 4.2 veya sonraki bir sürümüyle uyumlu bir JDBC sürücüsü kullanın . Dizeye gerek yok, sınıflara gerek yok .java.sql.*

Java.time sınıflarını nereden edinebilirsiniz?

ThreeTen-Ekstra proje ek sınıfları ile java.time uzanır. Bu proje, java.time'a gelecekteki olası eklemeler için bir kanıt zeminidir. Burada bazı yararlı sınıfları gibi bulabilir Interval, YearWeek, YearQuarter, ve daha .


Joda-Time

İken Joda-Time hala aktif korunur, onun yapımcıları kısa sürede uygun olduğu gibi java.time göç etmek söylediler. Bu bölümü referans olarak olduğu gibi bırakıyorum, java.timebunun yerine yukarıdaki bölümü kullanmanızı öneririm .

In Joda-Time , bir tarih-saat nesnesi ( DateTime) gerçekten kendisine atanmış zaman dilimini biliyor. Bu UTC'den sapma ve o zaman diliminin Yaz Saati Uygulaması (DST) ve diğer anormalliklerin kuralları ve geçmişi anlamına gelir .

String input = "2014-01-02T03:04:05";
DateTimeZone timeZone = DateTimeZone.forID( "Asia/Kolkata" );
DateTime dateTimeIndia = new DateTime( input, timeZone );
DateTime dateTimeUtcGmt = dateTimeIndia.withZone( DateTimeZone.UTC );

ISO 8601 biçiminde toStringbir String oluşturmak için yöntemi çağırın .

String output = dateTimeIndia.toString();

Joda-Time ayrıca her türlü diğer String formatlarını oluşturmak için zengin özellikler sunar.

Gerekirse, Joda-Time DateTime'dan java.util.Date'e dönüştürebilirsiniz.

Java.util.Date date = dateTimeIndia.toDate();

Bazı oldukça ayrıntılı daha fazla örnek bulmak için "joda tarih" için StackOverflow arayın.


Aslında orada olan bazı iç fonksiyonları için kullanılan bir java.util.Date gömülü bir zaman dilimi, (bu Yanıt yorumlara bakınız). Ancak bu dahili saat dilimi bir özellik olarak gösterilmez ve ayarlanamaz. Bu dahili saat dilimi, tarih-saat değerinin dize ile temsil edilmesinde yöntem tarafından kullanılan bölge değildirtoString ; bunun yerine JVM'nin geçerli varsayılan saat dilimi anında uygulanır. Kısaca, sık sık “juDate'in saat dilimi yok” diyoruz. Kafa karıştırıcı? Evet. Bu yorgun eski sınıflardan kaçınmanın bir başka nedeni.


3
"JuDate'de Saat Dilimi Yok" yanlış. Ayarlanmışsa, mülkünde saklanan juDate'de bir saat dilimi bilgisi vardır BaseCalendar.Date cdate. Buradaki kaynak koduna bir göz atın . JVM'nin varsayılan saat dilimini arayarak değiştirmek dışında bir juDate nesnesinin saat dilimini ayarlayamazsınız TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"));. Böylece, bir saat dilimi farkı vardır ve kullanımdan kaldırılan yöntemi juDate.getTimezoneOffset ()
Tay Bui

3
@blquythai Doğru, ödevini yaptın. Daha önce yaptığım gibi, bu kaynak kodunu görmüştüm. Orada olduğunu orada gömülü bir zaman dilimi. Ancak tüm pratik amaçlar için bu zaman dilimi göz ardı edilir. Bir java.util.Date, gömülü saat dilimini yok sayarken, UTC'de olmak üzere herhangi bir saat dilimi olmadan çalışır. JVM'nin toStringgeçerli varsayılan saat dilimini uygulayan yöntem hariç ; yine gömülü zaman dilimini görmezden gelir. Kısacası, bir java.util.Date'in saat dilimi olmadığını söylüyoruz. Sanat gibi , gerçeği anlatan bir yalandır.
Basil Bourque

@blquythai olarak çağırmak için TimeZone.setDefault, sen edilir değil java.util.Date nesnesinin saat diliminin ayarlanmasına - Tarih nesne hala UTC etkin hareket ederek gömülü saat dilimini yok sayar. Date'in toStringyöntemini etkilerdiniz. Varsayılan ayarı JVM'nin genellikle ana bilgisayar işletim sisteminin saat dilimine ayarlanan varsayılan saat dilimini değiştirir. Bu çağrı, JVM'de çalışan tüm uygulamaların tüm iş parçacıklarındaki tüm kodu etkilediği ve yürütülürken anında gerçekleştirdiği için önerilmez. Kaba ve tehlikeli olan bu çağrı sadece son çare olarak düşünülmelidir.
Basil Bourque

5
Bu zaman dilimi çok sık kullanılır (kullanılan equals, hashcode, getTime..) Eğer bir göz atacak olursak equalsyöntemle, bu aramaları getTime()hangi aramaları getTimeImpl(), hangi aramaları normalize()durumunda cdatemülkiyet normalize değildir. In normalize()yöntemin, saat dilimidir eğer durum onun depolanmış zaman dilimi bilgilere dayanarak 1/1/70 beri milisaniye yeniden hesaplar ise son cdateüzerinde çalıştığı anki JVM ortamının diliminin farklıdır. (Bir göz atın sun.util.calendar.AbstractCalendar getCalendarDate(long millis, CalendarDate date))
Thai Bui

2
@MichalM Tavsiyenize göre, bir süre önce gömülü bölge hakkında bir son söz ekledim.
Basil Bourque

75

Saat dilimini JVM düzeyinde de ayarlayabilirsiniz

Date date1 = new Date();
System.out.println(date1);

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
// or pass in a command line arg: -Duser.timezone="UTC"

Date date2 = new Date();
System.out.println(date2);

çıktı:

Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013

Bu bana yardımcı oldu. setting timeZone in SDF herhangi bir fark
yaratmadı

Bence daha güvenilir. (+1)
foobar

23
Dikkat: Arama TimeZone.setDefaulttüm JVM'yi etkilediğinden, diğer tüm nesneleri ve iş parçacıklarını etkilediğinden oldukça serttir. Bir SecurityManager ile çalışıyorsanız daha da fazla komplikasyon içeren ayrıntılar için bu cevaba bakınız . Daha da fazla karmaşıklık ekleme: Bu davranış, bu Soru'da tartışıldığı gibi Java'nın çeşitli sürümlerinde değişmiştir .
Basil Bourque

Bu, bu ifadeden sonra ortaya çıkan tüm evreler arasında ortak bir saat dilimi belirler, değil mi?
Jaydev

Bu soruya kabul edilenden daha iyi bir cevaptır.
francogrex

10

Yalnızca standart JDK sınıflarıyla çalışmanız gerekiyorsa, bunu kullanabilirsiniz:

/**
 * Converts the given <code>date</code> from the <code>fromTimeZone</code> to the
 * <code>toTimeZone</code>.  Since java.util.Date has does not really store time zome
 * information, this actually converts the date to the date that it would be in the
 * other time zone.
 * @param date
 * @param fromTimeZone
 * @param toTimeZone
 * @return
 */
public static Date convertTimeZone(Date date, TimeZone fromTimeZone, TimeZone toTimeZone)
{
    long fromTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, fromTimeZone);
    long toTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, toTimeZone);

    return new Date(date.getTime() + (toTimeZoneOffset - fromTimeZoneOffset));
}

/**
 * Calculates the offset of the <code>timeZone</code> from UTC, factoring in any
 * additional offset due to the time zone being in daylight savings time as of
 * the given <code>date</code>.
 * @param date
 * @param timeZone
 * @return
 */
private static long getTimeZoneUTCAndDSTOffset(Date date, TimeZone timeZone)
{
    long timeZoneDSTOffset = 0;
    if(timeZone.inDaylightTime(date))
    {
        timeZoneDSTOffset = timeZone.getDSTSavings();
    }

    return timeZone.getRawOffset() + timeZoneDSTOffset;
}

Kredi bu gider yazı .


1
Zaman dilimlerinin uzaklığı her zaman sabit değildir. Aslında, jeopolitik nedenlerden dolayı değişebilirler. TimeZone.getDSTSavings () bunu dikkate almaz ve daima geçerli ofseti döndürür . Bu, o tarihten bu yana ofsetleri değiştiren bir fromTimeZone / toTimeZone ile geçmiş bir tarihle uğraşıyorsanız yanlış bir dönüşüm elde edebileceğiniz anlamına gelir.
andrerobot

6

java.util.Calendaryalnızca JDK sınıflarını kullanarak saat dilimlerini işlemenin genel yoludur. Apache Commons'ın yardımcı olabilecek bazı alternatifleri / yardımcı programları vardır. Edit Spong'un notu bana Joda-Time hakkında gerçekten iyi şeyler duyduğumu hatırlattı (yine de kullanmadım).


Joda Time için +1. Standart Java API'sinden (herhangi bir durumda bulduğum - aksi gösterilmekten mutluluk duyduğum) elde edemeyeceğiniz ek işlevler sağlamazsa da, Joda Time bazı görevleri kolaylaştırır.
Joshua Hutchison

2
@JoshuaHutchison Joda-Time, tonlarca ek işleve sahiptir. Örnek: sınıflarıyla zaman açıklıklı temsil Period, Durationve Interval. Bu açıklıklar örneğin karşılaştırma yöntemleri içerir contains, abuts, overlapve gap. Ve PeriodFormatterBuilder"15 yıl 8 ay" gibi açıklayıcı ifadeler oluşturabilir.
Basil Bourque

1

Tarihi String'e dönüştürün ve SimpleDateFormat ile yapın.

    SimpleDateFormat readFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    readFormat.setTimeZone(TimeZone.getTimeZone("GMT" + timezoneOffset));
    String dateStr = readFormat.format(date);
    SimpleDateFormat writeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    Date date = writeFormat.parse(dateStr);

1
Yığın Taşması ile ilgili cevapların biraz tartışılması veya açıklanması bekleniyor. Bu sitenin bir kod pasajı kitaplığından daha fazlası olması amaçlanmıştır.
Basil Bourque

Yorumunuz için @Basil Bourque teşekkür ederim. Yanıtı düzenlemek
avisper

0

Herkes buna ihtiyaç duyarsa XMLGregorianCalendar, UTC'den geçerli bir saat dilimine dönüştürmeniz gerekiyorsa , yapmanız gereken tek şey saat dilimini ayarlamak 0, sonra aramaktır toGregorianCalendar()- aynı saat diliminde kalacaktır, ancak Datenasıl dönüştürüleceğini bilir böylece verileri oradan alabilirsiniz.

XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar(
        ((GregorianCalendar)GregorianCalendar.getInstance());
xmlStartTime.setTimezone(0);
GregorianCalendar startCalendar = xmlStartTime.toGregorianCalendar();
Date startDate = startCalendar.getTime();
XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar(startCalendar);
xmlStartTime.setHour(startDate.getHours());
xmlStartTime.setDay(startDate.getDate());
xmlStartTime.setMinute(startDate.getMinutes());
xmlStartTime.setMonth(startDate.getMonth()+1);
xmlStartTime.setTimezone(-startDate.getTimezoneOffset());
xmlStartTime.setSecond(startDate.getSeconds());
xmlStartTime.setYear(startDate.getYear() + 1900);
System.out.println(xmlStartTime.toString());

Sonuç:

2015-08-26T12:02:27.183Z
2015-08-26T14:02:27.183+02:00

0

Bu kod üzerinde çalıştığım bir uygulamada yardımcı oldu:

    Instant date = null;
    Date sdf = null;
    String formatTemplate = "EEE MMM dd yyyy HH:mm:ss";
    try {
        SimpleDateFormat isoFormat = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss");
        isoFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("US/Pacific")));
        sdf = isoFormat.parse(timeAtWhichToMakeAvailable);
        date = sdf.toInstant();

    } catch (Exception e) {
        System.out.println("did not parse: " + timeAtWhichToMakeAvailable);
    }

    LOGGER.info("timeAtWhichToMakeAvailable: " + timeAtWhichToMakeAvailable);
    LOGGER.info("sdf: " + sdf);
    LOGGER.info("parsed to: " + date);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.