Java - 将时间戳转换为日期并返回时间戳会更改日期
Java - convert timestamp to Date and back to timestamp changes the date
我正在从当前时间创建一个字符串,我想再次将它转换为时间戳,但问题是,它在转换时减去了 2 小时。
这是我正在执行的步骤 -
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern("uuuu-MM-dd")).appendLiteral(" ")
.append(DateTimeFormatter.ofPattern("HH:mm:ss")).parseLenient();
long ts = Clock.systemUTC().millis();
System.out.println(ts);
Instant instant = Instant.ofEpochMilli(ts);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
long timestamp = simpleDateFormat.parse(str).getTime();
System.out.println(timestamp);
} catch (ParseException e) {
e.printStackTrace();
}
输出-
1639065502667
1639058302000
(2021-12-09 15:58:22
2021-12-09 13:58:22)
为什么两个小时相差这么大?
我怎样才能解析它以使输出相等?
让我猜猜,你的时区是 UTC+2
?
simpleDateFormat.parse(str)
假设您的日期在当前系统时区,但它是 UTC。
dateTimeFomatter
生成器使用没有毫秒和时区的格式。
这就是 str
值不包含有关它们的信息的原因。
然后 simpleDateFormat.parse(str)
使用 JVM 的时区,在本例中为 UTC+02:00
。
跟踪发生了什么:
Instant instant = Instant.now();
// => 2021-12-09 15:58:22.798 +00:00
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
// => "2021-12-09 15:58:22"
simpleDateFormat.parse(str);
// => 2021-12-09 15:58:22.000 +02:00
您只需修复模式(添加毫秒 .SSS
和时区 XXX
部分)以使结果与预期一致:
DateTimeFormatter.ofPattern("HH:mm:ss.SSSXXX")
// and something similar for SimpleDateFormat if needed
从自定义格式的字符串解析 Instant。
此示例说明如何从序列化时间解析 Instant,假设所有情况都有固定时区。
var serializedDateTime = "2020-01-01 10:20:30";
var zoneId = ZoneOffset.UTC; // may be any other zone
var format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
var instant = LocalDateTime
.parse(serializedDateTime, format)
// LocalDateTime is unzoned, just representation of (date + time) numbers in a single object
.atZone(zoneId)
// apply a zone to get ZonedDateTime (point in space-time on Earth)
.toInstant();
// convert to Instant (the same point adjusted to UTC+00:00)
答案只是将时区设置为 UTC -
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern("uuuu-MM-dd")).appendLiteral(" ")
.append(DateTimeFormatter.ofPattern("HH:mm:ss")).parseLenient();
long ts = Clock.systemUTC().millis();
System.out.println(ts);
Instant instant = Instant.ofEpochMilli(ts);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
*******
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
*******
long timestamp = simpleDateFormat.parse(str).getTime();
System.out.println(timestamp);
} catch (ParseException e) {
e.printStackTrace();
}
tl;博士
试图理解 Date
、Calendar
和 SimpleDateFormat
是在浪费大量时间。仅使用它们的替代品,即 java.time classes.
LocalDateTime // Represent a date with time-of-day, but lacking the context of a time zone or offset-from-UTC. So *not* a moment, *not* a point on the timeline.
.parse( // Parse your text string input into a date-time object.
"2021-12-09 15:58:22" // Your input of date with time-of-day but no offset/zone.
.replace( " " , "T" ) // Replace SPACE with a `T` to comply with standard ISO 8601 format.
) // Returns a `LocalDateTime` object.
.atZone( // Place that date-with-time into the context a particular time zone.
ZoneId.of( "America/Montreal" ) // Specify a time zone by its `Continent/Region` name.
) // Returns a `ZonedDateTime` object, a date with time-of-day as seen through the wall-clock time used by the people of a particular region. This *does* represent a moment, *is* a specific point on the timeline.
.toInstant() // Adjust from time zone to UTC (an offset of zero hours-minutes-seconds). This represents the very same moment as the `ZonedDateTime` object above, but as seen through a different wall-clock time.
.toEpochMilli() // Get a count of milliseconds from first moment of 1970 in UTC (1970-01-01T00:00Z) to the moment of our `Instant` object (& `ZonedDateTime` object).
看到这个 code run live at IdeOne.com。在那里你可以点击fork
复制,修改,然后运行。
1639083502000
避免遗留日期时间 classes
关于您关于两个小时差异的具体问题,明显的原因是时区差异。
解析不完整信息
你的解析,SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
是一个通配符。结果将是一个 java.util.Date
对象,它代表一个时刻,一个带有偏移量为零的时间的日期。但是您的输入缺少偏移量或区域的指示符。由于 ,您正在使用部分输入进行解析,信息不完整。
因此将应用一些默认 zone/offset。我不知道具体是什么区域或偏移量,因为我不在乎。那个可怕的 class 有可悲的缺陷,应该避免。
此外,Date
class 的另一个问题是它的 toString
方法具有应用 JVM 当前默认时区以偏离 UTC 的反特征class 表示的值。非常令人困惑,因为这会产生该区域是 Date
对象一部分的错觉,但事实并非如此。正如我所说,一个可怕的 class,可悲的缺陷。
仅使用 java.time classes。
java.time
了解带有时间的日期本质上是模棱两可的,不是片刻。
如果您跟踪的是 9 日下午 4 点,我们不知道那是指日本东京下午 4 点、法国图卢兹下午 4 点还是美国俄亥俄州托莱多下午 4 点——三个截然不同的时刻发生了几个小时分开。
LocalDateTime ldt = LocalDateTime.parse( "2021-12-09 16:00:00" ) ;
要跟踪时刻,时间线上的一个点,您必须在与 UTC 或时区的偏移量的上下文中放置新的时间日期。
偏移量只是比现代计时基准线(格林威治皇家天文台的本初子午线)提前或落后的小时-分钟-秒数。
时区更多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的命名历史。每个区域都有一个格式为 Continent/Region
的名称,例如 Europe/Berlin
或 Asia/Tokyo
.
要跟踪 UTC 中的时刻,偏移量为零,请使用 Instant
。
Instant instant = Instant.now() ;
要通过某个地区人们使用的挂钟时间查看同一时刻,请应用 ZoneId
以获得 ZonedDateTime
。
ZoneId z = ZoneId.of( "America/Edmonton" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
至于你使用SimpleDateFormat
、Date
和Calendar
,不要。避免使用这些遗留的日期时间 classes。嘿,它们是由不了解日期时间处理的人设计的。多年前,它们被 JSR 310 中定义的现代 java.time classes 所取代。Sun、Oracle 和 JCP 社区都放弃了那些 class是的。我建议你也这样做。
在您的代码中:
long ts = Clock.systemUTC().millis();
Instant instant = Instant.ofEpochMilli(ts);
这和这样做是一样的:
Instant.now().truncatedTo( ChronoUnit.MILLISECONDS )
在您的代码中:
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
(A) 如果仅使用偏移量而不是时区,请使用 OffsetDateTime
class。 ZonedDateTime
class 用于处理时区。
(B) 上面显示了从 Instant
调整到分区时刻的更简单方法:
myInstant.atZone( z )
我正在从当前时间创建一个字符串,我想再次将它转换为时间戳,但问题是,它在转换时减去了 2 小时。
这是我正在执行的步骤 -
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern("uuuu-MM-dd")).appendLiteral(" ")
.append(DateTimeFormatter.ofPattern("HH:mm:ss")).parseLenient();
long ts = Clock.systemUTC().millis();
System.out.println(ts);
Instant instant = Instant.ofEpochMilli(ts);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
long timestamp = simpleDateFormat.parse(str).getTime();
System.out.println(timestamp);
} catch (ParseException e) {
e.printStackTrace();
}
输出- 1639065502667 1639058302000
(2021-12-09 15:58:22 2021-12-09 13:58:22)
为什么两个小时相差这么大? 我怎样才能解析它以使输出相等?
让我猜猜,你的时区是 UTC+2
?
simpleDateFormat.parse(str)
假设您的日期在当前系统时区,但它是 UTC。
dateTimeFomatter
生成器使用没有毫秒和时区的格式。
这就是 str
值不包含有关它们的信息的原因。
然后 simpleDateFormat.parse(str)
使用 JVM 的时区,在本例中为 UTC+02:00
。
跟踪发生了什么:
Instant instant = Instant.now();
// => 2021-12-09 15:58:22.798 +00:00
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
// => "2021-12-09 15:58:22"
simpleDateFormat.parse(str);
// => 2021-12-09 15:58:22.000 +02:00
您只需修复模式(添加毫秒 .SSS
和时区 XXX
部分)以使结果与预期一致:
DateTimeFormatter.ofPattern("HH:mm:ss.SSSXXX")
// and something similar for SimpleDateFormat if needed
从自定义格式的字符串解析 Instant。
此示例说明如何从序列化时间解析 Instant,假设所有情况都有固定时区。
var serializedDateTime = "2020-01-01 10:20:30";
var zoneId = ZoneOffset.UTC; // may be any other zone
var format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
var instant = LocalDateTime
.parse(serializedDateTime, format)
// LocalDateTime is unzoned, just representation of (date + time) numbers in a single object
.atZone(zoneId)
// apply a zone to get ZonedDateTime (point in space-time on Earth)
.toInstant();
// convert to Instant (the same point adjusted to UTC+00:00)
答案只是将时区设置为 UTC -
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern("uuuu-MM-dd")).appendLiteral(" ")
.append(DateTimeFormatter.ofPattern("HH:mm:ss")).parseLenient();
long ts = Clock.systemUTC().millis();
System.out.println(ts);
Instant instant = Instant.ofEpochMilli(ts);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
*******
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
*******
long timestamp = simpleDateFormat.parse(str).getTime();
System.out.println(timestamp);
} catch (ParseException e) {
e.printStackTrace();
}
tl;博士
试图理解 Date
、Calendar
和 SimpleDateFormat
是在浪费大量时间。仅使用它们的替代品,即 java.time classes.
LocalDateTime // Represent a date with time-of-day, but lacking the context of a time zone or offset-from-UTC. So *not* a moment, *not* a point on the timeline.
.parse( // Parse your text string input into a date-time object.
"2021-12-09 15:58:22" // Your input of date with time-of-day but no offset/zone.
.replace( " " , "T" ) // Replace SPACE with a `T` to comply with standard ISO 8601 format.
) // Returns a `LocalDateTime` object.
.atZone( // Place that date-with-time into the context a particular time zone.
ZoneId.of( "America/Montreal" ) // Specify a time zone by its `Continent/Region` name.
) // Returns a `ZonedDateTime` object, a date with time-of-day as seen through the wall-clock time used by the people of a particular region. This *does* represent a moment, *is* a specific point on the timeline.
.toInstant() // Adjust from time zone to UTC (an offset of zero hours-minutes-seconds). This represents the very same moment as the `ZonedDateTime` object above, but as seen through a different wall-clock time.
.toEpochMilli() // Get a count of milliseconds from first moment of 1970 in UTC (1970-01-01T00:00Z) to the moment of our `Instant` object (& `ZonedDateTime` object).
看到这个 code run live at IdeOne.com。在那里你可以点击fork
复制,修改,然后运行。
1639083502000
避免遗留日期时间 classes
关于您关于两个小时差异的具体问题,明显的原因是时区差异。
解析不完整信息
你的解析,SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
是一个通配符。结果将是一个 java.util.Date
对象,它代表一个时刻,一个带有偏移量为零的时间的日期。但是您的输入缺少偏移量或区域的指示符。由于
因此将应用一些默认 zone/offset。我不知道具体是什么区域或偏移量,因为我不在乎。那个可怕的 class 有可悲的缺陷,应该避免。
此外,Date
class 的另一个问题是它的 toString
方法具有应用 JVM 当前默认时区以偏离 UTC 的反特征class 表示的值。非常令人困惑,因为这会产生该区域是 Date
对象一部分的错觉,但事实并非如此。正如我所说,一个可怕的 class,可悲的缺陷。
仅使用 java.time classes。
java.time
了解带有时间的日期本质上是模棱两可的,不是片刻。
如果您跟踪的是 9 日下午 4 点,我们不知道那是指日本东京下午 4 点、法国图卢兹下午 4 点还是美国俄亥俄州托莱多下午 4 点——三个截然不同的时刻发生了几个小时分开。
LocalDateTime ldt = LocalDateTime.parse( "2021-12-09 16:00:00" ) ;
要跟踪时刻,时间线上的一个点,您必须在与 UTC 或时区的偏移量的上下文中放置新的时间日期。
偏移量只是比现代计时基准线(格林威治皇家天文台的本初子午线)提前或落后的小时-分钟-秒数。
时区更多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的命名历史。每个区域都有一个格式为 Continent/Region
的名称,例如 Europe/Berlin
或 Asia/Tokyo
.
要跟踪 UTC 中的时刻,偏移量为零,请使用 Instant
。
Instant instant = Instant.now() ;
要通过某个地区人们使用的挂钟时间查看同一时刻,请应用 ZoneId
以获得 ZonedDateTime
。
ZoneId z = ZoneId.of( "America/Edmonton" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
至于你使用SimpleDateFormat
、Date
和Calendar
,不要。避免使用这些遗留的日期时间 classes。嘿,它们是由不了解日期时间处理的人设计的。多年前,它们被 JSR 310 中定义的现代 java.time classes 所取代。Sun、Oracle 和 JCP 社区都放弃了那些 class是的。我建议你也这样做。
在您的代码中:
long ts = Clock.systemUTC().millis();
Instant instant = Instant.ofEpochMilli(ts);
这和这样做是一样的:
Instant.now().truncatedTo( ChronoUnit.MILLISECONDS )
在您的代码中:
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
(A) 如果仅使用偏移量而不是时区,请使用 OffsetDateTime
class。 ZonedDateTime
class 用于处理时区。
(B) 上面显示了从 Instant
调整到分区时刻的更简单方法:
myInstant.atZone( z )