有些日期无法在Java中准确转换为特定时区午夜的纪元时间戳。

huangapple go评论62阅读模式
英文:

Some dates cannot be converted correctly in Java to an epoch timestamps at the midnight of a specific timezone

问题

以下是您要求的翻译部分:

这段 Java 代码接收一个日期字符串,旨在打印出该日期在 CET 时区的午夜时刻的纪元时间戳(假设我不在相同的时区)。

public static void main(String[] args) throws ParseException {

    String dateStr = "1995-06-06";

    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    formatter.setTimeZone(TimeZone.getTimeZone("CET"));
    Date date = formatter.parse(dateStr);
    Calendar c = new GregorianCalendar();
    c.setTimeZone(TimeZone.getTimeZone("CET"));
    c.setTime(date);

    c.set(Calendar.HOUR_OF_DAY, 0);
    c.set(Calendar.MINUTE, 0);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MILLISECOND, 0);

    System.out.println("Epoch timestamp = " + c.getTime().getTime());
}

如果我运行上面的程序,应该会打印出:

Epoch timestamp = 802389600000

我可以在这里验证它是否正确:

https://www.epochconverter.com/timezones?q=802389600&tz=Europe%2FMalta

现在,这对于大多数日期来说是有效的。然而,对于一些奇怪的日期,比如 "1975-09-19",它不起作用。事实上,它生成了时间戳 180313200000,这对应的是凌晨 1 点,而不是午夜:

https://www.epochconverter.com/timezones?q=180313200&tz=Europe%2FMalta

您能解释为什么吗?我漏掉了什么?

英文:

This Java code, given a date as a string, is supposed to print the epoch timestamp for the same date at the midnight for the CET zone (supposing I'm not in the same zone).

public static void main(String[] args) throws ParseException {

    String dateStr = "1995-06-06";

    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    formatter.setTimeZone(TimeZone.getTimeZone("CET"));
    Date date = formatter.parse(dateStr);
    Calendar c = new GregorianCalendar();
    c.setTimeZone(TimeZone.getTimeZone("CET"));
    c.setTime(date);

    c.set(Calendar.HOUR_OF_DAY, 0);
    c.set(Calendar.MINUTE, 0);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MILLISECOND, 0);

    System.out.println("Epoch timestamp = " + c.getTime().getTime());
}

If I run the above program I should get printed:

 Epoch timestamp = 802389600000

And I can verify it's correct here:

https://www.epochconverter.com/timezones?q=802389600&tz=Europe%2FMalta

Now, that works for most of the dates. However, there are some bizarre dates like "1975-09-19", where it doesn't work. In fact, It generates 180313200000 as a timestamp, which gives 1am and not midnight:

https://www.epochconverter.com/timezones?q=180313200&tz=Europe%2FMalta

Can you explain why? What am I missing?

答案1

得分: 3

时区差异

你的 Java 代码使用了 CET 时区,实际上这并不是一个真正的时区(例如,因为大多数使用这个时区的地区在大部分时间使用的是 CEST 时区)。Java 将 CET 转换为 Europe/Paris 时区。法国和巴黎在 1975 年没有使用夏令时(DST)。夏令时在 1976 年 3 月重新引入。

你提供的时代转换链接指定了马耳他时区(Europe/Malta)。马耳他在 1975 年使用了夏令时:那一年从 4 月 20 日到 9 月 21 日都处于 CEST 时区。

这解释了你结果中的差异。

在 Java 代码中

如果你想使用马耳他时区:

String dateStr = "1975-09-19";

long epochTimestamp = 
        LocalDate 
        .parse(dateStr)                             
        .atStartOfDay(ZoneId.of("Europe/Malta"))
        .toInstant()
        .toEpochMilli();

System.out.println("Epoch timestamp = " + epochTimestamp);

这会输出:

> Epoch timestamp = 180309600000

你提供的时代转换器也会显示相同的结果:

