Java-将timestamp转换为Date,并返回到timestamp更改日期



我正在从当前时间创建一个字符串,我想再次将其转换为时间戳,但问题是,它在转换时减去了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();
}

输出-16390655026671639058302000

(2021-12-09 15:58:222021-12-09 13:58:22(

为什么两个小时有差异?我如何解析它以使输出相等?

tl;dr

试图理解DateCalendarSimpleDateFormat是一种巨大的时间浪费。只使用它们的替换,java.time类。

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).

请在IdeOne.com上实时查看此代码。在那里,您可以单击fork进行复制、更改和运行。

1639083502000

避免使用旧式日期时间类

关于你关于两小时时差的具体问题,显而易见的原因是时区差异。

正在分析不完整的信息

你的解析,SimpleDateFormat("yyyy-MM-dd HH:mm:ss")是一个未知数。结果将是一个java.util.Date对象,它表示一个时刻,一个偏移量为零的日期。但您的输入缺少偏移或区域的指示器。正如Sotirios Delimanolis所评论的,您正在使用部分输入进行解析,其中包含不完整的信息。

因此,将应用一些默认的区域/偏移。我不知道具体是什么区域或偏移,因为我不在乎。这个可怕的阶层有着可悲的缺陷,应该避免。

此外,Date类的另一个问题是,其toString方法具有应用JVM的当前默认时区来调整该类所代表的UTC值的反特性。非常令人困惑,因为这会产生一种错觉,认为该区域是Date对象的一部分,但事实并非如此。正如我所说,一个糟糕的阶层,可悲的缺陷。

请仅使用java.time类。

java.time

要明白,一天中有时间的日期本质上是模糊的,是而不是一个时刻。

如果你追踪的是9日下午4点,我们不知道这是指日本东京下午4点、法国图卢兹下午4点还是美国俄亥俄州托莱多下午4点——这三个非常不同的时刻相隔几个小时。

LocalDateTime ldt = LocalDateTime.parse( "2021-12-09 16:00:00" ) ;

要跟踪某个时刻,即时间线上的某个点,必须在与UTC或时区偏移的上下文中放置一个带时间的日期。

偏移量只比现代计时的基准线(格林威治皇家天文台的本初子午线(提前或落后几个小时-分-秒。

时区远不止这些。时区是特定地区人民使用的偏移量的过去、现在和未来变化的命名历史。每个区域具有Continent/Region格式的名称,例如Europe/BerlinAsia/Tokyo

要跟踪UTC中偏移为零的力矩,请使用Instant

Instant instant = Instant.now() ;

要通过一个地区的人们使用的挂钟时间看到同一时刻,请应用ZoneId来获得ZonedDateTime

ZoneId z = ZoneId.of( "America/Edmonton" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

至于SimpleDateFormatDateCalendar的使用,请不要使用。避免使用这些遗留的日期-时间类。Hey是由不了解日期时间处理的人设计的。几年前,JSR310中定义的现代java.time类取代了它们。Sun、Oracle和JCP社区都放弃了这些课程。我建议你也这样做。

在您的代码中:

long ts = Clock.systemUTC().millis();
Instant instant = Instant.ofEpochMilli(ts);

这和这样做是一样的:

Instant.now().truncatedTo( ChronoUnit.MILLISECONDS )

在您的代码中:

ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);

(A( 当只使用偏移量而不是时区时,请使用OffsetDateTime类。ZonedDateTime类用于处理时区。

(B( 上面显示了一种从Instant调整到分区时刻的简单方法:

myInstant.atZone( z )

答案只是将时区设置为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();
}

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+2

simpleDateFormat.parse(str)假设您的日期在当前系统时区,但它是UTC。

最新更新