توزيع RFC3339 يرجع مع NSDateFormatter في iOS 4.x و MacOS X 10.6: مستحيل؟
-
30-09-2019 - |
سؤال
يبدو أن تحليل تاريخ RFC3339 مع NSDateFormatter مستحيل ، في الحالة العامة. هل أنا مخطئ؟ [تحرير بعد عامين: هناك الآن طريقة! انظر أدناه والحاشية.
تعمل خدمة الويب غير القابلة للارتداد على وجه الخصوص على إطعام مواعيد مثل:
2009-12-31T00:00:00-06:00
RFC3339 متوافق ، الإخراج الافتراضي لمكتبة JaxB التي يستخدمونها. لاحظ القولون ، الذي RFC3339 يستوجب عندما لا يكون الإزاحة "Z" حرفيًا:
time-numoffset = ("+" / "-") time-hour ":" time-minute time-offset = "Z" / time-numoffset
أريد تحليلها في nsdates.
NSDateFormatter يريد أنماط في بناء الجملة المحدد بواسطة يونيكود, ، الذي يوفر رموز حقل التاريخ للمناطق الزمنية مثل "PDT" ، "-0800" ، "GMT-08: 00" ولكن ليس "-08: 00".
غوغلينغ ، وغيرها من الأسئلة المتشابهة ، تنتج تنسيقات تاريخ فقط مثل
[myDateParser setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"];
/* or: */ [myDateParser setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
هذا الأخير يتطلب "Z" الحرفي ، ويصر الأول إما على غياب القولون أو وجود "GMT". ومع ذلك ، بدا أنهم يعملون قبل iOS 4.x (ربما عن طريق التخلص من إزاحة TZ تمامًا ؛ بياناتاتي ليست واضحة.)
خياراتي في هذه المرحلة هي الكثير آسف:
- اكتشف بعض المواصفات غير الموثقة ، أو بعض الوضع الغريب لوضع NSDateFormatter ، والتي ستقبل القولون الضال: Longshot ، على الأرجح غير موجود. [هامش
- إقناع ناشر الخدمة الخاص بي بتحويل جميع التواريخ إلى وقت Zulu وتحديد "Z": تحدي سياسي.
- اكتب بلدي الفئة الفرعية nsformatter أو بحث جيد قديم
strptime_l
: الشغل. قون - تقوم السلسلة بإلغاء مدخلاتي وتجريد القولون الأخير: هش وقبيح ، ولكن المسار المحتمل لأقل مقاومة.
هل فهمت الموقف بدقة ، أن NSDateFormatter الحالي يتبع Unicode بدقة دون امتدادات ؛ وتنسيقات Unicode غير كافية لوصف تاريخ RFC3339 بالكامل؟
هامش لقد عدت إلى هذا بعد ثلاث سنوات لأخذ إضافة صغيرة: أضافت Unicode و Apple هذه الميزة إلى سلاسل التنسيق ، اعتبارًا من iOS6/OSX10.8. قارن آخر مراجعة حتى كتابة هذه السطور مع سلفها المباشر, ، ولاحظ إضافة 5 "Z" S ، التي تعطي تنسيق المنطقة مثل "-08: 00". لذلك إذا تمكنت من التخلص من الدعم للتخلي عن 5.x/10.7 ، فهناك طريقة صحيحة جديدة للقيام بذلك. سأترك موقف الإجابة السابق ، لأنه لا يزال أفضل طريقة عندما يلزم التوافق المتخلف.
المحلول
يمكن أن يكون تحليل سلسلة التاريخ في الكاكاو ألمًا ، خاصة إذا كان عليك التعامل مع التواريخ التي تم إنشاؤها بواسطة خدمات الويب المستندة إلى .NET.
أقترح النظر في فئة NSDATE+InternetDateTime التي لدى Michael Waterfall على NSDATE كجزء من مشروع MWFeedParser الخاص به على Github. لقد عملت بشكل جيد بالنسبة لي لتحليل شكل التاريخ الذي تصفه بالضبط.
نصائح أخرى
- getObjectValue: forstring: المدى: خطأ: يمكن بالفعل تحليل RFC3339 بشكل صحيح. ليس لدي أي فكرة لماذا - DateWithString: لا يمكن:
// RFC3339 date formatting
NSString *dateString = @"2012-04-11T18:34:19+00:00";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
NSDate *date;
NSError *error;
[formatter getObjectValue:&date forString:dateString range:nil error:&error];
يتم توثيق أحرف تنسيق السلسلة في أي مكان في وثائق Apple. بدلاً من ذلك ، هناك رابط مخفي بعمق في بعض المستندات التي تشير إلى معيار Unicode في
http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#date_format_patterns
استخدام المعلومات في هذا الرابط أمر بسيط للغاية:
- (NSDate*)dateFromRFC3339String:(NSString*)aString
{
static NSDateFormatter* sRFC3339DateFormatter = nil;
static NSDateFormatter* sRFC3339DateFormatterSubSeconds = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
sRFC3339DateFormatter = [[NSDateFormatter alloc] init];
[sRFC3339DateFormatter setLocale:enUSPOSIXLocale];
[sRFC3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssXXXXX"];
[sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
sRFC3339DateFormatterSubSeconds = [[NSDateFormatter alloc] init];
[sRFC3339DateFormatterSubSeconds setLocale:enUSPOSIXLocale];
[sRFC3339DateFormatterSubSeconds setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSSXXXXX"];
[sRFC3339DateFormatterSubSeconds setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
});
NSDate* date = [sRFC3339DateFormatter dateFromString:aString];
if (date == nil)
date = [sRFC3339DateFormatterSubSeconds dateFromString:aString];
return date;
}