将java日期分成几小时

时间:2017-03-06 15:54:15

标签: java

我在程序中收到以下startdate和enddate作为输入。

2017-03-07 06:30:00至2017-03-07 11:35:00(yyyy-mm-dd hh:mi:ss)

我想按小时打破日期。对于我上面给出的示例,我想创建一个类似于下面列出的值的列表。

2017-03-07 0630-07 
2017-03-07 07-08  
2017-03-07 08-09 
2017-03-07 09-10  
2017-03-07 10-11  
2017-03-07 11-1135

你能否提出一个我可以探索的方法?

3 个答案:

答案 0 :(得分:3)

如果您可以使用Java8,请参考我建议的方法:

// with LocalDateTime startDate and LocalDateTime endDate defined,
LocalDateTime currentRangeStart = startDate;
while (currentRangeStart.isBefore(endDate)) {
    LocalDateTime nextHour = currentRangeStart.withMinute(0).plusHours(1);
    LocalDateTime currentRangeEnd = nextHour.isBefore(endDate) ? nextHour : endDate;
    System.out.printf("%s - %s%n", currentRangeStart, currentRangeEnd);
    currentRangeStart = currentRangeEnd;
}

Here你可以看到它的实际效果。如果您目前有Date个对象,请查看this question,了解如何从Date转换为LocalDateTime

答案 1 :(得分:2)

我会建立一个从开始日期到计数的地图。有点像...