> ## 转换结果(180309600)
>
> 180309600 转换为 1975 年 9 月 19 日 00:00:00(上午),位于**欧洲/马耳他(CEST)**时区。偏移量(与格林威治时间/GMT 的差异)为 +02:00,或以秒为单位为 7200。此日期位于夏令时中。

在 Java 中,请使用 java.time,这是现代 Java 日期和时间 API,用于处理日期和时间。与旧的日期和时间类(如 SimpleDateFormatTimeZoneDateCalendar)相比,它要更加方便。同时,将小时等设定为 0 并不是获取一天中的第一刻的正确方法。有些情况下,夏令时从一天开始时就开始生效,因此该天的第一刻是 01:00:00。Java 知道这一点,因此 atStartOfDay 方法会给你所询问日期的正确的第一刻。

无论使用过时的类还是现代类,始终要使用地区/城市格式来指定时区,例如 Europe/Paris 或 Europe/Malta。三、四和五个字母的时区缩写通常是含糊的,而且通常并不是真正的时区,因此不可靠。

链接

英文:

Time zone discrepancy

Your Java code uses CET, which is not really a time zone (for example because most of the areas where it’s used use CEST instead for most of the year). Java translates CET to Europe/Paris. France and Paris did not use summer time (DST) in 1975. It was reintroduced in March 1976.

Your link to the epoch converter specifies Malta time zone (Europe/Malta). Malta did use summer time in 1975: it was on CEST from 20 April to 21 September that year.

This explains the difference in your results.

In Java code

If you wanted Malta time:

	String dateStr = "1975-09-19";
	
	long epochTimestamp = 
            LocalDate 
            .parse(dateStr)                            
			.atStartOfDay(ZoneId.of("Europe/Malta"))
			.toInstant()
			.toEpochMilli();
	
	System.out.println("Epoch timestamp = " + epochTimestamp);

This prints:

> Epoch timestamp = 180309600000

And the epoch converter that you linked to is happy to agree:

> ## Conversion results (180309600)
>
> 180309600 converts to Friday September 19, 1975 00:00:00 (am) in
> time zone Europe/Malta (CEST) The offset (difference to Greenwich
> Time/GMT) is +02:00 or in seconds 7200. This date is in daylight
> saving time.

In Java do use java.time, the modern Java date and time API, for your date and time work. It is so much nicer to work with compared to the old date and time classes like SimpleDateFormat, TimeZone, Date and Calendar. Also setting the hours, etc., to 0 is not the correct way to get the first moment of the day. There are cases where summer time begins at the start of the day, so the first moment of the day is 01:00:00. Java knows that, so the atStartOfDay method will give you the correct forst moment of the day in question.

And no matter if using outdated or modern classes always specify time zone in the region/city format, for example Europe/Paris or Europe/Malta. The three, four and five letter time zone abbreviations are often ambiguous and often not true time zones, so not to be relied on.

答案2

得分: 2

在您的日期示例之间似乎存在关于夏时制的差异。
如果我使用java.time(自Java 8以来应始终使用),我会得到具有不同偏移量的结果:

  • "1995-06-06""+02:00"
  • "1975-09-19""+01:00"

这是我得到结果的方式:

public static void main(String[] args) {
	// 提供两个示例日期
	String workingDateStr = "1995-06-06";
	String failingDateStr = "1975-09-19";
	// 以指定格式创建日期时间格式化器
	DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
	// 然后将它们解析为不考虑时间或时区的日期对象
	LocalDate workingDate = LocalDate.parse(workingDateStr, dtf);
	LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
	/*
	 * 然后通过使用解析的日期,添加时间为00:00:00和时区,
	 * 创建具有时间和时区意识的对象
	 */
	ZonedDateTime workingZdt = ZonedDateTime.of(workingDate, LocalTime.MIN, ZoneId.of("CET"));
	ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));
	
	// 最后,打印结果的不同表示
	System.out.println(workingZdt + " ——> " + workingZdt.toInstant().toEpochMilli());
	System.out.println(failingZdt + " ——> " + failingZdt.toInstant().toEpochMilli());
}

