【问题标题】:NSDate Min and Max Possible ValuesNSDate 最小和最大可能值
【发布时间】:2013-03-01 11:46:51
【问题描述】:

有人知道 NSDate 的最小和最大可能值是多少吗?

【问题讨论】:

  • 请说明在什么情况下您需要知道这一点。
  • 有什么需要???
  • ===> WWDC 2011 视频“第 117 场 - 执行日历计算”(59:11)
  • 我从 JSON .NET 服务接收基于纪元的日期/时间值,并使用 [NSDate dateWithTimeIntervalSince1970:] 将这些转换为 NSDate。打印出 0001-01-01 的值(.NET DateTime.MinValue)时,我看到一个不寻常的输出;它错误地打印为 0001 年 12 月 31 日,我想知道这是否与 NSDate 的最低限制有关。随后,经过更多挖掘后,我发现问题出在 NSDateFormatter 上。但是我仍然很好奇最大值和最小值是什么,也许除了它的基础类型(双精度?)之外没有任何可行的界限。
  • @HaggleLad 你的直觉是正确的。边界由基础类型定义。至于“0001-01-01”位......当您谈论超过几百年的日期时,没有日历是可靠的,主要是因为人类记录不可靠。至于为什么打印为 12 月 31 日,请确保您正确设置了时区。 “0001 年 12 月 31 日”实际上可能指的是(例如)北美的公元前 1 年,而格林威治标准时间已经是公元 1 月 1 日。

标签: objective-c cocoa nsdate


【解决方案1】:

【讨论】:

  • +1 苹果为这类棘手的问题提供了这些方法。
  • 对于那些像 OP 一样正在转换 .Net DateTime 值并希望支持 DateTime.MinValue 和 DateTime.MaxValue 的人,那么这些 Swift 方法可能会很有趣:stackoverflow.com/a/41625877/253938
【解决方案2】:

编辑答案:

我以为我发现最小日期值是 0001-01-01 00:00:00 + 0000(在下面给出我的代码)但是正如下面的 cmets 所指出的,这仅适用于 当前时代(即公元)。 NSDate 值可能会低于此值,但您看到的 NSLog 结果可能不是您所期望的,因此我建议您在此处查看其他答案并忽略我的无知。

原始答案:

我相信我发现最小日期值是0001-01-01 00:00:00 + 0000

在 NSLog'ing 和解析值时需要非常小心,以确保您的时区不会稍微偏离日期值。

我将在下面尝试和演示:

NSDateFormatter *dateFormatter1 = [[NSDateFormatter alloc] init];
[dateFormatter1 setDateFormat:@"yyyy-MM-dd hh:mm:ss z"];

NSDateFormatter *dateFormatter2 = [[NSDateFormatter alloc] init];
[dateFormatter2 setDateFormat:@"yyyy-MM-dd HH:mm:ss z"];

