自动续订订阅收据日期格式

19

我正在努力弄清楚以下日期的格式:

2011-05-24 19:02:32 Etc/GMT

这个日期是从苹果的收据验证服务返回的,我需要将其转换为NSDate以进行一些比较操作。真正的麻烦与时区有关。

下面是我已经编写的一些代码:

        NSDictionary *receiptData = [info valueForKey:@"expires_date"];

        NSDateFormatter *f = [[NSDateFormatter alloc] init];

        [f setLocale:[NSLocale currentLocale]];
        [f setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
        [f setDateFormat:@"yyyy-MM-dd HH:mm:ss vvvv"];

        NSLog(@"%@", [f stringFromDate:[NSDate date]]);

        NSDate *subPurchaseDate = [f dateFromString:[receiptData valueForKey:@"original_purchase_date"]];

        [f release];

我已经尝试了我能想到的所有 'v' 和 'Z' 的组合。有什么见解吗?


你解决了这个问题吗?我正要发同样的问题,当我看到你的时候。 - Roger
1
还有其他人觉得苹果返回这个字符串,但苹果框架无法解析它很奇怪吗? - Paul de Lange
1
不是的,但这主要是因为我已经使用苹果技术工作了几年。久而久之,你会习惯这种痛苦。 - Hyperbole
将近9年过去了,但这仍然和当时一样痛苦。 - mm2001
4个回答

16

通过查看日期格式文档,我得到了正确的格式,对我来说运行良好:

        NSDateFormatter * formatter = [NSDateFormatter new];
        formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss VV";

        NSDate * purchaseDate = [formatter dateFromString:latestTransaction[@"original_purchase_date"]];
        NSDate * expirationDate = [formatter dateFromString:latestTransaction[@"expires_date"]];
不需要在格式化程序上设置时区,因为它已经嵌入到字符串中。
操纵日期字符串来解析它不是一个好主意。

来源:http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns


这个方法是有效的,我不会像被接受的答案那样删除时区。你应该交换日期和月份:yyyy-dd-MM => yyyy-MM-dd。 - null
1
由于某种原因,此格式不适用于在以色列购买的客户 :O - Jordan H
我不确定收据中的字符串是什么,但是对于在以色列购买的客户,[formatter dateFromString:latestTransaction[@"expires_date"]]; 返回了 NULL。在美国运行良好。 - Jordan H
有趣。如果您可以发布字符串,我们就可以确定它是否已本地化。以色列时间与欧洲时间相同 - “dd/MM/yyyy”。 - Yariv Nissim
不需要在格式化程序上设置时区,因为它已经嵌入到字符串中了。我的经验表明,在格式化程序上设置时区非常重要,否则会覆盖字符串中嵌入的时区信息。至少在使用iOS 13.4.1和iOS 13.5的Swift时是这样的。 - mm2001

4

经过进一步调查,我在这里使用的代码可以工作,但是存在疑问;

   NSString *purchaseDateString = [@"2011-05-24 19:02:32 Etc/GMT" stringByReplacingOccurrencesOfString:@" Etc/GMT" withString:@""];
   NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
   NSLocale *POSIXLocale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease];
   [formatter setLocale:POSIXLocale];
   [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
   [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

   NSDate *purchaseDate = [formatter dateFromString:purchaseDateString];

我不喜欢我对传入日期字符串所做的假设,所以我认为这段代码可能会在其他商店(我正在针对英国商店进行测试)中出现问题。所以虽然这种方法适用于我的情况,但我真的希望看到一个更健壮的解决方案,它可以正确地解析时区字符串,就像原始问题一样。
我简直不敢相信苹果在这些重要的日期字符串中使用了一个已弃用的时区(所有的Etc/*都已正式弃用)!
编辑:
我注意到你正在使用
NSDictionary *receiptData = [info valueForKey:@"expires_date"];

根据文档,这实际上不是一个字符串,应为“订阅收据的到期日期,表示为自1970年1月1日00:00:00 GMT以来的毫秒数”。

然而,当处理恢复的订阅时,您必须使用purchase_date字段,而该字段是您所描述的文本格式。


最终我在代码中进行了相同的查找/替换操作,但我将“Etc/GMT”替换为“GMT”,以使我的生活变得更加轻松。此外,我对使用此解决方案有与你相同的疑虑,但坦白地说,我找不到其他可行的方案。感谢确认我的解决方案。 - Hyperbole
我会添加一个检查,确保字符串以“Etc/GMT”结尾,如果不是,就记录一些日志(弹出 MFMailComposeViewController 并请用户发送错误报告?)。这有点混乱,但没办法。 - tc.
1
为什么要移除 Etc/GMT?它是一个合法的时区,看起来应该保留。例如:http://php.net/manual/en/timezones.others.php - Tudor
1
@Tudorizer,主要是因为NSDateFormatter似乎无法解析它。 - Roger
它肯定可以解析它,你只需要使用正确的格式。请看下面我的答案。 - Yariv Nissim

1
以下代码可用于解析在App Store收据中发现的RFC 3339日期
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV"
formatter.timeZone = TimeZone(secondsFromGMT: 0)

let date = formatter.date(from: "2020-06-17 10:59:33 Etc/GMT")

请注意必须设置timeZonelocale。根据技术问答 QA1480,在设置dateFormat之前设置locale很重要。

Apple建议在iOS 10+和macOS 10.12+上使用更新的ISO8601DateFormatter API。


0

这是我在Swift 3上执行的操作,用于将收据中的自动续订日期转换为日期。

let expirationDate = "2017-08-16 23:10:35 Etc/GMT"
let receiptDateFormat = "yyyy-MM-dd HH:mm:ss VV"
let theDateFormat = DateFormatter.Style.medium
let theTimeFormat = DateFormatter.Style.medium
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = receiptDateFormat
let firstDate = dateFormatter.date(from: stringDate)
dateFormatter.dateStyle = theDateFormat
dateFormatter.timeStyle = theTimeFormat

let result = dateFormatter.string(from: firstDate!)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接