输出:

1995-06-06T00:00+02:00[CET] ——> 802389600000
1975-09-19T00:00+01:00[CET] ——> 180313200000

这意味着您可能最好使用具体的偏移量而不是时区。

这个问题可能是由于马耳他夏时制的引入时间可能不同,查看以下代码及其输出:

public static void main(String[] args) {
	// 提供一个示例日期
	String failingDateStr = "1975-09-19";
	// 以指定格式创建日期时间格式化器
	DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
	// 将其解析为不考虑时间或时区的日期对象
	LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
	/*
	 * 然后通过使用解析的日期,添加时间为00:00:00和时区,
	 * 创建具有时间和时区意识的对象
	 */
	ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));
	
    // 将1975年添加一些年份,并...
	for (int year = 0; year < 4; year++) {
		// ...打印结果的不同表示
		System.out.println(failingZdt.plusYears(year) + " ——> " 
				+ failingZdt.plusYears(year).toInstant().toEpochMilli());
	}
}

输出:

1975-09-19T00:00+01:00[CET] ——> 180313200000
1976-09-19T00:00+01:00[CET] ——> 211935600000
1977-09-19T00:00+02:00[CET] ——> 243468000000
1978-09-19T00:00+02:00[CET] ——> 275004000000

这个输出表明在1977年引入了夏时制... 这是否正确?

英文:

There seems to be a difference concerning daylight saving time between your date examples.
If I use java.time (which should always be used since Java 8), I get results with different offsets:

  • "+02:00" for "1995-06-06" and
  • "+01:00" for "1975-09-19"

This is how I got the results:

public static void main(String[] args) {
	// provide two sample dates
	String workingDateStr = "1995-06-06";
	String failingDateStr = "1975-09-19";
	// and a formatter that parses the format
	DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
	// then parse them to date objects that don't know about time or zone
	LocalDate workingDate = LocalDate.parse(workingDateStr, dtf);
	LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
	/*
	 *	then create an objects that are aware of time and zone
	 *	by using the parsed dates, adding a time of 00:00:00 and a zone
	 */
	ZonedDateTime workingZdt = ZonedDateTime.of(workingDate, LocalTime.MIN, ZoneId.of("CET"));
	ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));
	
	// finally, print different representations of the results
	System.out.println(workingZdt + " ——> " + workingZdt.toInstant().toEpochMilli());
	System.out.println(failingZdt + " ——> " + failingZdt.toInstant().toEpochMilli());
}

Output:

1995-06-06T00:00+02:00[CET] ——> 802389600000
1975-09-19T00:00+01:00[CET] ——> 180313200000

That means you might be better off using specific offsets instead of zones.

This issue could be due to the timing of the introduction of Daylight Saving Time in Malta, have a look at the following code and its output:

public static void main(String[] args) {
	// provide two sample dates
	String failingDateStr = "1975-09-19";
	// and a formatter that parses the format
	DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
	// then parse them to date objects that don't know about time or zone
	LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
	/*
	 *	then create an objects that are aware of time and zone
	 *	by using the parsed dates, adding a time of 00:00:00 and a zone
	 */
	ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));
	
    // add some years to 1975 and...
	for (int year = 0; year < 4; year++) {
		// ... print the different representations of the result
		System.out.println(failingZdt.plusYears(year) + " ——> " 
				+ failingZdt.plusYears(year).toInstant().toEpochMilli());
	}
}

Output:

1975-09-19T00:00+01:00[CET] ——> 180313200000
1976-09-19T00:00+01:00[CET] ——> 211935600000
1977-09-19T00:00+02:00[CET] ——> 243468000000
1978-09-19T00:00+02:00[CET] ——> 275004000000

This output indicates an introduction in 1977... Is that correct?

huangapple
  • 本文由 发表于 2020年3月16日 23:15:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/60708572.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定