代码之家  ›  专栏  ›  技术社区  ›  Chris Noe Paweł Dyda

如何存储日期范围(实际上是时间戳)

  •  7
  • Chris Noe Paweł Dyda  · 技术社区  · 16 年前

    时间戳 类型名为Date。开发人员倾向于操纵这些,就好像它们是 约会,我见过会引起一次性的讨厌的虫子。

    1. 对于基本日期数量,您可以在输入时简单地删除时间部分,即降低精度。但如果您在日期范围内执行此操作,(例如: 9/29-9/30 ),这两个值之间的差值为1天,而不是2天。此外,范围比较需要1)截断操作: start < trunc(now) <= end start < now < (end + 24hrs) . 不可怕,但不可怕 DRY .

    2. 9/29 00:00:00 - 10/1 start <= now < end . 当然,对于内部处理,结束日期需要在初始输入(+1)时转换,而对于输出(-1),则假定在用户级别使用日历日期隐喻。

    如何处理项目的日期范围?还有其他选择吗?我特别感兴趣的是如何在Java和Oracle方面处理这个问题。

    11 回复  |  直到 16 年前
        1
  •  7
  •   S.Lott    16 年前

    我们是这样做的。

    1. 使用时间戳。

    2. 使用半开间隔进行比较: start <= now < end .

    忽略那些坚持认为两者之间在某种程度上对SQL的成功至关重要的抱怨者。

    有了它,一系列的日期范围真的很容易审核。的数据库值 9/30 to 10/1 包括一天(9/30)。下一个间隔的开始必须等于上一个间隔的结束。那个 interval[n-1].end == interval[n].start 规则便于审计。

    显示时,如果需要,可以显示已格式化的 start end -1.事实证明,你 可以 教育人们认识到“结束”实际上是规则不再正确的第一天。因此,“9/30至10/1”意味着“有效开始9/30,不再有效开始10/1”。

        2
  •  4
  •   Eddie Awad    16 年前

    甲骨文拥有 TIMESTAMP datatype

    thread on asktom.oracle.com 关于日期算术。

        3
  •  3
  •   Werner Lehmann    16 年前

    我赞同S.洛特的解释。我们有一个广泛使用日期时间范围的产品套件,这是我们从中吸取的教训之一。顺便说一下,我们称之为结束日期 独家 结束日期,如果它不再是范围的一部分(低,半开放间隔)。相比之下,它是一个 包含全部费用 结束日期,如果它是范围的一部分,只有在没有时间部分时才有意义。

    用户通常期望输入/输出包含的日期范围。无论如何,请尽快将用户输入转换为独占的结束日期范围,并在必须向用户显示任何日期范围时尽可能晚地转换。

    在数据库上,始终存储独占的结束日期范围。如果存在包含结束日期范围的旧数据,请尽可能在DB上迁移它们,或者在读取数据时尽快转换为独占结束日期范围。

        4
  •  2
  •   David Aldridge    16 年前

    我使用Oracle的日期数据类型,并就影响边界条件的时间组件问题对开发人员进行培训。

    数据库约束还将防止意外指定列中不应包含时间组件的时间组件,并告知优化器所有值都不包含时间组件。

    例如,约束检查(MY_DATE=TRUNC(MY_DATE))防止将时间不是00:00:00的值放入MY_DATE列,还允许Oracle推断诸如MY_DATE=to_DATE('2008-09-12 15:00:00')之类的谓词永远不会为真,因此不会从表中返回任何行,因为它可以扩展为:

    MY_DATE = TO_DATE('2008-09-12 15:00:00') AND
    TO_DATE('2008-09-12 15:00:00') = TRUNC(TO_DATE('2008-09-12 15:00:00'))
    

    当然,这是自动错误的。

    尽管有时很容易将日期存储为数字,如20080915,但这可能会导致查询优化问题。例如,在20071231和20070101之间有多少法律价值?在2007年12月31日和2008年1月1日之间呢?它还允许输入非法值,例如20070100。

    因此,如果日期没有时间成分,那么定义范围就变得很容易:

    select ...
    from   ...
    where  my_date Between date '2008-01-01' and date '2008-01-05'
    

    当存在时间组件时,可以执行以下操作之一:

    select ...
    from   ...
    where  my_date >= date '2008-01-01' and
           my_date  < date '2008-01-06'
    

    select ...
    from   ...
    where  my_date Between date '2008-01-01'
                       and date '2008-01-05'-(1/24/60/60)
    

    select 27/24/60 from dual;
    

    ... 给出0.01875,而不是0.0187499999999或其他任何值。

        5
  •  1
  •   Mark Brady Mark Brady    16 年前

    我还没有看到发布的间隔数据类型。

    Oracle还为您的具体场景提供了数据类型。Oracle中也有从年到月的间隔数据类型和从天到秒的间隔数据类型。

    间隔年到月存储一个期间 日期时间字段。此数据类型为 用于表示差异 仅当 年份和月份值为 重要的

    间隔年[(年精度)]至 月

    其中,year_precision是 年份日期时间字段中的数字。这个

    间隔日到秒数据类型

    间隔日到秒存储一个周期 以天、小时为单位的时间, 分,秒。此数据类型为 用于表示精确的 两个日期时间之间的差异 价值观

    第二 [(分数秒精度)]

    哪里

    day_precision是位数 在DAY datetime字段中。认可的 值为0到9。默认值为2。

    分数秒精度是 接受的值为0到9。这个 默认值为6。

    你有很大的灵活性 将间隔值指定为 “文本”,以获取有关 将间隔值指定为文字。 另请参见“日期时间和间隔” “示例”用于使用 间隔。

        6
  •  0
  •   Steve M    16 年前

    1) 将日期转换为历元整数(自1970年1月1日起的秒数),并将其作为整数存储在数据库中。

    2) 将日期转换为YYYYMMDDHHMMSS整数,并将其作为整数存储在数据库中。

    3) 将其存储为日期

    我一直坚持使用1和2,因为它使您能够对日期执行快速而简单的算法,而不依赖于底层数据库功能。

        7
  •  0
  •   Alan    16 年前

    根据您的第一句话,您遇到了Java隐藏的“特性”(即bug)之一: java.util.Date 应该是不变的,但不是。(Java7承诺用一个新的日期/时间API解决这个问题。)几乎每个企业应用程序都依赖于各种 temporal patterns ,在某个时刻,你需要计算日期和时间。

    Joda time ,谷歌日历使用。如果你做不到这一点,我猜是一个由包装器组成的API 日期类型 使用类似于Grails/Rails的计算方法,以及一系列包装器(即,指示时间段开始和结束的有序对)就足够了。

        8
  •  0
  •   Ates Goral    16 年前

    getTime() 作为一个长整数。

    在数据库查询中需要日、周、月等操作的情况下,当查询性能至关重要时,时间戳(标准化为比毫秒更高的粒度)可以链接到日期细分表,该表包含日、周、月的列,等值,以便在查询中不必使用昂贵的日期/时间函数。

        9
  •  0
  •   Kyle Dyer    16 年前

    艾伦是对的——乔达时间很好。Date和Calendar只是一个遗憾。

    如果需要时间戳,请使用带有时间的oracle日期类型,用某种后缀命名该列,如_tmst。将数据读入java时,将其读入joda time DateTime对象。要确保时区是正确的,可以考虑在Oracle中有特定的数据类型,它将用时区存储时间戳。或者,您可以在表中创建另一列来存储时区ID。时区ID的值应为时区的标准全名ID,请参阅 http://java.sun.com/j2se/1.4.2/docs/api/java/util/TimeZone.html#getTimeZone%28java.lang.String%29 . 如果对TZ dta使用另一列,则在将数据读入java时,请使用DateTime对象,但在DateTime对象上使用.withZoneRetainFields设置时区。

    如果只需要日期数据(无时间戳),则在数据库中使用日期类型,而不使用时间。再说一次。在本例中,使用来自jodatime的DateMidnight对象。

    底线:利用数据库的类型系统和您使用的语言。学习它们,并从使用表达性api和语言语法来处理问题中获益。

        10
  •  0
  •   Basil Bourque    7 年前

    更新:Joda Time项目现在处于维护模式。它的团队建议迁移到 java.time Java中内置的类。

    Joda Time提供了3个类来表示时间跨度:间隔、持续时间和周期。

    ISO 8601标准规定了如何格式化表示 Duration Interval . Joda Time解析并生成这样的字符串。

    时区是一个重要的考虑因素。数据库应以UTC格式存储其日期时间值。但是您的业务逻辑可能需要考虑时区。“一天”的开始取决于时区。顺便说一下,使用 proper time zone names 而不是3或4个字母的代码。

    这个 correct answer by S.Lott 明智地建议使用半开放逻辑,因为这通常最适合日期时间工作。一段时间的开始是 包含全部费用 而结局是 独家

    diagram defining a week as greater than or equal to Day 1 and less than Day 8

    DateTimeZone timeZone_NewYork = DateTimeZone.forID( "America/New_York" );
    DateTime start = new DateTime( 2014, 9, 29, 15, 16, 17, timeZone_NewYork );
    DateTime stop = new DateTime( 2014, 9, 30, 1, 2, 3, timeZone_NewYork );
    
    int daysBetween = Days.daysBetween( start, stop ).getDays();
    
    Period period = new Period( start, stop );
    
    Interval interval = new Interval( start, stop );
    Interval intervalWholeDays = new Interval( start.withTimeAtStartOfDay(), stop.plusDays( 1 ).withTimeAtStartOfDay() );
    
    DateTime lateNight29th = new DateTime( 2014, 9, 29, 23, 0, 0, timeZone_NewYork );
    boolean containsLateNight29th = interval.contains( lateNight29th );
    

    转储到控制台

    System.out.println( "start: " + start );
    System.out.println( "stop: " + stop );
    System.out.println( "daysBetween: " + daysBetween );
    System.out.println( "period: " + period ); // Uses format: PnYnMnDTnHnMnS
    System.out.println( "interval: " + interval );
    System.out.println( "intervalWholeDays: " + intervalWholeDays );
    System.out.println( "lateNight29th: " + lateNight29th );
    System.out.println( "containsLateNight29th: " + containsLateNight29th );
    

    跑步时

    start: 2014-09-29T15:16:17.000-04:00
    stop: 2014-09-30T01:02:03.000-04:00
    daysBetween: 0
    period: PT9H45M46S
    interval: 2014-09-29T15:16:17.000-04:00/2014-09-30T01:02:03.000-04:00
    intervalWholeDays: 2014-09-29T00:00:00.000-04:00/2014-10-01T00:00:00.000-04:00
    lateNight29th: 2014-09-29T23:00:00.000-04:00
    containsLateNight29th: true
    
        11
  •  -1
  •   Vugluskr    16 年前

    我以毫秒为单位存储所有日期。我根本不使用时间戳/日期时间字段。

    所以,我必须把它当作长线来操纵。这意味着我在sql查询中不使用“before”、“after”、“now”关键字。