LocalDateTime ile tarihler nasıl ayrıştırılır / biçimlendirilir? (Java 8)


341

Java 8 , tarihler ve saatler ile çalışmak için yeni bir java.time API'sı ekledi ( JSR 310 ).

Tarih ve saat dizesi (örn. "2014-04-08 12:30") Var . LocalDateTimeVerilen dizeden nasıl örnek alabilirim ?

Nesneyle çalışmayı bitirdikten sonra LocalDateTime: Daha sonra LocalDateTimeörneği yukarıda gösterilenle aynı biçime sahip bir dizeye nasıl dönüştürebilirim ?


11
Bilginize, çoğu insan çoğu zaman bir ZonedDateTimeyerine bir tane isteyecektir LocalDateTime. Adı sezgiseldir; bu Local, belirli bir saat dilimi yerine genel olarak herhangi bir yerellik anlamına gelir . Bu nedenle, bir LocalDateTimenesne 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.
Basil Bourque

İle ilgili açıklama için benim cevap bakınız LocalDateTimevs ZonedDateTimevs OffsetDateTimevs Instantvs LocalDatevs LocalTime, o kadar karmaşık ve ne kadar haklı ilk atışta bunu yapmak için neden hakkında sakin tutmak nasıl.
Ondra Žižka

1
Pratik olmayan uzun LocalDateTimeolmasaydı, muhtemelen adlandırılmış olurdu ZonelessOffsetlessDateTime.
Ondra Žižka

Yanıtlar:


534

Ayrıştırma tarihi ve saati

LocalDateTimeBir dizeden nesne oluşturmak için statik LocalDateTime.parse()yöntemi kullanabilirsiniz . Bir dize ve bir DateTimeFormatteras parametresi alır. DateTimeFormatterTarih / saat modeli belirtmek için kullanılır.

String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);

Tarih ve saati biçimlendirme

Bir LocalDateTimenesneyi biçimlendirilmiş bir dize oluşturmak için format()yöntemi kullanabilirsiniz .

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"

İçinde sabit olarak önceden tanımlanmış bazı sık kullanılan tarih / saat biçimleri olduğunu unutmayın DateTimeFormatter. Örneğin: Örneği yukarıdan DateTimeFormatter.ISO_DATE_TIMEbiçimlendirmek için kullanmak LocalDateTimedizeye neden olur "1986-04-08T12:30:00".

parse()Ve format()yöntemleri tüm tarih / saat ile ilgili nesneler için kullanılabilir (örn LocalDateya ZonedDateTime)


77
DateTimeFormatter'ın değişmez ve iş parçacığı açısından güvenli olduğunu ve bu nedenle önerilen yaklaşımın mümkün olan yerlerde statik bir sabitte saklamak olduğunu unutmayın.
JodaStephen

@micha "2016-12-31T07: 59: 00.000Z" bu tarih formatına sahipsem ne olur?
Dawood Ahmed

14
DarawoodAbbasi deneyinDateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")
Ray Hulha

1
@Loenix belki de bunun format()yerine LocalDateTime sınıfını çağırmaya çalışıyorsunuzdur ? En azından, benim yaptığım ne: Ben karıştı DateTimeile dateTimeyukarıdaki örnekte.
sırlı

2

159

Ayrıca kullanabilir LocalDate.parse()veya LocalDateTime.parse()bir ilgili Stringolmadığını, bir desenle bunu sağlamadan Stringolduğu ISO-8601 biçimindeki .

Örneğin,

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
System.out.println("Date: " + aLD);

String strDatewithTime = "2015-08-04T10:11:30";
LocalDateTime aLDT = LocalDateTime.parse(strDatewithTime);
System.out.println("Date with Time: " + aLDT);

Çıktı ,

Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30

ve DateTimeFormatteryalnızca diğer tarih desenleriyle uğraşmanız gerektiğinde kullanın.

Örneğin, aşağıdaki örnekte, gg MMM uuuu ayın gününü (iki basamaklı), ayın adının üç harfini (Oca, Şub, Mar, ...) ve dört haneli bir yılı temsil eder:

DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
String anotherDate = "04 Aug 2015";
LocalDate lds = LocalDate.parse(anotherDate, dTF);
System.out.println(anotherDate + " parses to " + lds);

Çıktı

04 Aug 2015 parses to 2015-08-04

ayrıca DateTimeFormatternesnenin çift yönlü olduğunu da unutmayın ; hem girdi hem de çıktıyı ayrıştırabilir.

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
System.out.println(aLD + " formats as " + dTF.format(aLD));

Çıktı

2015-08-04 formats as 04 Aug 2015

( Biçimlendirme ve Ayrıştırma Tarihi Kalıplarının tam listesine bakın )

  Symbol  Meaning                     Presentation      Examples
  ------  -------                     ------------      -------
   G       era                         text              AD; Anno Domini; A
   u       year                        year              2004; 04
   y       year-of-era                 year              2004; 04
   D       day-of-year                 number            189
   M/L     month-of-year               number/text       7; 07; Jul; July; J
   d       day-of-month                number            10

   Q/q     quarter-of-year             number/text       3; 03; Q3; 3rd quarter
   Y       week-based-year             year              1996; 96
   w       week-of-week-based-year     number            27
   W       week-of-month               number            4
   E       day-of-week                 text              Tue; Tuesday; T
   e/c     localized day-of-week       number/text       2; 02; Tue; Tuesday; T
   F       week-of-month               number            3

   a       am-pm-of-day                text              PM
   h       clock-hour-of-am-pm (1-12)  number            12
   K       hour-of-am-pm (0-11)        number            0
   k       clock-hour-of-am-pm (1-24)  number            0

   H       hour-of-day (0-23)          number            0
   m       minute-of-hour              number            30
   s       second-of-minute            number            55
   S       fraction-of-second          fraction          978
   A       milli-of-day                number            1234
   n       nano-of-second              number            987654321
   N       nano-of-day                 number            1234000000

   V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
   z       time-zone name              zone-name         Pacific Standard Time; PST
   O       localized zone-offset       offset-O          GMT+8; GMT+08:00; UTC-08:00;
   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
   x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;

   p       pad next                    pad modifier      1

   '       escape for text             delimiter
   ''      single quote                literal           '
   [       optional section start
   ]       optional section end
   #       reserved for future use
   {       reserved for future use
   }       reserved for future use

11
Bu cevap önemli bir konuya değindi: mümkün olduğunca önceden tanımlanmış formatlayıcılar kullanın, örneğin "yyyy-AA-gg" üzerinde bir formatlayıcı tabanı OLMAYIN, bunun yerine DateTimeFormatter.ISO_LOCAL_DATE kullanın. Kodunuzun çok daha temiz görünmesini sağlayacaktır. Ayrıca, ISO8061 formatının kullanımını en üst düzeye çıkarmaya çalışın, uzun vadede temettü ödeyecek.
Christopher Yang

Gibi doğrulama için bir tarih ayrıştırmak istiyorum 2018-08-09 12:00:08ama ayrıştırdığımda Tihtiyacım olmayan bir eklendiğini görüyorum . Bunu yapmanın bir yolu var mı?
Raghuveer

@ Raghuveer T, tarih ve saat arasındaki ISO-8061 sınırlayıcısıdır. Bunun yerine biçiminizde bir boşluk varsa, deseni yyyy-MM-dd hh:mm:ssayrıştırmak ve biçimlendirmek için kullanabilirsiniz . T her zaman varsayılan (ISO-8061) biçiminde gösterilir, ancak kendi desenlerinizi kullanabilirsiniz.
Egor Hans

39

Yukarıdaki her iki cevap da dize kalıpları ile ilgili soruyu çok iyi açıklamaktadır. Ancak, ISO 8601 ile çalışmanız durumunda , DateTimeFormatterLocalDateTime zaten hazırlandığından başvurmanıza gerek yoktur :

LocalDateTime'ı Saat Dilimi ISO8601 Dizesine Dönüştür

