从不同时区的日期开始计算时间

fkvaft9z  于 2021-07-06  发布在  Java
关注(0)|答案(2)|浏览(527)

我有一个mysql数据库,它存储一个datetime值,比如2020-10-11 12:00:00(yyyy-mm-dd hh:mm:ss格式)
这个日期的类型(在mysql中)是datetime
当我在我的控制器中检索这个数据时,它的java7类型是“date”。但它增加了一个时区,因为我怀疑我的地区。在这里我已经发现混淆,当显示这个日期,这是不应该有时区附加它实际上有。。。调试器说是“2020-10-11 12:00:00 cest”。
我的问题是日期没有存储在cest时区。例如,它与美国/纽约的一个存储在一起。编辑:我这行的意思是,日期是从纽约用纽约的时区存储的。所以,那里确实是12:00:00,但在马德里是18:00:00。我需要18:00。
所以在纽约,当时有人做了一个插页。这意味着欧洲的时代不同了。我需要计算一下在欧洲的时间是什么时候,在美国是12点。但是我的电脑在我取回它的时候一直把日期设置为cest,所以我所有的解析尝试都失败了。。。这是我的主意:

Date testingDate // This date is initialized fetching the "2020-10-11 12:00:00" from mySql
Calendar calendar = new GregorianCalendar()
calendar.setTime(testingDate)
calendar.setTimeZone(TimeZone.getTimeZone("America/New_York")

SimpleDateFormat localDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
TimeZone localTimeZone = TimeZone.getTimeZone("Europe/Madrid")
localDateFormatter.setTimeZone(localTimeZone)

String localStringDate = localDateFormatter.format(calendar.getTime())
Date newDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(localStringDate)

在这里我的想法是:我创建一个全新的日历,把它的时间,我在美国,我也说,嘿,这个日历应该有美国时区。所以当我使用欧洲的格式化程序得到时间时,它应该加上相应的时间。这在我的头脑中是很有意义的,但它在代码d中不起作用:我真的不想自己计算时差,加上或减去小时数,因为在我看来,这看起来非常硬编码。
有谁能告诉我一些我理解错误的地方,或者我应该如何更好地解决这个问题?
重要提示:我使用的是Java7和Grails2.3.6。

qnyhuwrf

qnyhuwrf1#

我的问题是日期没有存储在cest时区。例如,它与美国/纽约的一个存储在一起。
据我所知,这是不可能的。 Calendar calendar = new GregorianCalendar() 不,不要。日历api是一场灾难。使用 java.time ,这是java中唯一一个真正有效的时间api,并且没有完全损坏/设计得非常糟糕。如果不能(Java7已经非常过时和不安全,您必须升级!),这是jsr310后端口。添加依赖项并使用它。
让我先解释一下如何理解时间,否则,这个问题的任何答案都无法正确理解:

是时候了!

有3个完全不同的概念,它们通常都被简化为“时间”的意思,但你不应该简化它们——这3个不同的概念其实并不相关,如果你把它们混淆了,问题总会出现。你不能在这三个概念之间转换,除非你故意这样做!
“太阳光时间”:这些描述的时刻作为一个普遍的全球概念,某件事发生或将要发生的时间太阳耀斑是在“x”时观测到的,这是一个“太阳耀斑”时间。最好的存储方式是millis-since-epoch。
“预约时间”:这些描述了一个特定的时刻,因为它是或将是在某个地方,但在全球范围内可以理解的方式我们下周二5点有一个zoom会议就是其中之一。它实际上并不是常数,因为地区可以决定采用新的时区,或者例如移动夏时制的“切换日期”。例如,如果您在“2021年11月5日17:00”与牙医预约,并且您想知道距预约开始还有多少小时,那么该值不应仅因为您飞往另一个时区并从那里查看该数字而更改。但是,如果您预约的国家决定取消夏令时,则应该有所改变。这就是这个和“日光灯”的区别。这一点仍然可以改变由于政治决定。
“唤醒警报时间”:这些描述了一个更易变的概念:人类指代时间的某种方式,它不指任何特定的瞬间,甚至试图指代。想想“我喜欢在8点起床”,因此,如果你要穿越时区旅行,那么下次闹钟响之前的时间是不断变化的。
现在,回答你的问题:
我有一个mysql数据库,它存储一个datetime值,比如2020-10-11 12:00:00(yyyy-mm-dd hh:mm:ss格式)
别那么快。那一栏的确切类型是什么?你的房间里有什么 CREATE TABLE 声明?这里要弄清楚的关键是磁盘上实际存储了什么?是日光浴、约会还是闹钟?有 DATE , DATETIME 以及 TIMESTAMP 多年来,mysql已经显著改变了这些东西的存储方式。
我相信,假设您使用的是现代的takes on storage(因此,新的mysql,没有显式模拟旧行为的设置),例如 DATETIME 商店标志,年,日,时,分,秒在引擎盖下,这意味着它是唤醒警报样式:没有时区信息在这,因此,实际的时刻在时间上根本没有设置,并取决于谁是问。
与存储为utc epoch秒的时区不同,它是solarflares时间,并且它根本不包括任何时区。你得分开存放。据我所知,3种时间表示法(约会时间)中最有用的在mysql中并不是什么东西。那很烦人;mysql往往是,所以可能是标准的课程。
在java中,所有3个概念都存在:
阳光灿烂的时光 java.time.Instant . java.util.Date , java.sql.Timestamp , System.currentTimeMillis() 也是阳光灿烂的时光。那个“日期”是solarflares的时间戳是疯狂的,但还有一个原因就是api被替换了。
预约时间是 java.time.ZonedDateTime 唤醒警报时间为 java.time.LocalDateTime .
当我在我的控制器中检索这个数据时,它的java7类型是“date”。
正确的。所以,阳光灿烂的时光。
关键是:
如果mysql中存储的时间类型与java端的时间类型不匹配,就会发生痛苦。
这听起来确实像你在磁盘上有唤醒警报时间,它最终在java端作为SolarFlaresTime。这意味着有人涉及时区转换。可能发生在mysql内部,可能发生在mysql和jdbc驱动程序之间的飞行中(mysql将其“在线”转换),或者jdbc驱动程序这样做是为了匹配java.sql.timestamp。
最好的解决方案是根本不进行转换,唯一真正的方法是更改mysql表def以匹配java,所以,要做到这一点 CREATE TABLE (foo TIMESTAMP) ,因为timestamp也是solarflares time,或者在jdbc级别使用,而不是:

someResultSet.getTimestamp(col);

因为它返回太阳光时间,但是:

someResultSet.getObject(col, LocalDateTime.class);

问题是:您的jdbc驱动程序可能不支持这一点。如果没有,它就是一个蹩脚的jdbc驱动程序,但这种情况有时会发生。
这仍然是最好的计划——a计划。因此,除非没有其他办法,否则不要继续执行糟糕的b计划。
方案b:
承认转换发生了,这是非常烦人和容易出错。因此,请确保您仔细而明确地管理它:确保设置了正确的set调用,以便mysql能够识别我们所处的时区。如果您真的需要预约时间,可以考虑添加将时区存储为表中的一列。等等。

of1yzvn4

of1yzvn42#

多亏了@rzwitserloot,我才找到了解决办法。
首先我要从数据库中获取数据。我将通过将driver/mysql添加的时区转换为localdatetime来消除它。然后,我将使用在数据库中存储数据时使用的时区创建一个新的zoneddatetime。
一旦我有了一个ZoneDateTime,就是时候用我当前的时区来转换它了。我将在适当的时间获取一个新的ZoneDateTime对象。
然后我再添加几行,将其转换回我的主“日期”类:
我已经按照建议使用了“三个十”后端口。

Date dateMySQL //Initialized with the date from mysql
Calendar calendar = new GregorianCalendar()
calendar.setTime(dateMySQL)

org.threeten.bp.LocalDateTime localDateTime = org.threeten.bp.LocalDateTime.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH)+1,
            calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE),
            calendar.get(Calendar.SECOND))

String timezone //Initialized with the timezone from mysql (Ex: "America/New_York") 
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of(timezone))
ZonedDateTime utcDate = zonedDateTime.withZoneSameInstant(ZoneId.of("Europe/Madrid"))
calendar.setTimeInMillis(utcDate.toInstant().toEpochMilli())
Date desiredDate = calendar.time

datemysql:“2020-10-11 10:00:00”//cest由于我的司机
时区:“美国/纽约”
期望日期:“2020-10-11 19:00:00”//cest yay!

相关问题