【问题标题】:Generating Datetime sequence using Joda-Time API使用 Joda-Time API 生成日期时间序列
【发布时间】:2017-11-28 09:15:32
【问题描述】:

我想以 15 分钟的时间间隔生成以下格式 (yyyy-mm-dd hh:mm:ss) 的时间序列。我会给出开始日期和结束日期。

使用以下代码进行测试。

public static void main(String[] args) throws ParseException {
        DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-mm-dd HH:mm:ss");
        DateTime dt1 = formatter.parseDateTime("2017-06-21 00:00:00");
        DateTime dt2 = formatter.parseDateTime("2017-06-23 00:00:00");

        DateTime dateTime1 = new DateTime(dt1);

        DateTime dateTime2 = new DateTime(dt2);
        List<Date> allDates = new ArrayList();

        while( dateTime1.isBefore(dateTime2) ){
           allDates.add( dateTime1.toDate() );
           dateTime1 = dateTime1.plusMinutes(15);

           System.out.println(dateTime1);
        }

它生成如下输出:

2017-01-21T00:15:00.000+05:30

预期的输出是 2017-06-21 00:00:00 ,它没有选择我想要的正确日期。

【问题讨论】:

  • 看看你的模式。在您看来,相同的 mm 模式在几个月和几分钟内都使用是正常的吗?
  • 我已将模式更改为 "YYYY-MM-DD HH:mm:ss" ,但仍然没有得到预期的输出。得到相同的输出。 2017-01-21T00:15:00.000+05:30 2017-01-21T00:30:00.000+05:30 2017-01-21T00:45:00.000+05:30
  • 不要随意做事。阅读文档以使用 right 模式。

标签: java datetime jodatime datetime-format datetime-parsing


【解决方案1】:

tl;博士

使用取代 Joda-Time 项目的 java.time 类。将输入转换为标准格式以便于解析。

LocalDateTime.parse( 
    "2017-06-23 00:00:00".replace( " " , "T" ) 
).plus( Duration.ofMinutes( 15 ) ) 

详情

正如 JB Nizet 所指出的,您在格式化模式中使用了不正确的代码。显然,您是在猜测代码而不是阅读文档——这是一种不明智的做法。这些代码在 Stack Overflow 上已经被覆盖了数百次,所以显然您是在此处发布,而无需费心第一次搜索。

Joda-Time 与 java.time

您正在使用 Joda-Time 库。该项目现在处于维护模式,团队建议迁移到 java.time 类。

ISO 8601 标准

您的输入字符串几乎符合ISO 8601 标准。要完全遵守,请将中间的空格替换为T。 java.time 类在解析/生成字符串时默认使用 ISO 8601 格式。所以不需要定义任何格式化模式。

注意LocalDateTime

您的输入没有任何时区或offset-from-UTC 的指示。所以我们将解析为LocalDateTime。请注意,LocalDateTime 不是真实的时刻,也不是时间线上的实际点。如果没有时区或偏移的上下文,LocalDateTime 是不现实的。

解决方案

LocalDateTime start = LocalDateTime.parse( "2017-06-21 00:00:00".replace( " " , "T" ) ) ;
LocalDateTime stop = LocalDateTime.parse( "2017-06-23 00:00:00".replace( " " , "T" ) ) ;

对于防御性编程,请确认您的开始在您的停止之前。类似stop.isAfter( start )

在每个循环中,添加您指定的 15 分钟持续时间。

Duration d = Duration.ofMinutes( 15 ) ;
LocalDateTime ldt = start ;
List< LocalDateTime > ldts = new ArrayList<>() ;

while( ! ldt.isAfter( stop ) ) {  // "Not after" is a shorter way of saying "is earlier than or is equal to".
    ldts.add( ldt ) ;
    // Set up the next loop.
    ldt = ldt.plus( d ) ;
}

关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案2】:

    首先,read the javadoc。小写的mm 模式对应于分钟。要获得月份,您需要使用大写的MM。因此,您的格式化程序将是这样的:

    DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
    

    而且你不需要这样做:

    DateTime dateTime1 = new DateTime(dt1);
    

    这是多余的,因为您正在创建 2 个相同的对象。只需使用parseDateTime 方法返回的日期即可。或者干脆做:

    DateTime dateTime1 = dt1;
    

    而且您已经创建了一个DateTimeFormatter,所以只需使用它来格式化输出。而不是:

    System.out.println(dateTime1);
    

    这样做:

    System.out.println(formatter.print(dateTime1));
    

    print 方法将以您想要的格式返回日期。

    我不确定您是否要打印第一个日期 (2017-06-21 00:00:00)。如果需要,只需更改 plusMinutesSystem.out.println 行的顺序即可。


    新的 Java 日期/时间 API

    Joda-Time 处于维护模式,正在被新的 API 取代,因此我不建议使用它来启动新项目。即使在joda's website 中它说:“请注意,Joda-Time 被认为是一个基本上“完成”的项目。没有计划进行重大改进。如果使用 Java SE 8,请迁移到 java.time (JSR-310 )。”

    因此,如果您可以迁移 Joda-Time 代码或启动新项目,请考虑使用新 API。如果您使用的是 Java 8,则可以使用 new java.time API。更简单,less bugged and less error-prone than the old APIs

    如果您使用的是 Java ,则可以使用 ThreeTen Backport,这是 Java 8 新日期/时间类的一个很好的向后移植。对于Android,还有ThreeTenABP(更多关于如何使用它here)。

    下面的代码适用于两者。 唯一的区别是 包名称(在 Java 8 中是 java.time,而在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但类和方法 名称是一样的。

    逻辑非常相似。唯一的区别是我使用了LocalDateTime 类,并将其转换为java.util.Date 我需要知道它在哪个时区。我使用了系统的默认时区,这可能是您想要的(因为您的原始代码也使用了默认时区):

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDateTime dateTime1 = LocalDateTime.parse("2017-06-21 00:00:00", formatter);
    LocalDateTime dateTime2 = LocalDateTime.parse("2017-06-23 00:00:00", formatter);
    
    List<Date> allDates = new ArrayList();
    
    while (dateTime1.isBefore(dateTime2)) {
        // get the date in the system default timezone and convert to java.util.Date
        allDates.add(Date.from(dateTime1.atZone(ZoneId.systemDefault()).toInstant()));
        dateTime1 = dateTime1.plusMinutes(15);
    
        System.out.println(formatter.format(dateTime1));
    }
    

    在 Java 8 中,Date.from 方法可用。在 ThreeTen Backport(Java 7 和 Android)中,您可以改用 org.threeten.bp.DateTimeUtils 类:

    allDates.add(DateTimeUtils.toDate(dateTime1.atZone(ZoneId.systemDefault()).toInstant()));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-24
      • 1970-01-01
      • 2021-01-31
      • 2015-06-27
      • 1970-01-01
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      相关资源
      最近更新 更多