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

Java 如何存储日期范围(实际上是时间戳),java,oracle,architecture,types,date-range,Java,Oracle,Architecture,Types,Date Range,Java和Oracle都有一个称为Date的时间戳类型。开发人员倾向于像处理日历日期一样处理这些数据,我看到这些数据会导致一次性的错误 对于基本日期数量,您可以在输入时简单地删除时间部分,即降低精度。但如果使用日期范围(例如:9/29-9/30)执行此操作,则这两个值之间的差值为1天,而不是2天。此外,范围比较需要1)截断操作:start

Java和Oracle都有一个称为Date的时间戳类型。开发人员倾向于像处理日历日期一样处理这些数据,我看到这些数据会导致一次性的错误


  • 对于基本日期数量,您可以在输入时简单地删除时间部分,即降低精度。但如果使用日期范围(例如:9/29-9/30)执行此操作,则这两个值之间的差值为1天,而不是2天。此外,范围比较需要1)截断操作:
    startJava.util.Date
    应该是不可变的,但它不是。(Java7承诺用一个新的日期/时间API来解决这个问题。)几乎每个企业应用程序都依赖于各种各样的数据,在某个时候,您需要对日期和时间进行算术运算

    理想情况下,您可以使用Google日历。如果您不能做到这一点,那么我想一个包含
    java.util.Date
    的包装器的API就足够了,它的计算方法类似于Grails/Rails,并且包含一系列包装器(即一个有序的对,指示一个时间段的开始和结束)


    在我当前的项目(一个HR计时应用程序)中,我们尝试将Oracle和Java的所有日期标准化为同一时区。幸运的是,我们的本地化要求很轻(=1时区就足够了)。当一个持久对象不需要比一天更精确的精度时,我们使用午夜的时间戳。我会更进一步,坚持将额外的毫秒浪费到持久对象可以容忍的最粗粒度(这将使您的处理更简单)

    我使用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'
    
    请注意使用(1/24/60/60)而不是幻数。在Oracle中,通过添加一天中定义的分数来执行日期算术是很常见的。。。3/24三小时,27/24/60 27分钟。此类型的Oracle math是精确的,不会出现舍入错误,因此:

    select 27/24/60 from dual;
    

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

    根据我的经验,有四种主要方法:

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

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

    3) 将其存储为日期

    4) 将其存储为字符串

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

    Oracle拥有。它存储日期数据类型的年、月和日,以及小时、分钟、秒和分数秒值


    这是一个关于日期的算法。

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


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

    通过将getTime()的结果存储为长整数,所有日期都可以明确地存储为GMT时间戳(即没有时区或夏令时)


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

    Alan是对的-Joda time很棒。Date和Calendar只是一个遗憾

    如果需要时间戳,请使用带有时间的oracle日期类型,用某种后缀命名该列,如_tmst。将数据读入java时,将其读入joda time DateTime对象。要确保时区是正确的,可以考虑在Oracle中有特定的数据类型,它将用时区存储时间戳。也可以在表中创建另一列来存储时区ID。时区ID的值应为时区的标准全名ID,请参阅。如果对TZ dta使用另一列,则在将数据读入java时,请使用DateTime对象,但在DateTime对象上使用.withZoneRetainFields设置时区

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

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

    以下是我们的做法

  • 使用时间戳

  • 使用半开式
    select 27/24/60 from dual;
    
    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 );