【问题标题】:How to generate a random time between two times say 4PM and 2AM?如何在下午 4 点和凌晨 2 点之间生成一个随机时间?
【发布时间】:2023-03-24 16:47:01
【问题描述】:

我尝试过使用 -

int startSeconds = restaurant.openingTime.toSecondOfDay();
int endSeconds = restaurant.closingTime.toSecondOfDay();
LocalTime timeBetweenOpenClose = LocalTime.ofSecondOfDay(ThreadLocalRandom.current().nextInt(startSeconds, endSeconds));

但这通常会在 nextInt(origin, bounds) 中遇到错误,如果我的 openingTime 是 16:00:00 并且 closingTime 是 02:00,则原点不能小于 bounds: 00.

【问题讨论】:

  • 我是否正确假设 restaurant.openingTimerestaurant.closingTime 也是 java.time.LocalTime
  • 是的,没错。此外,下午 4 点属于今天,凌晨 2 点属于午夜后,即第二天。 @OleV.V.

标签: java random time java-time localtime


【解决方案1】:

startSeconds大于endSeconds时,可以加上一天的秒数(24*60*60)来表示第二天的秒数,得到一个随机数以一天的秒数取模后转换成LocalTime 按有效的第二个值。

int secondsInDay = (int)Duration.ofDays(1).getSeconds();
if(startSeconds > endSeconds){
  endSeconds += secondsInDay;
}
LocalTime timeBetweenOpenClose = LocalTime.ofSecondOfDay(
              ThreadLocalRandom.current().nextInt(startSeconds, endSeconds) % secondsInDay);

【讨论】:

    【解决方案2】:

    如果不应用日期和时区,我们无法知道下午 4 点到凌晨 2 点之间会经过多少时间。因此,我们将使用ZonedDateTime 来解决它。

    1. 第一步是:通过调用LocalDate#atStartOfDay获取ZonedDateTime
    ZoneId zoneId = ZoneId.systemDefault();
    LocalDate.now().atStartOfDay(zoneId);
    
    1. 接下来,使用ZonedDateTime#with 获取指定时间的ZonedDateTime
    2. 现在,您可以使用ZonedDateTime#toInstantZonedDateTime 派生Instant
    3. 通过这种方式导出开始和结束Instants 后,您可以使用ThreadLocalRandom.current().nextLong 在开始和结束Instants 的范围内生成long 值,并将获得的值用于获取所需的Instant
    4. 最后,您可以使用Instant#atZone 从这个Instant 派生一个ZonedDateTime,然后使用ZonedDateTime#toLocalTime 获得所需的时间。

    演示:

    import java.time.Instant;
    import java.time.LocalDate;
    import java.time.LocalTime;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    import java.util.concurrent.ThreadLocalRandom;
    
    public class Main {
        public static void main(String[] args) {
            // Change it as per the applicable timezone e.g. ZoneId.of("Europe/London")
            ZoneId zoneId = ZoneId.systemDefault();
            LocalDate today = LocalDate.now();
            
            ZonedDateTime zdtStart = today.atStartOfDay(zoneId)
                                          .with(LocalTime.of(16, 0));
            
            ZonedDateTime zdtEnd = today.plusDays(1)
                                        .atStartOfDay(zoneId)
                                        .with(LocalTime.of(2, 0));
            
            ZonedDateTime zdtResult = 
                    Instant.ofEpochMilli(
                                ThreadLocalRandom
                                .current()
                                .nextLong(
                                            zdtStart.toInstant().toEpochMilli(), 
                                            zdtEnd.toInstant().toEpochMilli()
                                        )
                            ).atZone(zoneId);
            
            LocalTime time = zdtResult.toLocalTime();
            System.out.println(time);
        }
    }
    

    Trail: Date Time 了解有关现代日期时间 API 的更多信息。

    ONLINE DEMO 随机打印 100 次。

    【讨论】:

    • 我建议添加一些解释,说明如果不应用日期和时区,我们如何无法知道下午 4 点到凌晨 2 点之间会经过多少时间。您的代码采取的步骤虽然正确且适当,但对于对日期时间处理有天真误解的人来说似乎无关紧要。
    • 这是正确、解释清楚和推荐的答案。从问题的第一句中的通常中,我收集到一个要求将这两种情况都考虑在内(a)餐厅在午夜之前关门,所以时间是同一天(b)餐厅关门在午夜或之后,关闭时间为第二天。假设 today 可能不正确。如果不知道日期,就不能考虑夏令时转换等,需要另一种方法。
    • @OleV.V.你提到的两个要求都是正确的。虽然我能够为案例 (a) 生成打开和关闭时间之间的随机时间,但对于案例 (b),我无法这样做。
    • @VaibhavAgrawal A ZonedDateTime 是时区中的日期和时间。如果您想要特定日期的时间并且它需要在所有时区正常工作,那么您需要这个,因为它考虑了 DST 转换和其他时间线异常。 Instant 是一个简单的时间点(顾名思义)。代码从LocalTimeZonedDateTimeInstant 转换为毫秒,以便对毫秒值进行数学运算,然后转换回InstantZonedDateTimeLocalTime
    • 这很好@OleV.V。我现在明白了..谢谢!并且 origin 小于 bounds 的问题得到了解决,因为 EpochMilli 返回了从 1970-01-01T00:00:00Z 的纪元开始的毫秒数。尽管下午 4 点和凌晨 2 点只是示例,因此可以使用 LocalTime.of(16, 0) 而不是硬编码值 restaurant.openingTime。并且可以使用 if 条件检查 ZonedDateTime zdtEnd = today.plusDays(1).atStartOfDay(zoneId).with(LocalTime.of(2, 0));,以便如果关闭时间在同一天内,即 if (openingTime.isBefore(closingTime)),则可以删除 plusDays(1)
    猜你喜欢
    • 1970-01-01
    • 2021-04-13
    • 2016-07-02
    • 1970-01-01
    • 2022-01-14
    • 2017-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多