LocalDateTime ldt = LocalDateTime.now(); 
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC); //you might use a different zone
String iso8601 = zdt.toString();

ISO8601 Dizesi'nden tekrar LocalDateTime'a dönüştürme

String iso8601 = "2016-02-14T18:32:04.150Z";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601);
LocalDateTime ldt = zdt.toLocalDateTime();

20

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.timeve 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?

  1. 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ı.
  2. 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.
  3. 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, ZonedDateTimeet al. çok karışık

  1. 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 .

  2. 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".

  3. 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):

  1. 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:00geç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.
  2. 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 .

  3. 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:00iş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.Dateyı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.Datekendi sınırlamaları ile getirildi.
  • İkisi de farklı takvimleri ve saat dilimlerini iyi kapsamadığından, CalendarAPI 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?

  1. Zaman ofseti var mı?
    Zaman ofseti +hh:mmkısımdır. Bazen +00:00ile ikame edilebilir Z, 'Zulu zamanı' olarak UTCEvrensel Zaman olarak veya GMTGreenwich Saati olarak. Bunlar aynı zamanda saat dilimini de ayarlar.
    Bu zaman damgaları için kullanırsınız OffsetDateTime.

  2. 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 .

    ZoneIdJavadoc 'a göre , bölge kimlikleri bir şekilde Zve 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.

  3. İ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ı LocalDateTimeve 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 YearMonthbunun dışında.

Tam bilgiye sahipseniz, alabilirsiniz java.time.Instant. Bu ayrıca dahili olarak OffsetDateTimeve arasında dönüştürme yapmak için kullanılır ZonedDateTime.

Nasıl ayrıştırılacağını öğrenin

DateTimeFormatterHem zaman damgası dizesini hem de dizeyi biçimlendirmeyi ayrıştırabilen kapsamlı bir belge vardır .

Önceden oluşturulmuş DateTimeFormatterler 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 DateTimeFormatterve 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 DateTimeFormatterikisine 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");

TemporalAccessorBununla 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.timeJava 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 :))


3

GEREKLİ FORMATTA MEVCUT UTC ZAMANI ALIN

// Current UTC time
        OffsetDateTime utc = OffsetDateTime.now(ZoneOffset.UTC);

        // GET LocalDateTime 
        LocalDateTime localDateTime = utc.toLocalDateTime();
        System.out.println("*************" + localDateTime);

        // formated UTC time
        DateTimeFormatter dTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        System.out.println(" formats as " + dTF.format(localDateTime));

        //GET UTC time for current date
        Date now= new Date();
        LocalDateTime utcDateTimeForCurrentDateTime = Instant.ofEpochMilli(now.getTime()).atZone(ZoneId.of("UTC")).toLocalDateTime();
        DateTimeFormatter dTF2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        System.out.println(" formats as " + dTF2.format(utcDateTimeForCurrentDateTime));

0

Bunun gibi tarih saat biçiminin birden çok varyantını kapsamak harika buldum:

final DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSS"))
.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSS"))
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"))
    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
    .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);

1
`` genel nihai statik DateTimeFormatter TIMESTAMP_XX = yeni DateTimeFormatterBuilder (). appendPattern ("[[uuuu] [- MM] [- dd]] [[SS] [: mm] [: ss] [. SSS]]"). parseDefaulting (ChronoField.YEAR, 2020) .parseDefaulting (ChronoField.MONTH_OF_YEAR, 1) .parseDefaulting (ChronoField.DAY_OF_MONTH, 1) .parseDefaulting (ChronoField.HOUR_OF_FEEFUTEFUTEFUTHDO.FUTHOF_DOA_FUTEFUTEFUTHOFA_DAHA_FUTHOFUT_DOFUT_DOA_FUTEFUTHOFUT_DOFUT_DOA_FOE_FUTHOF_DOA_FOE , 0) .parseDefaulting (ChronoField.NANO_OF_SECOND, 0) .toFormatter (); ``
Alan Stewart
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.