İOS'ta nasıl bir ISO 8601 tarihi edinebilirim?


115

2004-02-12T15:19:21+00:00PHP'de ISO 8601 tarih dizesini (örneğin ) almak yeterince kolaydır date('c'), ancak bunu Objective-C (iPhone) ile nasıl elde edebilirsiniz? Bunu yapmanın benzer şekilde kısa bir yolu var mı?

İşte bunu yapmak için bulduğum uzun yol:

NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

NSDate *now = [NSDate date];
NSString *formattedDateString = [dateFormatter stringFromDate:now];
NSLog(@"ISO-8601 date: %@", formattedDateString);

// Output: ISO-8601 date: 2013-04-27T13:27:50-0700

Bu kadar merkezi bir şey için çok fazla kesinlik gibi görünüyor.


1
Sanırım kısa bakanın gözünde. Üç satır kod oldukça kısa değil mi? Ekleyebileceğiniz bir C NSDate alt sınıfı var, bunu bir satırla yapacak, az önce onu açtım : github.com/soffes/SAMCategories/tree/master/SAMCategories/… . Bununla birlikte, gerçekten çok iyi cevaplar aldınız (IMHO) ve cevabı ya Etienne ya da rmaddy'yi ödüllendirmelisiniz. Bu sadece iyi bir uygulama ve insanları gerçekten yararlı bilgiler sağlayan ödüllendiriyor (bana yardımcı oldu, bugün bu bilgiye ihtiyacım vardı). Etienne, puanları rmaddy'den daha fazla kullanabilir. İyi bir önlem olarak, sorunuzu bile oylayacağım!
David H

Yanıtlar:


212

Kullanım NSDateFormatter:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];   
NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
[dateFormatter setCalendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]];

NSDate *now = [NSDate date];
NSString *iso8601String = [dateFormatter stringFromDate:now];

Ve Swift'de:

let dateFormatter = DateFormatter()
let enUSPosixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.locale = enUSPosixLocale
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.calendar = Calendar(identifier: .gregorian)

let iso8601String = dateFormatter.string(from: Date())

1
Teşekkürler Maddy. Kodumu gönderdikten sonra cevabınızı şimdi gördüm. Mı setLocaleönemli?
JohnK

2
@rmaddy Örneğinizi ZZZZZ kullanmak için düzenleyebilirsiniz, böylece kopyalayıp yapıştıran insanlar yanmaz.
Jesse Rusak

4
@jlmendezbonini Hayır. YYYY'yi istemeniz çok nadirdir. Genellikle yyyy'yi istersiniz.
rmaddy

7
Yerel ayarın neden en_US_POSIX olarak ayarlanması gerektiğinin kaynağı: developer.apple.com/library/mac/qa/qa1480/_index.html
yood

11
@maddy - Başkalarının bunu Google aracılığıyla bulmasını kolaylaştırmak istedi ve doğru biçimi, yerel ayarı vb. araştırdığınız için size kredi vermek istedi. Ancak isterseniz ayrı bir yanıt olarak göndermekten mutluluk duyarız
Robin

60

iOS 10, NSISO8601DateFormattertam da bunun üstesinden gelmek için yeni bir sınıf sunuyor. Swift 3 kullanıyorsanız, kodunuz aşağıdaki gibi olacaktır:

let formatter = ISO8601DateFormatter()
let date = formatter.date(from: "2016-08-26T12:39:00Z")
let string = formatter.string(from: Date())

ve hangi formatı veriyor? Ayrıca, 2016-08-26T12: 39: 00-0000 ve 2016-08-26T12: 39: 00-00: 00 ve 2016-08-26T12: 39: 00.374-0000 tarihleri ​​arasında çalışıp çalışamayacağını bilmek iyi olacaktır.
malhal

1
stringCevabımdaki üç satır çalıştırıldıktan sonra "2016-09-03T21: 47: 27Z" içeriyor.
TwoStraws

3
Eğer CAN NOT işlemek milisaniye bu biçimde.
Enrique

3
@Enrique: NSISO8601DateFormatWithFractionalSeconds'ı milisaniyelerle çalışabilmek için formatlayıcı seçeneklerine eklemelisiniz, belgeleri kontrol edin.
PakitoV

1
NSISO8601DateFormatWithFractionalSeconds yalnızca 11.2+ üzerinde çalışır. Aksi takdirde kaza yaparsınız!
hash3r

