Tarih ve saati içeren bir dizeyi belirli bir noktaya (Java " Instant
" olarak adlandırır) ayrıştırmak oldukça karmaşıktır. Java bunu birkaç yinelemede ele almaktadır. Son bir, java.time
ve java.time.chrono
, kapakları hemen hemen tüm ihtiyaçları (hariç zaman genişleme :)).
Bununla birlikte, bu karmaşıklık çok fazla karışıklık getirir.
Tarih ayrıştırmayı anlamanın anahtarı:
Java'nın bir tarihi ayrıştırmak için neden bu kadar çok yolu var?
- Bir zamanı ölçmek için birkaç sistem vardır. Örneğin, tarihi Japon takvimleri, ilgili imparator veya hanedanın saltanatı zaman aralıklarından türetilmiştir. Sonra UNIX zaman damgası var. Neyse ki, bütün (iş dünyası) aynı şeyi kullanmayı başardı.
- Tarihsel olarak, sistemler çeşitli nedenlerden ötürü / sisteme değiştiriliyordu . Örneğin Julian takviminden 1582'de Gregoryen takvimine. Bu yüzden 'batılı' tarihler farklı şekilde ele alınmalıdır.
- Ve elbette değişiklik bir anda gerçekleşmedi. Takvim bazı dinlerin ve Avrupa'nın diğer diyetlere inanan diğer bölgelerinin merkezlerinden geldiği için, örneğin Almanya 1700 yılına kadar değişmedi.
... ve neden LocalDateTime
, ZonedDateTime
et al. çok karışık
Orada zaman dilimleri . Bir zaman dilimi temel olarak , yetkilileri hangi zaman dengelemesine sahip olduğu ile aynı kuralları izleyen Dünya yüzeyinin bir "çizgisidir" * [1] . Buna yaz saati kuralları da dahildir.
Zaman dilimleri, çoğunlukla kimin fethettiğine bağlı olarak çeşitli alanlar için zamanla değişir. Ve bir zaman diliminin kuralları da zaman içinde değişir .
Zaman ofsetleri var. Bu, zaman dilimleriyle aynı değildir, çünkü bir zaman dilimi örneğin "Prag" olabilir, ancak yaz saati ofseti ve kış saati ofseti vardır.
Bir zaman dilimi içeren bir zaman damgası alırsanız, ofset, yılın hangi bölümünde olduğuna bağlı olarak değişebilir. Artık saatte, zaman damgası 2 farklı zaman anlamına gelebilir, bu nedenle ek bilgi olmadan güvenilir bir şekilde olamaz dönüştürülür.
Not: Zaman damgasına göre "isteğe bağlı olarak bir saat dilimi ve / veya saat ofseti içeren bir tarih ve / veya saat içeren bir dize".
Birkaç zaman dilimi belirli zaman dilimleri için aynı zaman farkını paylaşabilir. Örneğin, GMT / UTC saat dilimi, yaz saati ofseti etkin olmadığında "Londra" saat dilimi ile aynıdır.
Biraz daha karmaşık hale getirmek için (ancak kullanım durumunuz için çok önemli değil):
- Bilim adamları Dünya'nın zaman içinde değişen dinamiğini gözlemliyorlar; buna dayanarak, bireysel yılların sonuna saniye eklerler. (Bu nedenle
2040-12-31 24:00:00
geçerli bir tarih-saat olabilir.) Bunun için, sistemlerin tarih dönüşümlerini doğru yapmak amacıyla kullandığı meta verilerin düzenli olarak güncellenmesi gerekir. Örneğin Linux'ta, bu yeni veriler de dahil olmak üzere Java paketlerinde düzenli güncellemeler alırsınız.
Güncellemeler, geçmiş ve gelecek zaman damgaları için her zaman önceki davranışı korumaz. Bu nedenle, iki zaman damgasının bir zaman diliminin bunları karşılaştırarak değişmesi etrafında ayrıştırılması , yazılımın farklı sürümlerinde çalışırken farklı sonuçlar verebilir . Bu, etkilenen saat dilimi ile diğer saat dilimi arasındaki karşılaştırma için de geçerlidir.
Bu, yazılımınızda bir hataya neden olursa, UNIX zaman damgası gibi karmaşık kurallara sahip olmayan bir zaman damgası kullanmayı düşünün .
7 nedeniyle, gelecek tarihler için tarihleri tam olarak kesin olarak dönüştüremeyiz. Örneğin, şu andaki ayrıştırma 8524-02-17 12:00:00
işlemi, gelecekteki ayrıştırma işleminden birkaç saniye sonra olabilir.
JDK'nın API'leri çağdaş ihtiyaçlarla gelişti
- İlk Java sürümleri, sadece
java.util.Date
yıl, ay, gün ve zaman olduğunu varsayarak, biraz naif bir yaklaşıma sahipti. Bu hızlı bir şekilde yeterli değildi.
- Ayrıca, veritabanlarının ihtiyaçları farklıydı, bu yüzden oldukça erken,
java.sql.Date
kendi sınırlamaları ile getirildi.
- İkisi de farklı takvimleri ve saat dilimlerini iyi kapsamadığından,
Calendar
API tanıtıldı.
- Bu hala zaman dilimlerinin karmaşıklığını kapsamıyordu. Ve yine de, yukarıdaki API'lerin karışımı gerçekten çalışmak için bir acıydı. Java geliştiricileri küresel web uygulamaları üzerinde çalışmaya başladığında, JodaTime gibi en çok kullanım durumunu hedefleyen kütüphaneler hızla popüler oldu. JodaTime yaklaşık on yıl boyunca fiili standarttı.
- Ancak JDK, JodaTime ile entegre olmadı, bu yüzden onunla çalışmak biraz hantaldı. Bu nedenle, konuya nasıl yaklaşılacağı üzerine çok uzun bir tartışmadan sonra, esas olarak JodaTime'a dayanarak JSR-310 oluşturuldu .
Java ile nasıl başa çıkılır java.time
Zaman damgasının ayrıştırılacağı türü belirleme
Bir zaman damgası dizesi tüketirken, hangi bilgileri içerdiğini bilmeniz gerekir. Bu çok önemli bir nokta. Bunu doğru şekilde yapmazsanız, "Anında Arama oluşturulamıyor" veya "Bölge ofseti eksik" veya "bilinmeyen bölge kimliği" vb.
Tarih ve saati içeriyor mu?
Zaman ofseti var mı?
Zaman ofseti +hh:mm
kısımdır. Bazen +00:00
ile ikame edilebilir Z
, 'Zulu zamanı' olarak UTC
Evrensel Zaman olarak veya GMT
Greenwich Saati olarak. Bunlar aynı zamanda saat dilimini de ayarlar.
Bu zaman damgaları için kullanırsınız OffsetDateTime
.
Saat dilimi var mı?
Bu zaman damgaları için kullanırsınız ZonedDateTime
.
Bölge ya
- ad ("Prag", "Pasifik Standart Saati", "PST") veya
- java.time.ZoneId tarafından temsil edilen "bölge kimliği" ("Amerika / Los_Angeles", "Avrupa / Londra") .
Saat dilimlerinin listesi ICAAN tarafından desteklenen bir "TZ veritabanı" tarafından derlenir .
ZoneId
Javadoc 'a göre , bölge kimlikleri bir şekilde Z
ve ofset olarak da belirlenebilir . Bunun gerçek bölgelerle nasıl eşleştiğinden emin değilim. Sadece bir TZ'ye sahip olan zaman damgası, bir saat farkı ofset değişimine düşerse, belirsizdir ve yorumlama tabi tutulur ResolverStyle
, aşağıya bakınız.
İkisinde de yoksa, eksik bağlam varsayılır veya ihmal edilir. Ve tüketici karar vermek zorunda. Bu nedenle , eksik bilgi eklenerek ayrıştırılması LocalDateTime
ve dönüştürülmesi gerekir OffsetDateTime
:
- Sen olabilir varsayalım bir UTC zaman olduğunu. 0 saatlik UTC farkını ekleyin.
- Sen olabilir varsayalım o dönüşüm oluyor yerin zamanı olduğunu. Sistemin saat dilimini ekleyerek dönüştürün.
- İhmal edebilir ve sadece olduğu gibi kullanabilirsiniz. Bu, örneğin iki kez karşılaştırmak veya özetlemek (bkz.
Duration
) Veya bilmediğiniz ve gerçekten önemli olmadığı durumlarda (örn. Yerel otobüs tarifesi) faydalıdır .
Kısmi zaman bilgisi
- Zaman damgası ne içerdiğini dayanarak, alabilir
LocalDate
, LocalTime
, OffsetTime
, MonthDay
, Year
, veya YearMonth
bunun dışında.
Tam bilgiye sahipseniz, alabilirsiniz java.time.Instant
. Bu ayrıca dahili olarak OffsetDateTime
ve arasında dönüştürme yapmak için kullanılır ZonedDateTime
.
Nasıl ayrıştırılacağını öğrenin
DateTimeFormatter
Hem zaman damgası dizesini hem de dizeyi biçimlendirmeyi ayrıştırabilen kapsamlı bir belge vardır .
Önceden oluşturulmuş DateTimeFormatter
ler moreless tüm standart damgası biçimlerini kapsamalıdır. Örneğin ISO_INSTANT
, ayrıştırabilir 2011-12-03T10:15:30.123457Z
.
Bazı özel biçimleriniz varsa, kendi DateTimeFormatter'ınızı da oluşturabilirsiniz (bu da ayrıştırıcıdır).
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
Ben kaynak koduna bakmak DateTimeFormatter
ve nasıl kullanarak oluşturmak için ilham almanızı öneririz DateTimeFormatterBuilder
. Oradayken ResolverStyle
, ayrıştırıcının formatlar ve belirsiz bilgiler için LENIENT, SMART veya STRICT olup olmadığını kontrol edenlere de göz atın .
TemporalAccessor
Şimdi, sık sık hata karmaşıklığına girmektir TemporalAccessor
. Bu, geliştiricilerin çalışmak için nasıl kullanıldıklarından geliyor SimpleDateFormatter.parse(String)
. Doğru, DateTimeFormatter.parse("...")
verir TemporalAccessor
.
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
Ancak, önceki bölümdeki bilgilerle donatılmış olarak, ihtiyacınız olan türe kolayca ayrışabilirsiniz:
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
Aslında DateTimeFormatter
ikisine de ihtiyacınız yok. Ayrıştırmak istediğiniz türlerin parse(String)
yöntemleri vardır.
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
TemporalAccessor
Bununla ilgili olarak , dizede hangi bilgilerin bulunduğuna dair belirsiz bir fikriniz varsa ve çalışma zamanında karar vermek istiyorsanız kullanabilirsiniz.
Umarım ruhuna biraz ışık tutarım :)
Not: java.time
Java 6 ve 7 için bir backport var : ThreeTen-Backport . Android için ThreeTenABP var .
[1] Sadece çizgili değil, aynı zamanda bazı garip uçlar da var. Örneğin, bazı komşu pasifik adalarında +14: 00 ve -11: 00 saat dilimleri vardır. Bu demektir ki, bir adada 1 Mayıs 15:00, başka bir adada şu ana kadar değil, hala 30 Nisan 12 PM (doğru sayılırsam :))
ZonedDateTime
yerine bir tane isteyecektirLocalDateTime
. Adı sezgiseldir; buLocal
, belirli bir saat dilimi yerine genel olarak herhangi bir yerellik anlamına gelir . Bu nedenle, birLocalDateTime
nesne zaman çizgisine bağlı değildir. Bir anlam ifade etmek için, zaman çizgisinde belirli bir an elde etmek için bir zaman dilimi uygulamanız gerekir.