【问题标题】:Java 8 zoned dates conversionJava 8 分区日期转换
【发布时间】:2016-10-20 11:27:31
【问题描述】:

我有一个关于 java 中区域的问题。 当用户可以设置时区和一些时间表时,我有一个用户案例。例如,在时区 America/Los_Angeles 的周一和周日晚上 11 点运行任务。 如果我的服务器时区是 UTC+0:00,我会遇到如何正确检测星期几和新时间的问题(大约是周二和周一​​早上 6 点)。所以我的问题是如何根据服务器时区和用户时区正确运行用户任务。

更新

我有一个 cron 表达式,我在其中设置小时、分钟和星期几。 当用户创建新任务时,他可以设置自定义时区(例如在星期一晚上 11 点使用自定义时区 UTC-7:00 运行任务) 因此,如果我的理解是正确的,我需要将他的时间设置(晚上 11 点)转换为相应的服务器时间。因此,如果我的服务器时区是 UTC+3:00,我需要将晚上 11 点转换为上午 9 点,并且在我的服务器上不是星期一(而是星期二)。然后,如果我将在周二上午 9 点运行 cron,它将看起来像任务在周一晚上 11 点运行。我的方法正确吗?我希望你能理解我的问题。提前致谢。

【问题讨论】:

  • 你能告诉我们问题是什么吗?你能告诉我们你的代码吗?系统时钟始终为​​ UTC,因此没有理由一定会出现问题。
  • @PeterLawrey 我编辑了我的问题
  • 您必须一起转换时间和星期几。请注意夏令时,您不能只计算一次并假设这总是正确的。您必须计算下一次并添加要在该时间运行的任务。例如如果他们在纽约选择凌晨 1 点,这将是一年中不同时间的 21:00 UTC-4 和 20:00 UTC-5。

标签: java date


【解决方案1】:

简短的回答:自纪元以来的毫秒数。

长答案:
首先,了解计算机时间的工作原理非常重要。我会推荐this article.,但要点是,一切都是从 1970 年 1 月 1 日开始计算的。其次,我想一个例子会告诉你超过 1000 个单词:

ZonedDateTime dateTime = ZonedDateTime.parse("2016-10-20T11:34:57+02:00[Europe/Zurich]"); // UTC + 2
long millisEurope = dateTime.toInstant().toEpochMilli();
System.out.println(millisEurope); // 1476956097000

dateTime = dateTime.withZoneSameLocal(ZoneId.of("America/Los_Angeles")); // UTC - 7
long millisAmerica = dateTime.toInstant().toEpochMilli();
System.out.println(millisAmerica); // 1476988497000

// The difference between UTC + 2 and UTC - 7  == -9
System.out.println((millisEurope - millisAmerica) / 1000 / 60 / 60);

ZonedDateTime 将始终使用 UTC (+ 0) 加上定义的时区。

编辑(更新后):
是的,如果您获得了用户设置的时间和时区,则必须将其转换为您自己的时区才能在同一时间点执行。

【讨论】:

    【解决方案2】:

    您的服务器操作系统和 JVM 的时区设置应该与您的编程无关。两者都可以随时更改,运行时,所以不要依赖它。 始终在各种日期时间方法的可选参数中指定所需/预期的时区

    请注意,您不能提前很长时间安排对时区敏感的时刻。政客因经常更改时区定义而臭名昭著,有时几乎没有提前通知。

    如果您想在用户时区的上下文中为下周一上午 11 点设置闹钟,首先您需要用户的时区。您可能能够检测到默认时区,但最终唯一可靠的方法是询问用户他们想要/预期的时区。

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

    将闹钟的星期几保存为 DayOfWeek 枚举对象。

    DayOfWeek alarmDow = DayOfWeek.MONDAY ;
    

    将闹钟的时间保留为LocalTime

    LocalTime alarmTimeOfDay = LocalTime.parse( "11:00:00" );
    

    此类缺少日期,并且缺少任何与 UTC 或时区的偏移。因此,在您适应时区之前,它没有任何意义。

    将闹钟的时区保留为ZoneId

    ZoneId z = ZoneId.of( "America/Montreal" );
    

    有了这些部件,您就可以安排闹钟。

    Instant 的形式获取当前时刻。 Instant 类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

    Instant now = Instant.now();
    

    应用所需的时区以获得ZonedDateTime

    ZonedDateTime zdtNow = instant.atZone( z );
    

    要确定警报的未来时刻,请使用TemporalAdjuster 接口来操作日期时间值。 TemporalAdjusters 类(注意复数 s)提供了实现。首先提取一个仅限日期的值,因为我们将把闹钟的LocalTime 指定为时间。

    LocalDate today = zdtNow.toLocalDate();
    LocalDate dateOfNextOrSameDow = today.with( TemporalAdjusters.withNextOrSame( alarmDow ) );
    

    应用所需的时间。

    ZonedDateTime zdtAlarm = ZonedDateTime.of( dateOfNextOrSameDow , alarmTimeOfDay , z ) ;
    

    这个特定的闹钟日期时间可能已经过了今天早些时候。所以测试。如果是这样,请添加一周以获得所需的星期几的下一次出现。

    if( zdtAlarm.isBefore( zdtNow ) ) {  // If already passed…
        zdtAlarm = zdtAlarm.plusWeeks( 1 );  // …go to next day-of-week occurrence.
    }
    

    【讨论】:

      猜你喜欢
      • 2020-09-24
      • 2019-10-24
      • 2016-03-13
      • 2016-01-10
      • 1970-01-01
      • 2014-11-11
      • 2021-07-22
      • 1970-01-01
      相关资源
      最近更新 更多