我正在使用TimeZone,需要帮助。
背景:我有2个办公室,1个在巴黎,1个在纽约。我需要为两者执行一些任务。我唯一的服务器位于巴黎。任务必须在当地时间上午 9:00 执行。
所以,在巴黎时间9点,我的流程将运行,并为巴黎办公室做所有事情。
巴黎时间15:00(或下午3:00),流程将再次运行以完成纽约任务,因为当巴黎下午3:00时,纽约上午9:00。
我尝试了以下方法,但成功率有所降低:
Entity e1 = Entity.getEntityInstance("Paris", TimeZone.getTimeZone("Europe/Paris"));
Entity e2 = Entity.getEntityInstance("NewYork", TimeZone.getTimeZone("America/New_York"));
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyyy");
sdf.setTimeZone(e1.getTimeZone());
sdf.format(now);
System.out.printf("Date à %s = %s", e1.getName(), sdf.getCalendar());
sdf.setTimeZone(e2.getTimeZone());
sdf.format(now);
System.out.printf("Date à %s = %s", e2.getName(), sdf.getCalendar());
我得到的结果是这样的:
Date à Paris = java.util.GregorianCalendar[time=1563224081926,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2019,MONTH=6,WEEK_OF_YEAR=29,WEEK_OF_MONTH=3,DAY_OF_MONTH=15,DAY_OF_YEAR=196,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=10,**HOUR_OF_DAY=22,MINUTE=54,SECOND=41**,MILLISECOND=926,ZONE_OFFSET=3600000,DST_OFFSET=3600000]
Date à NewYork = java.util.GregorianCalendar[time=1563224081926,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/New_York",offset=-18000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/New_York,offset=-18000000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2019,MONTH=6,WEEK_OF_YEAR=29,WEEK_OF_MONTH=3,DAY_OF_MONTH=15,DAY_OF_YEAR=196,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=4,**HOUR_OF_DAY=16,MINUTE=54,SECOND=41**,MILLISECOND=926,ZONE_OFFSET=-18000000,DST_OFFSET=3600000]
我可以有效地看到HOUR_OF_DAY的差异,但是我应该做些什么来使用非弃用对象(如DateTime
(Joda time)或其他任何东西)获得相同的结果呢?
编辑
我尝试了我在这里发现的:
ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );
…
Instant instant = zdt.toInstant();
我认为这可以解决我的问题,因为实际上,我想要的是获得两个< code>Date对象,它们代表相同的瞬间,但是在两个不同的地方。
我所做的是:
ZonedDateTime zdt1 = ZonedDateTime.now( ZoneId.of(e1.getTimeZone().getID()));
ZonedDateTime zdt2 = ZonedDateTime.now( ZoneId.of(e2.getTimeZone().getID()));
System.out.println("ZDT : " + zdt1);
System.out.println("ZDT : " + zdt2);
结果是引人入胜的:
ZDT : 2019-07-16T01:23:29.344+02:00[Europe/Paris]
ZDT : 2019-07-15T19:23:29.346-04:00[America/New_York]
但当我尝试将它们转换为Instant
,然后转换为Date
时,结果完全相同:
Instant i1 = zdt1.toInstant();
Instant i2 = zdt2.toInstant();
System.out.println(i1);
System.out.println(i2);
Date dd1 = Date.from(i1);
Date dd2 = Date.from(i2);
System.out.println(dd1);
System.out.println(dd2);
在控制台中:
Instant 1 = 2019-07-15T23:23:29.344Z
Instant 2 = 2019-07-15T23:23:29.346Z
Date 1 = Tue Jul 16 01:23:29 CEST 2019
Date 2 = Tue Jul 16 01:23:29 CEST 2019
我真的不明白 2 个不同的 ZonedDateTime
s 如何产生相同的 Instant
s 和 Date
s...
您正在使用糟糕的日期时间类,这些类几年前被JSR 310中定义的现代java.time类所取代。
服务器的默认时区(和区域设置)不应影响代码。作为程序员,那里的设置是您无法控制的。因此,与其依赖这些默认值,不如始终通过将可选参数传递给各种方法来指定所需/预期的时区(和区域设置)。
提示:最佳做法是将服务器保留为 UTC 作为其默认时区。事实上,你的大部分业务逻辑、日志记录、数据交换和数据存储都应该用 UTC 完成。
每个位置都有一天的目标时间。
LocalTime targetTimeOfDayParis = LocalTime.of( 9 , 0 ) ; // 09:00.
LocalTime targetTimeOfDayNewYork = LocalTime.of( 9 , 0 ) ; // 09:00.
获取UTC时间的当前时刻。
Instant now = Instant.now() ; // No need for time zone here. `Instant` is always in UTC, by definition.
今天是什么时候?首先定义我们的时区。
ZoneId zParis = ZoneId.of( "Europe/Paris" ) ;
ZoneId zNewYork = ZoneId.of( "America/New_York" ) ;
然后从Instant
中的UTC调整到与特定地区(时区)的人们使用的挂钟时间相同的时刻。该调整后的时刻由ZonedDateTime
类表示。
请注意,这不会改变这一刻。我们在两者的时间线上都有相同的点。只是挂钟时间不同。就像两个长途电话的人可以同时抬头看各自墙上的时钟,同时看到不同的时间(和日期!)。
ZonedDateTime zdtParis = now.atZone( zParis ) ;
ZonedDateTime zdtNewYork = now.atZone( zNewYork ) ;
将一天的时间更改为我们的目标。ZonedDateTime::with
方法将生成一个新的ZonedDateTime
对象,该对象的值基于原始值,您传递的值除外
这是我们在巴黎和纽约问题上分歧的关键部分。巴黎的上午九点与纽约的上午9点截然不同,相隔几个小时。
ZonedDateTime zdtParisTarget = zdtParis.with( targetTimeOfDayParis ) ;
ZonedDateTime zdtNewYorkTarget = zdtNewYork.with( targetTimeOfDayNewYork ) ;
我们错过了上午9点吗?
boolean pastParis = zdtParisTarget.isBefore( zdtParis ) ;
boolean pastNewYork = zdtNewYorkTarget.isBefore( zdtNewYork ) ;
如果过去,则添加一天。
if( pastParis ) {
zdtParisTarget = zdtParisTarget.plusDays( 1 ) ;
}
if( pastNewYork ) {
zdtNewYorkTarget = zdtNewYorkTarget.plusDays( 1 ) ;
}
使用Duration
类计算运行时间。
Duration durationParis = Duration.between( now , zdtParisTarget.toInstant() ) ;
Duration durationNewYork = Duration.between( now , zdtNewYorkTarget.toInstant() ) ;
仔细检查持续时间是在未来,而不是过去。
if( durationParis.isNegative() ) { … handle error … }
if( durationNewYork.isNegative() ) { … handle error … }
0同上。
if( durationParis.isZero() ) { … handle error … }
if( durationNewYork.isZero() ) { … handle error … }
在实际工作中,我会检查持续时间是否非常小。如果简短到下面的代码可能需要比时间跨度更长的时间,请添加一些任意数量的时间。我将把它作为读者的练习。
准备要完成的工作。
Runnable runnableParis = () -> taskParis.work() ;
Runnable runnableNewYork = () -> taskNewYork.work() ;
或者,您可以编写一个实用程序,该实用程序接受ZoneId
参数,查找要完成的工作,并返回可运行
。
安排要完成的工作。请参阅有关Exec的Oracle教程框架。该框架极大地简化了在后台线程上完成的调度工作。
我们安排任务在经过一定的分钟、秒或任何此类粒度后运行。我们使用<code>TimeUnit</code>枚举指定一个数字和粒度。我们可以从上面计算的持续时间
中提取数字。
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 1 );
ScheduledFuture<?> futureParis = scheduler.schedule( runnableParis , durationParis.toSeconds() , TimeUnit.SECONDS ) ;
ScheduledFuture<?> futureNewYork = scheduler.schedule( runnableNewYork , durationNewYork.toSeconds() , TimeUnit.SECONDS ) ;
future对象允许您检查进度状态。你可能根本不需要它。
重要事项:当不再需要时,例如应用程序即将结束时,请确保正常关闭ScheduledExecutorService
。
注意:我还没有尝试过这些代码。但这会让你朝着正确的方向前进。
注意重复。无需在所有这些变量中对城市名称进行硬编码。您可以在将 ZoneId
作为参数的方法中编写一次此逻辑,如果有可能,也可以编写一天中的时间因城市而异。然后为任意数量的区域调用该方法。
List< ZoneId > zones =
List.of(
ZoneId.of( "Europe/Paris" ) ,
ZoneId.of( "America/New_York" ) ,
ZoneId.of( "Asia/Kolkata" )
)
;
for( ZoneId z : zones ) {
workManager.scheduleForZone( z ) ;
}
问题内容: 我一直想知道,在最佳实践中,是否允许不使用on方法,而是对from的结果进行空检查。 我的理由是,重复两次查找值似乎是多余的:首先对进行查找,然后对进行查找。 另一方面,可能是大多数标准实现都缓存了最后一次查找,或者编译器可以通过其他方式消除冗余,并且对于代码的可读性而言,最好保留该部分。 非常感谢您的评论。 问题答案: 一些Map实现被允许具有空值,例如HashMap,在这种情况下,
问题内容: 因此,我在Google上搜索了几分钟如何使用计时器,并在此处找到了一些有用的线程。但是,当我想使用建议的代码时,Eclipse总是向我显示错误。 这是我现在要使用的代码。Eclipse在最后一行下划线,并在它们建议“删除参数以匹配’Timer()’”时加下划线。此外,它强调了start()并希望将其强制转换。:S 有人可以帮我吗?我已经安装了最新的Java版本^^ 多谢。 问题答案:
我刚开始使用线程,一调用wait方法就会得到一个IllegalMonitorStateException,有人能帮我吗
我有一个日期存储在DB字符串格式ddMMyyyy和hh: mm和时区。我想根据这些信息创建一个即时,但我不知道如何做到这一点。 像这样的东西
我有一个Employee类,它有如下3个字段。 为此,我希望根据员工姓名(empName)排序,如果多个员工的姓名相同,则根据员工id(empId)排序。 为此,我编写了一个自定义比较器,使用java.util.比较器如下所示。 我已经创建了8个Employee对象并添加到ArrayList中,如下所示。 并使用上述比较器对列表进行如下排序。 它工作得非常好。但这可以使用类似的方法来完成,如下所示
问题内容: StringBuilder在Java中,通常最好使用a 进行字符串连接。总是这样吗? 我的意思是:创建对象,调用方法并最终已经很小,然后将现有字符串与+运算符连接成两个字符串的开销是小的,还是建议仅用于两个以上的字符串? 如果有这样的阈值,它取决于什么(也许是字符串长度,但以哪种方式)? 最后,你是否会以+级联的可读性和简洁性为代价,以较小的情况(例如两个,三个或四个字符串)的性能为代