【问题标题】:Java Calculate time until event from current timeJava计算从当前时间到事件的时间
【发布时间】:2016-03-16 17:35:12
【问题描述】:

我正在尝试计算距离足球比赛开始还有多长时间。

这是我所知道的:

  • 我有一个事件的时间:2016-08-16T19:45:00Z
  • 我知道它的字符串格式是“yyyy-M-dd'T'h:m:s'Z'
  • 我知道时区是“CET”。

我希望能够以天为单位计算从当前时间到该日期的差异。

这是我尝试过的:

    String gameDate = "2016-03-19T19:45:00'Z'"
    DateFormat apiFormat = new SimpleDateFormat("yyyy-M-dd'T'h:m:s'Z'");
    apiFormat.setTimeZone(TimeZone.getTimeZone("CET"));
    Date dateOfGame = apiFormat.parse(gameDate);

    DateFormat currentDateFormat = new SimpleDateFormat("yyyy-M-dd'T'h:m:s'Z'");
    currentDateFormat.setTimeZone(TimeZone.getTimeZone(userTimezone));
    Date currentDate = apiFormat.parse(currentDateFormat.format(new Date()));

    long lGameDate = dateOfGame.getTime();
    long lcurrDate = currentDate.getTime();
    long difference = lGameDate - lcurrDate;
    Date timeDifference = new Date(difference);

    String daysAway = new SimpleDateFormat("d").format(timeDifference);
    Integer intDaysAway = Integer.parseInt(daysAway);

您可能想知道为什么我不只获取游戏日期 (8) 并减去当前日期 (19)。在当前日期为 29 日且比赛日期为下个月 3 日的极端情况下,我不会这样做。

【问题讨论】:

  • 不,在减法之前使用归一化是惯例。该方法看起来总体上是正确的,但不清楚您的问题或问题是什么。
  • 我想指出您的日期字符串以Z 结尾。这意味着时区是 UTC,而不是其他任何东西。

标签: java


【解决方案1】:

还没有人提供 Java 8 java.time 答案...

    String eventStr = "2016-08-16T19:45:00Z";
    DateTimeFormatter fmt = DateTimeFormatter.ISO_ZONED_DATE_TIME;
    Instant event = fmt.parse(eventStr, Instant::from);
    Instant now = Instant.now();
    Duration diff = Duration.between(now, event);
    long days = diff.toDays();
    System.out.println(days);

【讨论】:

【解决方案2】:

tl;博士

ChronoUnit.DAYS.between( 
    Instant.parse( "2016-08-16T19:45:00Z" ).atZone( ZoneId.of( "Europe/Paris" ) ), 
    Instant.parse( "2016-08-23T12:34:00Z" ).atZone( ZoneId.of( "Europe/Paris" ) ) 
);

定义“天”

有两种方法可以计算天数。您的问题不太清楚您的意图。此答案显示了两种方式的示例代码。

  • 基于日历的日期按日期
    应用预期的时区来确定开始和停止的日期。日期由区域确定,因为对于任何特定时刻,全球范围内的日期都不同,“明天”向东,而“昨天”向西,具体取决于您所在的位置。例如,在法国巴黎午夜过后几分钟,新的一天在魁北克蒙特利尔仍然是“昨天”。
  • 24 小时时间段
    如果您只考虑 24 小时时间段中的一般天数而忽略夏令时 (DST) 等异常情况,那么您将获得从开始到结束之间的总秒数时刻并将其除以 24 小时。在这种方法中,我们忽略了日历及其日期。

差异示例:从星期一深夜开始,午夜前一小时开始。周三早上午夜后一小时停止。对于 24 小时的块,总共 26 小时是一天。但是按日历日期,这将经过两天,已经触及三个日历日。

如果我们碰了三天,为什么要两天?日期时间工作通常使用半开放方法,其中开始是包含,而结束是独占

避免使用旧的日期时间类

完成这项工作的现代方法是使用 java.time 类,而不是麻烦的旧旧日期时间类(DateCalendar 等)。

Answer by dcsohl 是正确的,但可以更短。无需明确说明 DateTimeFormatter,因为输入字符串是默认使用的标准 ISO 8601 格式之一。

我知道它的字符串格式是“yyyy-M-dd'T'h:m:s'Z'”

作为 ISO 8601 标准的一部分,末尾的 ZZulu 的缩写,表示 UTC

String startInput = "2016-08-16T19:45:00Z" ;
String stopInput = "2016-08-23T12:34:00Z" ;

解析为Instant 对象。 Instant 类代表UTC 中时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

Instant start = Instant.parse( startInput );
Instant stop = Instant.parse( stopInput );

我知道时区是“CET”。

时区与解析字符串无关。但是,如果按日历日期而不是按每个普通日的 24 小时计算,则时区在计算经过的天数方面确实很重要。

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTISTCET,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

ZoneId z = ZoneId.of( "Europe/Paris" );