27

Maddy'nin cevabına bir tamamlayıcı olarak, saat dilimi formatı tek bir "Z" (RFC 822 formatı için) yerine ISO 8601 için "ZZZZZ" (5 kez Z) olmalıdır. En azından iOS 6'da.

(bkz. http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns )


Büyük yakalamak! Bununla ilgilenen başka biri, bağlantıya gidin, 8601'i arayın, göreceksiniz.
David H

Tarihteki saat dilimi UTC olduğunda, Z yerine 00: 00'ı göstermenin bir yolu var mı (bunlara eşdeğerdir)?
şim

Çok teşekkürler;) bir tarih formatıyla başımı duvara çarpıp sıfır tarihi döndürüyordu !!! :)
polo987

19

Sıklıkla gözden kaçan bir sorun, ISO 8601 formatındaki dizelerin milisaniye olabilir ve olmayabilir.

Diğer bir deyişle, hem "2016-12-31T23: 59: 59.9999999" hem de "2016-12-01T00: 00: 00" yasaldır, ancak statik yazılan tarih biçimlendiricisi kullanıyorsanız, bunlardan biri ayrıştırılmayacaktır .

İOS 10'dan başlayarak ISO8601DateFormatter, ISO 8601 tarih dizilerinin tüm varyasyonlarını işleyen kullanmalısınız . Aşağıdaki örneğe bakın:

let date = Date()
var string: String

let formatter = ISO8601DateFormatter()
string = formatter.string(from: date)

let GMT = TimeZone(abbreviation: "GMT")
let options: ISO8601DateFormatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
string = ISO8601DateFormatter.string(from: date, timeZone: GMT, formatOptions: options)

İçin IOS 9 ve aşağıdaki çok sayıda veri biçemleyicilerle aşağıdaki yaklaşım kullanımı.

Bu ince farkı hem vakaları hem de özetleri kapsayan bir cevap bulamadım. İşte ona hitap eden çözüm:

extension DateFormatter {

    static let iso8601DateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static let iso8601WithoutMillisecondsDateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static func date(fromISO8601String string: String) -> Date? {
        if let dateWithMilliseconds = iso8601DateFormatter.date(from: string) {
            return dateWithMilliseconds
        }

        if let dateWithoutMilliseconds = iso8601WithoutMillisecondsDateFormatter.date(from: string) {
            return dateWithoutMilliseconds
        }

        return nil
    }
}

Kullanımı:

let dateToString = "2016-12-31T23:59:59.9999999"
let dateTo = DateFormatter.date(fromISO8601String: dateToString)
// dateTo: 2016-12-31 23:59:59 +0000

let dateFromString = "2016-12-01T00:00:00"
let dateFrom = DateFormatter.date(fromISO8601String: dateFromString)
// dateFrom: 2016-12-01 00:00:00 +0000

Tarih biçimlendiricileri hakkındaki Apple makalesine de bakmanızı tavsiye ederim .


Ayrıca NSISO8601DateFormtter'ı Obj-C'de nasıl elde edeceğinizi de belirtebilir misiniz? (tabii ki iOS 10 ve üzeri için)
Motti Shneor

8

Sam Soffee'nin burada bulunan NSDate kategorisini kullanın . Projenize bu kod eklendikten sonra NSDate üzerinde tek bir yöntem kullanabilirsiniz:

- (NSString *)sam_ISO8601String

Sadece tek satır değil, saf C ile yazıldığı için NSDateFormatter yaklaşımından çok daha hızlı.


1
Kategorinin şu anki konumu burada
Perry

1
Buna karşı tavsiye
ederim,

1
@M_Waly bunu ona rapor ettin mi? Kodunu hiçbir uyarı olmadan oluşturdum ve analiz kontrollerinden gayet iyi geçti.
David H

6

IS8601'den itibaren , sorunlar temsil ve saat dilimidir

  • ISO 8601 = year-month-day time timezone
  • Tarih ve saat için temel (YYYYAAGG, ssddss, ...) ve genişletilmiş format (YYYY-AA-GG, ss: dd: ss, ...) vardır
  • Saat dilimi Zulu, ofset veya GMT olabilir
  • Tarih ve saat ayırıcısı boşluk olabilir veya T
  • Tarih için hafta formatı vardır, ancak nadiren kullanılır
  • Saat dilimi, sonrasında çok fazla boşluk olabilir
  • İkincisi isteğe bağlıdır