NSDateFormatter *dateFormatter3 = [[NSDateFormatter alloc] init];
[dateFormatter3 setDateFormat:@"yyyy-MM-dd HH:mm:ss z"];
[dateFormatter3 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

NSDate *date1 = [NSDate dateWithTimeIntervalSince1970:-62135596800];
NSLog(@"date1 = %@", date1);
NSLog(@"date1 via dateFormatter1 = %@", [dateFormatter1 stringFromDate:date1]);
NSLog(@"date1 via dateFormatter2 = %@", [dateFormatter2 stringFromDate:date1]);
NSLog(@"date1 via dateFormatter3 = %@", [dateFormatter3 stringFromDate:date1]);

NSDate *date2 = [NSDate dateWithTimeIntervalSince1970:-62135596800 - 100]; // The - 100 makes no difference
NSLog(@"date2 = %@", date2);
NSLog(@"date2 via dateFormatter1 = %@", [dateFormatter1 stringFromDate:date2]);
NSLog(@"date2 via dateFormatter2 = %@", [dateFormatter2 stringFromDate:date2]);
NSLog(@"date2 via dateFormatter3 = %@", [dateFormatter3 stringFromDate:date2]);

这会产生以下输出(在我的 iPad 上给定其特定时区):

date1 = 0001-01-01 00:00:00 +0000
date1 via dateFormatter1 = 0001-12-31 11:58:45 GMT-00:01:15
date1 via dateFormatter2 = 0001-12-31 23:58:45 GMT-00:01:15
date1 via dateFormatter3 = 0001-01-01 00:00:00 GMT
date2 = 0001-12-31 23:58:20 +0000
date2 via dateFormatter1 = 0001-12-31 11:57:05 GMT-00:01:15
date2 via dateFormatter2 = 0001-12-31 23:57:05 GMT-00:01:15
date2 via dateFormatter3 = 0001-12-31 23:58:20 GMT

注意 date2 以及如何减去额外的 100 秒并没有减少日期的值,但实际上似乎增加了它。显然 NSDate 的内部工作方式有些特殊,时间调整了 100 秒,但这可能会使日期和月份出现偏差,也许是因为它不能再低了?

【讨论】:

  • 如果你尝试 [NSDate dateWithTimeIntervalSinceNow:- DBL_MAX] 你会得到类似 41221-07--2147483641 00:00:00 +0000 ..
  • -1 不正确。这只是当前时代的最短日期,而不是总体上的最短日期。
  • 好的,谢谢你的指点,我已经更新了我的答案。日期让我大吃一惊,NSDateFormatter 真的无济于事。
【解决方案3】:

最大值应该是:5828963-12-20 00:00:00 +0000 因为

NSDate *dateSinceNow = [NSDate dateWithTimeIntervalSinceNow:DBL_MAX];
NSDate *dateSindeReferenceDate = [NSDate dateWithTimeIntervalSinceReferenceDate:DBL_MAX];
NSLog(@"min: %@, max: %@", dateSinceNow, dateSindeReferenceDate);

是一样的。

我得到的最低限度是:0001-01-01 00:00:00 +0000

因为这是我尝试时得到的:

NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:@"yyyy-MM-dd hh:mm:ss"];
[df setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDate *myDate = [df dateFromString: @"0001-01-01 00:00:00"];
NSLog(@"%@", myDate);

但正如 davedelong 所指出的,这是当前时代

因此,如果您尝试使用 - DBL_MAX 的第一个示例,您会得到:41221-07--2147483641 00:00:00 +0000。不知道这是什么时代...

【讨论】:

  • 我相信您遇到了同样的问题,导致我提出这个问题。我认为最小可能的日期值是 0001-01-01 00:00:00 但是当您使用 NSDateFormatter 时,日期会被您的时区扭曲。使用以下命令重试:[df setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]。
  • 设置时区也给了我 0001-01-01 00:00:00 +0000
  • 这只是对了一半。最大位是正确的,但是您的最小值逻辑是有缺陷的。这是当前时代 (CE) 的最低要求。 NSDate 完全能够表达 BCE 日期。你要找的是[NSDate dateWithTimeIntervalSince1970:-DBL_MAX]
  • @DaveDeLong 是 41221-07--2147483641 00:00:00 +0000 a bce date?还是我必须要一个时代或别的什么?
  • @peko 在我看来很像。您可以通过-compare:-将该日期更改为0001-01-01 日期来确认这一点。
【解决方案4】:

OP 的真正问题(在他对自己的 OP 的评论中澄清)大约是 1/1/1。正如您在我的 TL;DR; 中的 Playground 中所见,如果您未指定,Apple 会打印“31 Dec 0001”而不是“31 Dec 0001 BC”带有 Era 的 DateFormatter,因此如果 OP 的本地时区不是 UTC,那么公元 1/1/1 很容易是 31/1/1 BC(前一天)。

NSDate / Date 可以有效地使用到过去约 3000 万年,但 NSDateComponents / DateComponents 操作在公元前 4500 年之前会失败。请继续阅读以了解详细信息...

TL;DR;

正如一些评论员和受访者所指出的,[NSDate distantPast](或 Swift 中的 Date.distantPast)由 API 提供,然而,这不是最低要求NSDate / Date - 这只是为了方便业务逻辑,因为很少有应用程序需要对 BCE 进行计算(请注意,.distantFuture 也是任意的 - 它是公元 4001 年 1 月 1 日)。

其他受访者 (@peko) 处理过真正的“distantFuture”最大值,但没有人完全探索过最小值。

那么,当您实际给它提供.distantPast 之前的日期时,Date 会做什么?这是一个小游乐场:

// Swift 5 Playground
import Foundation

var d = Date.distantPast // "31 Dec 1 at 23:58"
// However, that's the default print, which *does not* include Era

let df = DateFormatter()
df.dateFormat = "EEEE dd MMMM yyyy G" // "G" is Era

print(df.string(from: d)) // "Friday 31 December 0001 BC\n"

// What happens when we go lower?
d = d.addingTimeInterval(-1e12) // "29 Nov 31689 at 22:12"
print(df.string(from: d)) // Thursday 29 November 31689 BC\n"

// Lower still?
d = d.addingTimeInterval(-1e14) // "18 Feb 3200497 at 12:25"
print(df.string(from: d)) // "Tuesday 18 February 3200497 BC\n"

// But it breaks here...
d = d.addingTimeInterval(-1e15) // ""
print(df.string(from: d)) // "\n"

所以NSDate / Date 的有效时间最短为 3000 万年。

然而,我在计算时遇到了很多困难,而NSDate / Date 无法处理。

具体来说,Calendar.dateComponents(unitFlags, from: date) 失败(并打印如下错误消息),早于公元前 4713 年左右。

2021-12-03 19:15:48.885955+0000 Tempus II[61381:6027263] [一般] CFAbsoluteTime -268229356861.914001 超出日历计算 范围。

... "-268229356861.914001" 是公元前 6500 年,但任何尝试对公元前 4713 年之前的任何事物进行 DateComponent 计算都会给出类似的信息,计算会出错!

这是另一个小游乐场,您可以自己尝试一下:

let df = DateFormatter()
df.dateFormat = "EEEE dd MMMM yyyy G" // "G" is Era

// 500BC -> by 1000 years, which works
let dc500 = DateComponents(calendar: .autoupdatingCurrent,
                           era: 0,
                           year: 500)
var d = Calendar.autoupdatingCurrent.date(from: dc500)!
print(df.string(from: d)) // Thursday 01 January 0500 BC\n"

d = Calendar.autoupdatingCurrent.date(byAdding: delta, to: d)!
print(df.string(from: d)) // "Monday 01 January 0501 AD\n"
// *** NB 501AD is the RIGHT answer for 500BC + 1000, as there
// *** was no year 0

d = Calendar.autoupdatingCurrent.date(byAdding: delta, to: d)!
print(df.string(from: d)) // "Friday 01 January 1501 AD\n"


// 5000BC -> by 2000 years, which doesn't work
delta = DateComponents(calendar: .autoupdatingCurrent,
                       year: 2000)

let dc5000 = DateComponents(calendar: .autoupdatingCurrent,
                            era: 0,
                            year: 5000)
d = Calendar.autoupdatingCurrent.date(from: dc5000)!
print(df.string(from: d)) // "Sunday 01 January 5000 BC\n"

d = Calendar.autoupdatingCurrent.date(byAdding: delta, to: d)!
print(df.string(from: d)) // "Tuesday 01 January 2713 BC\n"
// *** WRONG!

d = Calendar.autoupdatingCurrent.date(byAdding: delta, to: d)!
print(df.string(from: d)) // "Wednesday 01 January 0713 BC\n"
// *** Back on track again

d = Calendar.autoupdatingCurrent.date(byAdding: delta, to: d)!
print(df.string(from: d)) // "Thursday 01 January 1288 AD\n"
// *** Again, correct 
// 713 -> 1BC + 1 AD -> 1288 = 1288 + 713 - 1 = 2000

附言

在不了解TimeZoneCalendarDateComponentsDateFormatter的情况下,不要在业务逻辑中使用Date

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 2016-12-04
    • 2012-09-16
    • 2016-04-10
    • 2019-01-09
    相关资源
    最近更新 更多