Map<java.util.Date,Integer> map = new TreeMap<>();
Calendar cal = new GregorianCalendar();
for (java.sql.Timestamp exactDate: dates) {
  cal.setTime(exactDate);
  cal.set(Calendar.MINUTE, 0);
  cal.set(Calendar.SECOND, 0);
  cal.set(Calendar.MILLISECOND, 0);
  java.util.Date key = new java.util.Date(cal.getTime());
  Integer count;
  if ((count = map.get(key)) == null) {
    map.put(key, Integer.valueOf(1);
  } else {
    map.put(key, Integer.valueOf(1 + count.intValue()));
  }
}

然后遍历地图以取出值;或者对从第一个到最后一个的所有可能值进行for循环以打印“零”小时。

然而,在数据库查询中进行排序和分组的工作可能不仅容易出错,而且效率更高,如

SELECT trunc(log_date,'HH24'),count(*) 
FROM tbl
GROUP BY trunc(log_date,'HH24')
ORDER BY trunc(log_date,'HH24')

答案 2 :(得分:1)

Answer by Aaron很好,但它忽略了输入中可能出现的任何秒或秒数。此外,我可以扩展一点,并应用时区。

截断

您可以截断LocalDateTime个对象,在增加小时数时消除任何整数或小数秒。

LocalDateTime ldtTruncated = ldt.truncatedTo( ChronoUnit.MINUTES ); // Lop off seconds and nanoseconds.

...或...

LocalDateTime ldtTruncated = ldt.truncatedTo( ChronoUnit.HOURS ); // Lop off minutes, seconds, and nanoseconds.

时区

一个关键问题是时区。这些值缺少offset-from-UTCtime zone的任何指标。因此,正如Aaron正确显示的那样,我们应该首先将它们解析为LocalDateTime

LocalDateTime ldtStart = LocalDateTime.parse ( "2017-03-07 06:30:00".replace ( " " , "T" ) );
LocalDateTime ldtStop = LocalDateTime.parse ( "2017-03-07 11:35:00".replace ( " " , "T" ) );

但我从问题的措辞中假设我们想要处理特定的时刻。根据定义,LocalDateTime 不是特定时刻,只是对潜在时刻的模糊概念。您必须添加时区(或偏移)的上下文以确定实际时刻。因此,如果您知道用于这些值的区域,请应用它。

由于Daylight Saving Time (DST)等异常情况,ZonedDateTime类会对您的特定时区无效的输入值进行调整。

ZoneId z = ZoneId.of ( "America/Montreal" );
ZonedDateTime zdtStart = ldtStart.atZone ( z );
ZonedDateTime zdtStop = ldtStop.atZone ( z );

确保输入有意义。

// Validate the inputs.
if ( zdtStop.isBefore ( zdtStart ) ) {
    // perhaps throw exception
    System.out.println ( "ERROR - stop is befor start." );
    return;
}

日期时间范围,作为一对ZonedDateTime个对象

现在我们可以逐小时递增以生成您想要的范围。我们可以使用ThreeTen-Extra项目中的Interval类(见下文),它跟踪一对始终为UTC的Instant个对象。但这可能对我们没有用,因为我们真的想用分区时间(或者我假设)。因此,让我们将每个日期时间范围跟踪为一对ZonedDateTime类。我们可以为该对创建自己的类,但我们重新使用AbstractMap.SimpleImmutableEntry类。该类将一对对象作为键和值进行跟踪。

不要让那个配对类的名字让你感到困惑。该类的原始目的是与Map一起使用,但本身就是地图。该类是指来自 a Map的键和值,但我们将以其“键”是我们的起始时刻并且其“值”是我们的方式使用停止的时刻。

我们按List类型的AbstractMap.SimpleImmutableEntry收集每个计算的时间范围。

int initialCapacity = ( ( int ) ChronoUnit.HOURS.between ( zdtStart , zdtStop ) ) + 2; // Plus two for good measure. (not sure if needed).
List<AbstractMap.SimpleImmutableEntry<ZonedDateTime , ZonedDateTime>> ranges = new ArrayList<> ( initialCapacity );

使用起始值和循环设置循环。在每个循环中,我们希望将值截断为整整一小时(以减去秒和小数秒)。截断后,我们添加一个小时到达下一个小时。我们只需要在第一个循环上进行截断,因此您可以通过避免在连续循环上进行不必要的截断来重写此代码以提高效率。除非您处理非常大量的小时,否则我不会打扰。

ZonedDateTime zdt = zdtStart;
while ( zdt.isBefore ( zdtStop ) ) {
    ZonedDateTime zdt2 = zdt.truncatedTo ( ChronoUnit.HOURS ).plusHours ( 1 );  // Truncate to whole hour, and add one.
    if ( zdt2.isAfter ( zdtStop ) ) { // Oops, went too far. Clip this range to end at `zdtStop`.
        zdt2 = zdtStop;
    }
    AbstractMap.SimpleImmutableEntry<ZonedDateTime , ZonedDateTime> range = new AbstractMap.SimpleImmutableEntry<> ( zdt , zdt2 );
    ranges.add ( range );
    // Prepare for next loop.
    zdt = zdt2;
}

转储到控制台。

System.out.println ( "ldtStart/ldtStop: " + ldtStart + "/" + ldtStop );
System.out.println ( "zdtStart/zdtStop: " + zdtStart + "/" + zdtStart );
System.out.println ( "ranges: " + ranges );

跑步时。

  

ldtStart / ldtStop:2017-03-07T06:30 / 2017-03-07T11:35

     

zdtStart / zdtStop:2017-03-07T06:30-05:00 [America / Montreal] / 2017-03-07T06:30-05:00 [America / Montreal]

     

范围:[2017-03-07T06:30-05:00 [America / Montreal] = 2017-03-07T07:00-05:00 [America / Montreal],2017-03-07T07:00-05: 00 [America / Montreal] = 2017-03-07T08:00-05:00 [America / Montreal],2017-03-07T08:00-05:00 [America / Montreal] = 2017-03-07T09:00-05 :00 [America / Montreal],2017-03-07T09:00-05:00 [America / Montreal] = 2017-03-07T10:00-05:00 [America / Montreal],2017-03-07T10:00- 05:00 [America / Montreal] = 2017-03-07T11:00-05:00 [America / Montreal],2017-03-07T11:00-05:00 [America / Montreal] = 2017-03-07T11:35 -05:00 [美国/蒙特利尔]]

关于java.time

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

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

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore