从不同时区拍摄的日期计算时间
Calculate time from date taken with different timezone
我有一个 MySQL 数据库,它存储一个日期时间值,比方说 2020-10-11 12:00:00。 (yyyy-mm-dd hh:mm:ss 格式)
此日期的类型(在 mysql 中)是 DATETIME
当我在控制器中检索此数据时,它具有 java 7 类型“日期”。但由于我怀疑我的语言环境,它添加了一个时区 CEST。在这里,我已经发现令人困惑的是,当显示这个不应该附加时区的日期时,它实际上有……调试器说它是“2020-10-11 12:00:00 CEST”。
我的问题是日期 没有存储 CEST 时区。例如,它与 America/New_York 一起存储。编辑:我对这一行的意思是,日期是使用纽约时区从纽约存储的。所以,那里确实是 12:00:00 上午,但在马德里这里是 18:00:00 下午。我需要 18:00:00.
所以在纽约,当时有人做了一个插入。这意味着欧洲的时间不同。我需要计算欧洲时间是凌晨 12 点,而美国时间是凌晨 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 中不起作用:而且我真的不想自己计算时差并添加或减去小时数,因为在我看来这看起来非常硬编码。
任何人都可以告诉我一些关于我解释错误的想法或者我应该如何以更好的方式解决这个问题?
重要提示:我正在使用 java 7 和 grails 2.3.6。
My problem is that date was not stored with the CEST timezone. It was stored with the America/New_York one, for example.
据我所知MySQL,这是不可能的。
Calendar calendar = new GregorianCalendar()
不,不要。日历 API 是一场灾难。使用 java.time
,java 中唯一一次 API 实际有效并且没有完全损坏/设计非常糟糕。如果你不能(java 7 已经过时且不安全,你必须升级!),那就是 jsr310 backport。添加该依赖项并使用它。
让我先解释一下如何理解时间,否则这个问题的任何答案都无法正确理解:
时间差不多了!
有 3 个完全不同的概念,它们通常都被简化为 'time',但您不应该简化它们 - 这 3 个不同的概念并不真正相关 如果你把一个与另一个混淆,问题总是会发生。除非你刻意这样做,否则你无法在这 3 个概念之间转换!
- “太阳耀斑时间”:这些时间将时间描述为一个普遍的全球概念,表明某事已发生或将要发生。 “在 X 处观察到太阳耀斑”是 'solarflares' 时间。最好的存储方式是自纪元以来的毫秒数。
- “预约时间”:这些描述了某个特定地点过去或将要发生的特定时间,但以全球可理解的方式陈述。 “我们下周二 5 点召开 Zoom 会议”就是其中之一。它实际上并不恒定,因为语言环境可以决定采用新的时区或例如为夏令时移动 'switch date'。例如,如果您在 'november 5th, at 17:00, 2021' 预约了牙医,并且您想知道距离预约开始还有多少小时,那么该值不应仅仅因为您飞往另一个时区并且正在查看这个数字来自那里。但是,如果您预约的国家/地区决定废除夏令时,则 应该 更改。这就是这个和 'solarflares' 之间的区别。由于政治决定,这个仍然可以改变。
- "wake-up-alarm 时间":这些描述了一个更 mutable 的概念:人类指代时间的某种方式并不指代任何特定的瞬间,甚至试图指代。想一想“我喜欢在 8 点起床”,因此,如果您正在跨时区旅行,那么到下一次闹钟响起的时间会不断变化。
现在,回答你的问题:
I have a MySQL database which is storing a datetime value, let's say 2020-10-11 12:00:00. (yyyy-mm-dd hh:mm:ss format)
没那么快。该列有什么 exact 类型?您的 CREATE TABLE
声明中包含什么?这里要弄清楚的关键是实际存储在磁盘上的是什么?是耀斑、约会还是wakeup-alarm?有 DATE
、DATETIME
和 TIMESTAMP
,多年来,mysql 显着改变了这些东西的存储方式。
我相信,假设您使用的是现代存储方式(因此,新 mysql 并且没有设置来明确模拟旧行为),例如a DATETIME
在引擎盖下存储符号、年、日、小时、分钟和秒,这意味着它是 wakeup alarm 样式:这里没有时区信息,因此, 实际的时间根本没有设置,取决于谁在问。
与存储为 UTC 纪元秒的 TIMEZONE 形成对比,因此它是太阳耀斑时间,并且根本不包括任何时区。您必须单独存储它。据我所知,最有用的3个时间表示(预约时间)是not a thing in mysql。那很烦人; mysql 趋向于,所以也许是正常的。
在 java 中,所有 3 个概念都存在:
太阳耀斑时间是 java.time.Instant
。 java.util.Date
、java.sql.Timestamp
、System.currentTimeMillis()
也是耀斑时间。 'Date' 是 solarflares 时间戳是疯狂的,但是 API 被替换是有原因的。
预约时间是java.time.ZonedDateTime
wakeup-alarm时间为java.time.LocalDateTime
.
When I retrieve this data in my controller, it has the java 7 type "Date".
没错。所以,太阳耀斑时间。
关键是:
如果MySQL中存储的时间类型与java侧的时间类型不匹配,就会发生疼痛.
听起来你确实有 wakeup-alarm 时间在磁盘上,它最终在 java 一侧作为 solarflares 时间。这意味着 somebody 涉及时区转换。可能发生在 mysql 内部,可能发生在 mysql 和 jdbc 驱动程序之间的飞行中(mysql 把它 'on the wire' 转换),或者 jdbc 驱动程序匹配 java.sql.Timestamp.
最好的解决方案是 根本不转换,唯一真正的方法是将 mysql table def 更改为匹配 java,因此,使 CREATE TABLE (foo TIMESTAMP)
,因为 TIMESTAMP 也是太阳耀斑时间,或者,在 JDBC 级别使用,而不是:
someResultSet.getTimestamp(col);
作为那个 returns 太阳耀斑时间,但是:
someResultSet.getObject(col, LocalDateTime.class);
问题是:您的 JDBC 驱动程序可能不支持此功能。如果不是,那是一个蹩脚的 JDBC 驱动程序,但有时会发生这种情况。
这还是更好的计划 - 计划 A。所以除非别无选择,否则不要继续执行糟糕的计划 B。
方案二:
承认发生了转换,这是非常烦人且容易出错的。因此,请确保您仔细而明确地管理它:确保设置了正确的 SET 调用,以便 mysql 对我们所处时区的感觉相匹配。如果您确实需要预约时间,请考虑将时区存储为 table 中的一列。等等。
感谢@rzwitserloot,我找到了解决方案。
首先我将从数据库中获取数据。我将通过将 driver / mysql 转换为 LocalDateTime 来摆脱任何时区。然后,我将使用在数据库中存储数据时使用的时区创建一个新的 ZonedDateTime。
一旦我有了 ZonedDateTime,就可以使用我当前的时区转换它了。我将在适当的时间获得一个新的 ZonedDateTime object。
然后我再添加几行将其转换回我的主要“日期”class:
我已经按照建议使用了 ThreeTen backport。
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 由于我的 driver
时区:“America/New_York”
desiredDate: "2020-10-11 19:00:00" // CEST 耶!
我有一个 MySQL 数据库,它存储一个日期时间值,比方说 2020-10-11 12:00:00。 (yyyy-mm-dd hh:mm:ss 格式)
此日期的类型(在 mysql 中)是 DATETIME
当我在控制器中检索此数据时,它具有 java 7 类型“日期”。但由于我怀疑我的语言环境,它添加了一个时区 CEST。在这里,我已经发现令人困惑的是,当显示这个不应该附加时区的日期时,它实际上有……调试器说它是“2020-10-11 12:00:00 CEST”。
我的问题是日期 没有存储 CEST 时区。例如,它与 America/New_York 一起存储。编辑:我对这一行的意思是,日期是使用纽约时区从纽约存储的。所以,那里确实是 12:00:00 上午,但在马德里这里是 18:00:00 下午。我需要 18:00:00.
所以在纽约,当时有人做了一个插入。这意味着欧洲的时间不同。我需要计算欧洲时间是凌晨 12 点,而美国时间是凌晨 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 中不起作用:而且我真的不想自己计算时差并添加或减去小时数,因为在我看来这看起来非常硬编码。
任何人都可以告诉我一些关于我解释错误的想法或者我应该如何以更好的方式解决这个问题?
重要提示:我正在使用 java 7 和 grails 2.3.6。
My problem is that date was not stored with the CEST timezone. It was stored with the America/New_York one, for example.
据我所知MySQL,这是不可能的。
Calendar calendar = new GregorianCalendar()
不,不要。日历 API 是一场灾难。使用 java.time
,java 中唯一一次 API 实际有效并且没有完全损坏/设计非常糟糕。如果你不能(java 7 已经过时且不安全,你必须升级!),那就是 jsr310 backport。添加该依赖项并使用它。
让我先解释一下如何理解时间,否则这个问题的任何答案都无法正确理解:
时间差不多了!
有 3 个完全不同的概念,它们通常都被简化为 'time',但您不应该简化它们 - 这 3 个不同的概念并不真正相关 如果你把一个与另一个混淆,问题总是会发生。除非你刻意这样做,否则你无法在这 3 个概念之间转换!
- “太阳耀斑时间”:这些时间将时间描述为一个普遍的全球概念,表明某事已发生或将要发生。 “在 X 处观察到太阳耀斑”是 'solarflares' 时间。最好的存储方式是自纪元以来的毫秒数。
- “预约时间”:这些描述了某个特定地点过去或将要发生的特定时间,但以全球可理解的方式陈述。 “我们下周二 5 点召开 Zoom 会议”就是其中之一。它实际上并不恒定,因为语言环境可以决定采用新的时区或例如为夏令时移动 'switch date'。例如,如果您在 'november 5th, at 17:00, 2021' 预约了牙医,并且您想知道距离预约开始还有多少小时,那么该值不应仅仅因为您飞往另一个时区并且正在查看这个数字来自那里。但是,如果您预约的国家/地区决定废除夏令时,则 应该 更改。这就是这个和 'solarflares' 之间的区别。由于政治决定,这个仍然可以改变。
- "wake-up-alarm 时间":这些描述了一个更 mutable 的概念:人类指代时间的某种方式并不指代任何特定的瞬间,甚至试图指代。想一想“我喜欢在 8 点起床”,因此,如果您正在跨时区旅行,那么到下一次闹钟响起的时间会不断变化。
现在,回答你的问题:
I have a MySQL database which is storing a datetime value, let's say 2020-10-11 12:00:00. (yyyy-mm-dd hh:mm:ss format)
没那么快。该列有什么 exact 类型?您的 CREATE TABLE
声明中包含什么?这里要弄清楚的关键是实际存储在磁盘上的是什么?是耀斑、约会还是wakeup-alarm?有 DATE
、DATETIME
和 TIMESTAMP
,多年来,mysql 显着改变了这些东西的存储方式。
我相信,假设您使用的是现代存储方式(因此,新 mysql 并且没有设置来明确模拟旧行为),例如a DATETIME
在引擎盖下存储符号、年、日、小时、分钟和秒,这意味着它是 wakeup alarm 样式:这里没有时区信息,因此, 实际的时间根本没有设置,取决于谁在问。
与存储为 UTC 纪元秒的 TIMEZONE 形成对比,因此它是太阳耀斑时间,并且根本不包括任何时区。您必须单独存储它。据我所知,最有用的3个时间表示(预约时间)是not a thing in mysql。那很烦人; mysql 趋向于,所以也许是正常的。
在 java 中,所有 3 个概念都存在:
太阳耀斑时间是
java.time.Instant
。java.util.Date
、java.sql.Timestamp
、System.currentTimeMillis()
也是耀斑时间。 'Date' 是 solarflares 时间戳是疯狂的,但是 API 被替换是有原因的。预约时间是
java.time.ZonedDateTime
wakeup-alarm时间为
java.time.LocalDateTime
.
When I retrieve this data in my controller, it has the java 7 type "Date".
没错。所以,太阳耀斑时间。
关键是:
如果MySQL中存储的时间类型与java侧的时间类型不匹配,就会发生疼痛.
听起来你确实有 wakeup-alarm 时间在磁盘上,它最终在 java 一侧作为 solarflares 时间。这意味着 somebody 涉及时区转换。可能发生在 mysql 内部,可能发生在 mysql 和 jdbc 驱动程序之间的飞行中(mysql 把它 'on the wire' 转换),或者 jdbc 驱动程序匹配 java.sql.Timestamp.
最好的解决方案是 根本不转换,唯一真正的方法是将 mysql table def 更改为匹配 java,因此,使 CREATE TABLE (foo TIMESTAMP)
,因为 TIMESTAMP 也是太阳耀斑时间,或者,在 JDBC 级别使用,而不是:
someResultSet.getTimestamp(col);
作为那个 returns 太阳耀斑时间,但是:
someResultSet.getObject(col, LocalDateTime.class);
问题是:您的 JDBC 驱动程序可能不支持此功能。如果不是,那是一个蹩脚的 JDBC 驱动程序,但有时会发生这种情况。
这还是更好的计划 - 计划 A。所以除非别无选择,否则不要继续执行糟糕的计划 B。
方案二:
承认发生了转换,这是非常烦人且容易出错的。因此,请确保您仔细而明确地管理它:确保设置了正确的 SET 调用,以便 mysql 对我们所处时区的感觉相匹配。如果您确实需要预约时间,请考虑将时区存储为 table 中的一列。等等。
感谢@rzwitserloot,我找到了解决方案。
首先我将从数据库中获取数据。我将通过将 driver / mysql 转换为 LocalDateTime 来摆脱任何时区。然后,我将使用在数据库中存储数据时使用的时区创建一个新的 ZonedDateTime。
一旦我有了 ZonedDateTime,就可以使用我当前的时区转换它了。我将在适当的时间获得一个新的 ZonedDateTime object。
然后我再添加几行将其转换回我的主要“日期”class:
我已经按照建议使用了 ThreeTen backport。
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 由于我的 driver
时区:“America/New_York”
desiredDate: "2020-10-11 19:00:00" // CEST 耶!