2010-03-21 94 views

回答

33

为您在您的问题(1200年)所使用的具体的例子开始,技术上的东西会工作。

但是,一般来说,时间戳不适用于此用途。 首先,范围限制是任意的:在MySQL中是1000年1月1日。如果你正在使用12-13世纪的东西,事情会很好......但是如果在某个时候你需要添加一些旧的东西(10世纪或更早),日期将会悲惨地破裂,并且解决该问题将需要将所有历史日期重新格式化为更充分的东西。

时间戳通常表示为原始整数,具有给定的“时间间隔”和“时间点”,因此该数字确实是自到达所表示日期以来流逝的刻度数(或者反向日期反之亦然)。这意味着,与任何fixed-with整型数据类型一样,可表示值的集合也是有限的。大多数时间戳格式我知道牺牲范围有利于精度,主要是因为需要执行时间算术的应用程序通常需要以相当精确的方式来做;而需要使用历史日期的应用程序很少需要执行严格的算术操作。

换言之,时间戳意味着日期精确表示。第二次(或甚至第二次)精确度对历史日期没有意义:你能告诉我,在几毫秒之前,亨利八世加冕为英国国王吗?

在MySQL的情况下,格式固有地定义为“4位数年”,因此任何相关的优化都可以依靠假设年份将有4位数字,或者整个字符串将精确地具有10个字符(“yyyy-mm-dd”)等等。这只是一个运气问题,你在标题中提到的日期仍然适合,但即使依赖它仍然是危险的:除了DB本身可以存储的内容之外,你需要意识到你的服务器堆栈的其余部分可以操纵的东西。例如,如果您使用PHP与数据库进行交互,尝试处理历史日期很可能会在某个点或另一个点崩溃(在32位环境中,UNIX样式时间戳的范围是1901年12月13日到2038年1月19日)。

总结:MySQL将正确存储任何4位数年份的日期;但通常使用历史日期的时间戳几乎可以保证通常会引发问题和头痛。我强烈建议不要这样使用。

希望这会有所帮助。


编辑/添加:

感谢您对这个非常insteresting 答案。我是否应该为历史日期创建自己的algo 或者选择另一个 db,但哪一个? - user284523

我不认为任何数据库对这种日期的支持太多:使用它的应用程序通常具有足够的字符串/文本表示。实际上,对于第1年和以后的日期,文本表示甚至可以产生正确的排序/比较(只要日期由数量级表示:y,m,d阶)。然而,如果涉及“负”日期,比较将会中断(它们仍然会比任何正日期比较早,但比较两个负日期会产生相反的结果)。

如果你只需要1年及以后的日期,或者如果你不需要排序,那么你可以通过使用字符串使你的生活变得更容易。

否则,最好的方法是使用某种数字,并定义您自己的“滴答间隔”和“时代点”。一个好的时间间隔可能是几天(除非你真的需要进一步的精确度,但即使如此,你仍然可以依靠“实数”(浮点数)而不是整数);一个合理的时代可能是1月1日。主要的问题是将这些价值转化为他们的文本表达,反之亦然。你需要记住以下的细节:

  • 闰年有一个额外的一天。
  • 直到1582年闰年的规则是“4的倍数”,当它从朱利安变为公历时,变成“4的倍数,除非是100的倍数,除非它们也是400的倍数”。
  • Julian日历的最后一天是1582年10月4日。第二天,公历第一天是1582年10月15日。跳过了10天以使新日历与季节再次匹配。
  • 如评论所述,上述两条规则因国而异:教皇国家和一些天主教国家在规定的日期确实采用了新日历,但许多其他国家花了更长的时间来这样做(最后一次是1926年的特利) 。这意味着1582年的罗马教皇公牛和1926年的最后一次通过之间的任何日期在没有地理环境的情况下都是模棱两可的,处理起来更加复杂。
  • 没有“0年”:第1年的前一年是第-1年,即公元前1年。

所有这些都需要相当复杂的解析器和formater函数,但是除了许多逐案打破之外,并没有太多的复杂性(编码会非常繁琐,但是非常简单) 。使用数字作为底层表示可确保对任何值对进行正确的排序/比较。

了解了这一点,现在您可以选择更适合您需求的方法。

+1

谢谢你这个非常有趣的答案。我应该为历史日期创建自己的算法还是选择另一个数据库,但是哪一个? – user310291 2010-03-21 16:34:20

+2