İşte bazı geçerli dizeler

2016-04-08T10:25:30Z
2016-04-08 11:25:30+0100
2016-04-08 202530GMT+1000
20160408 08:25:30-02:00
2016-04-08 11:25:30     +0100

Çözümler

NSDateFormatter

İşte onmyway133 ISO8601'de kullandığım biçim

let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyyMMdd HHmmssZ"

Hakkında Ztanımlayıcı Tarih Alan Sembol Tablosu

Z: The ISO8601 basic format with hours, minutes and optional seconds fields. The format is equivalent to RFC 822 zone format (when optional seconds field is absent)

Yerel Ayar Ayarlarını Kullanarak Verileri Biçimlendirme Hakkındalocale

Yerel ayarlar, kullanıcının tercih ettiği dili değil, belirli bir kullanıcı için biçimlendirme seçeneklerini temsil eder. Bunlar genellikle aynıdır ancak farklı olabilir. Örneğin, ana dili İngilizce olan ve Almanya'da yaşayan bir kişi dil olarak İngilizce'yi ve bölge olarak Almanya'yı seçebilir.

Hakkında en_US_POSIX Teknik Q & A QA1480 NSDateFormatter ve Internet Tarihleri

Öte yandan, sabit biçimli tarihlerle çalışıyorsanız, önce tarih biçimlendiricinin yerel ayarını sabit biçiminize uygun bir şeye ayarlamalısınız. Çoğu durumda, seçilecek en iyi yerel ayar, hem kullanıcı hem de sistem tercihlerinden bağımsız olarak ABD İngilizcesi sonuçları verecek şekilde özel olarak tasarlanmış bir yerel ayar olan "en_US_POSIX" dir. "en_US_POSIX" de zaman açısından değişmez (ABD, gelecekte bir noktada tarihleri ​​biçimlendirme biçimini değiştirirse, "en_US" yeni davranışı yansıtacak şekilde değişecek, ancak "en_US_POSIX" değişmeyecektir) ve makineler arasında ( "en_US_POSIX", iOS'ta OS X'te olduğu gibi ve diğer platformlarda olduğu gibi çalışır).

İlginç ilgili sorular


2016-07-23T07: 24: 23Z ile denedim, işe yaramıyor
Kamen Dobrev

@KamenDobrev "Çalışmıyor" derken neyi kastediyorsunuz? Örneğinizi github.com/onmyway133/ISO8601/blob/master/ISO8601Tests/… testlerine ekliyorum ve işe yarıyor
onmyway133


3

Bu öze dayanarak: https://github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.swift , NSDate'i yyyy-MM biçiminde ISO 8601 tarih dizesine dönüştürmek için aşağıdaki yöntem kullanılabilir. -dd'T'HH: dd: ss.SSSZ

-(NSString *)getISO8601String
    {
        static NSDateFormatter *formatter = nil;
        if (!formatter)
        {
            formatter = [[NSDateFormatter alloc] init];
            [formatter setLocale: [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
            formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
            [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];

        }
        NSString *iso8601String = [formatter stringFromDate: self];
        return [iso8601String stringByAppendingString: @"Z"];
    }

Saniyenin Salt sınırı burada önemliydi
Mark Meyer

3 S'den fazlasına ihtiyacınız varsa, tarih biçimlendiriciyi manuel olarak ayrıştırmak zorunda kalamazsınız.
malhal

-1

Bu biraz daha basittir ve tarihi UTC'ye yerleştirir.

extension NSDate
{
    func iso8601() -> String
    {
        let dateFormatter = NSDateFormatter()
        dateFormatter.timeZone = NSTimeZone(name: "UTC")
        let iso8601String = dateFormatter.stringFromDate(NSDate())
        return iso8601String
    }
}

let date = NSDate().iso8601()

-1

Swift 3.0 ve iOS 9.0'ı kullanma

extension Date {
    private static let jsonDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(identifier: "UTC")!
        return formatter
    }()

    var IOS8601String: String {
        get {
            return Date.jsonDateFormatter.string(from: self)
        }
    }

    init?(fromIOS8601 dateString: String) {
        if let d = Date.jsonDateFormatter.date(from: dateString) {
            self.init(timeInterval: 0, since:d)
        } else {
            return nil
        }
    }
}
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.