将我们的开始和停止时刻都调整到那个时区。

ZonedDateTime zdtStart = start.atZone( z );
ZonedDateTime zdtStop = stop.atZone( z );

java.time 框架为时间跨度提供了两个类:

  • Period 年-月-日
  • Duration 时分秒

Period 类采用一对LocalDate 对象,只有日期值,没有时间和时区。我们可以从ZonedDateTime 对象中提取LocalDate 对象。请记住,日期由区域确定。因此,我们将 UTC 值调整为 ZonedDateTime 对象至关重要。

Period p = Period.between( zdtStart.toLocalDate() , zdtStop.toLocalDate() );

您可以向 Period 询问该时间跨度的年数、月数和天数。

如果您需要总天数,请使用 ChronoUnit enum

long days = ChronoUnit.DAYS.between( zdtStart , zdtStop );

以上是基于日历日期的天数计算。

如果您想按 24 小时周期的通用块进行计数,请使用 Duration 类,如 Answer by dcsohl 所示。

Duration.between( start , stop ).toDays()

关于java.time

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

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

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

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

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

【讨论】:

    【解决方案3】:

    尝试做/使用TimeUnit:

    示例:

    final String gameDate = "2016-03-19T19:45:00Z";
    final SimpleDateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
    apiFormat.setTimeZone(TimeZone.getTimeZone("CET"));
    final Date dateOfGame = apiFormat.parse(gameDate);
    final long millis = dateOfGame.getTime() - System.currentTimeMillis();
    System.out.println(dateOfGame.getTime() - System.currentTimeMillis());
    
    final String hms = String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(millis),
                    TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
                    TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
    System.out.println(hms);
    

    这将打印输出:

    72:57:34

    从现在到比赛日期还有 72 小时 57 分 34 秒

    【讨论】:

    • 我最喜欢这种方法。谢谢!更容易
    • 我认为你应该在得到分钟之前从毫秒中减去hours * 60
    【解决方案4】:

    您可以简单地从long difference = lGameDate - lcurrDate; 获取结果,即毫秒差,然后转换为您喜欢的任何单位。

    例如,以天为单位:int days = difference/1000/3600/24;

    【讨论】:

    • 这样你就可以得到一天的错误。例如:如果您有 1 日 23:00 和 3 日 01:00,那就是 26 小时时差和 2 天时差。如果您有 1 日 20:00 和 2 日 22:00,那是相同的 26 小时差异,但只有 1 天
    • @celezar 这只是一个例子,你可以选择任何时间单位和你想要的近似值/误差。
    【解决方案5】:

    这是你需要的:

    public static void main (String[] args) throws Exception
    {
        String gameDate = "2016-03-19T19:45:00Z";
        DateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        apiFormat.setTimeZone(TimeZone.getTimeZone("CET"));
        Date dateOfGame = apiFormat.parse(gameDate);
    
        long now = new Date().getTime() / (3600000 * 24);
        long game = dateOfGame.getTime() / (3600000 * 24);
        System.out.println(now - game);
    }
    

    这将起作用,因为您从 epoch 到现在的游戏日期和现在的全天数,只需要找到差异。其他解决方案在边界情况下会出现错误。

    【讨论】:

      【解决方案6】:

      您的变量difference 包含以毫秒为单位的时间差。要将这些毫秒转换为天、小时、分钟、秒,我建议您使用 java.util.concurrent.TimeUnit 类。

      例子:

      final long durationMinutes = TimeUnit.MILLISECONDS.toMinutes(difference);
      final long durationSeconds = TimeUnit.MILLISECONDS.toSeconds(difference)
                  - TimeUnit.MINUTES.toSeconds(durationMinutes);
      final long durationMillis = difference- TimeUnit.MINUTES.toMillis(durationMinutes)
                  - TimeUnit.SECONDS.toMillis(durationSeconds);
      final String durationString = String.format("%d min, %d s, %d ms", durationMinutes, durationSeconds, durationMillis);
      

      【讨论】:

        【解决方案7】:

        首先你的日期格式是错误的。它应该是yyyy-MM-dd'T'HH:mm:ss'Z' 而不是yyyy-M-dd'T'h:m:s'Z'

        另外,您的gameDate 字符串应以Z 结尾,而不是'Z'

        您只需在当前日期和给定日期调用getTime() 函数即可轻松获取天数差异。您甚至不必格式化当前日期。

        这里是sn-p的代码:

        public static void main (String[] args) throws Exception
        {
            String gameDate = "2016-03-19T19:45:00Z";
            DateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
            apiFormat.setTimeZone(TimeZone.getTimeZone("CET"));
            Date dateOfGame = apiFormat.parse(gameDate);
        
            long difference = new Date().getTime() - dateOfGame.getTime();
            System.out.println((double)difference / (3600000d * 24d));
        }
        

        您可以根据需要对结果进行四舍五入。

        【讨论】:

          猜你喜欢
          • 2016-09-29
          • 2013-07-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多