我希望SO允许少量的+2 upvotes获得特别好的答案。感谢不仅非常有用的细节,而且使它成为一个有趣的阅读。我有一个小小的狡辩,那就是我认为一般来说“BCE”比“BC”更受欢迎。 – eyelidlessness 2010-11-18 07:15:50

+1

“Julian日历的最后一天是1582年10月4日”在某些欧洲国家,这只是真实的。公历的通过日期差异很大。见https://en.wikipedia.org/wiki/Gregorian_calendar#Adoption – 2017-03-24 12:00:59

11

documentation

DATE

的日期。支持的范围是'1000-01-01'至 '9999-12-31'。

+0

谢谢。所以日期低于1000不支持。 – user310291 2010-03-21 15:22:21

+1

@ user284523使用date或datetime数据类型不支持年1000之前的日期。当然,您可以使用自己的编码作为不同的数据类型(例如varchar)存储更早的日期。 – 2010-03-21 15:26:30

+0

如果我实现自己的编码,这意味着我还必须创建日期函数?在我惊讶地发现任何东西之前,没有人有这种需求吗? – user310291 2010-03-21 16:36:32

4

无论它的价值如何,我发现MySQL DATE字段在实际中确实支持日期< 1000,尽管文档中另有说明。例如,我能够输入325,并且它存储为0325-00-00。搜索WHERE table.date < 1000也给出了正确的结果。

但是,如果他们没有得到官方支持,我很犹豫要依靠1000日期,再加上我有时需要BCE年数超过4位数(例如10000 BCE)。因此,年,月,日独立的INT字段(如上所示)似乎是唯一的选择。

我确实希望DATE类型(或者可能是一个新的HISTDATE类型)支持全部的历史日期 - 将三个字段合并为一个日期并简单地按日期排序而不是按year, month, day排序会很好。

+0

您对这些日期的“非官方”支持提出了一个有趣的观点。你的建议“按行政长官排序,按时间顺序排列日期”是行不通的,并且可能会误导可能使用你的想法的人:你得到的顺序应该是从最新到最早的所有BCE日期,其次是所有CE从最早到最新的日期。这个问题是唯一阻止我给你+1的东西,所以也许你应该考虑修复它。 – 2013-02-13 14:17:30

+0

你是绝对正确的,谢谢。我相应地编辑了我的答案。 – Holly 2013-05-03 00:44:06

2

使用SMALLINT为一年,因此今年将-32768(BC)接受32768(AD) 至于月份和日期,使用TINYINT UNSIGNED

大多数历史事件不要有月份和日期,所以你可以这样的查询:

SELECT events FROM history WHERE year='-4990' 

结果: '诺亚方舟'

或者:SELECT events FROM history WHERE year='570' AND month='4' AND day='20' 回报: “穆罕默德永存出生”

根据要求,你还可以添加DATETIME列,使其NULL日期1000反之亦然之前(从而节省一些字节)

2

这是一个有另一种解决方案的一个重要和有趣的问题。而不是依靠数据库平台来支持具有毫秒级精度的潜在无限数量的日期,而是依靠面向对象的编程语言编译器和运行时来正确处理日期和时间算法。

使用Java虚拟机(JVM)可以做到这一点,其中时间相对于UTC(Epoch)1970年1月1日午夜以毫秒为单位,通过在数据库中将所需值持久保存包括负值),并在检索后在组件层中执行所需的转换/计算。

例如:

Date d = new Date(Long.MIN_VALUE); 
DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy G HH:mm:ss Z"); 
System.out.println(df.format(d)); 

应显示: 太阳,12月2日292269055公元前16时47分04秒+0000

这也使数据库版本和平台的独立性,因为它抽象的所有日期和(即运行时),即数据库版本和平台的更改将不太可能需要重新实现(如果有的话)。

+0

这是一个有趣的答案,但有一点需要注意:虽然这种方法会消除对数据库系统的依赖,但它引入了对Java环境的依赖;所以这是一个折衷而非净收益。另外,请记住,对数据库的依赖并未完全消除:您需要一个匹配Java'long'的字段类型;并且这种类型的名称在不同的数据库系统上可能会(略有不同)。 – 2013-05-07 13:37:37

2

我有类似的问题,我想继续在数据库中的日期字段上继电器,以允许我使用日期范围搜索精确度为历史值最多一天。 (我的数据库包括出生和罗马皇帝的日期的日期......)

解决的办法是增加一个恒定的一年(例如:3000)将其添加到数据库,并减去同之前所有的日期在向用户显示查询结果之前的编号。

如果DB中已经有一些日期值,请记住用新的常量编号更新退出值。