设置了时区的 SimpleDateFormat 获得正确的值但时区错误
SimpleDateFormat with Timezone set gets correct value but wrong zone
我在 Spring 应用程序中进行了简单测试,该应用程序的默认时区设置为 UTC
:
@SpringBootApplication
public class MemberIntegrationApp {
@Autowired
private TimeZoneProperties timeZoneProperties;
@PostConstruct
void started() {
TimeZone.setDefault(TimeZone.getTimeZone(timeZoneProperties.getAppDefault())); // which is UTC
}
public static void main(String[] args) {
SpringApplication.run(MemberIntegrationApp.class, args);
}
}
而且,这个简单的测试:(测试 class 注释为 @SpringBootTest
以在主 class 中加载配置, @SpringRunner
是也应用了)
/**
* Test the effect of setting timezone
*/
@Test
public void testTimezoneSettingOnSimpleDateFormat() throws ParseException {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String d = "2018-08-08 12:34:56";
log.info("Trying to parse the date string: {}", d);
Date result = f.parse(d);
log.info("The result should be 12:34 UTC: {}", result);
f.setTimeZone(TimeZone.getTimeZone("UTC"));
result = f.parse(d);
log.info("The result should be 12:34 UTC: {}", result);
f.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
result = f.parse(d);
log.info("The result should be 10:34 CEST: {}", result);
log.info("Now the offset(depre): {}", result.getTimezoneOffset());
}
我有输出:
Trying to parse the date string: 2018-08-08 12:34:56
The result should be 12:34 UTC: Wed Aug 08 12:34:56 UTC 2018
The result should be 12:34 UTC: Wed Aug 08 12:34:56 UTC 2018
The result should be 10:34 CEST: Wed Aug 08 10:34:56 UTC 2018
Now the offset(depre): 0
现在,为什么第四行的值是正确的,但时区是错误的?应该是Europe/Madrid
。而偏移量(在Java 8中被弃用,好吧我可以原谅),它应该是+0200,而不是0。
它是 UTC,因为在 log.info()
中转换为字符串时,slf4j 受到干扰????或者是什么?我不这么认为,因为 System.out.println()
也给了我 UTC。
我知道我应该使用 OffsetDateTime
,但它是遗留的,我们暂时无法将所有日期字段更改为该日期。我想知道为什么 Java 解析错误。
用SimpleDateFormat解析时Timezone.getDefault()
有什么作用? f.getTimezone()
是什么?他们似乎在这个过程的不同部分起作用.....
我问这个问题,因为 Jackson 在内部使用 SimpleDateFormat
来处理日期 string/formatting 日期。 ObjectMapper
上的配置会影响映射器使用的 SimpleDateFormat
吗?
我不认为这是一个错误,而是对这些行的误解:
f.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
result = f.parse(d);
log.info("The result should be 10:34 CEST: {}", result);
这是什么意思?
你首先设置一个时区,告诉解析器你将要解析一个Europe/Madrid时区的时间。
然后你显示它。它无法猜测您想要它的时区,因此它以默认时区显示它,在您的情况下为 UTC。
注意:
- 它在 UTC 中实际上是 10:34,而在马德里是 12:34,而不是相反。
Date.getTimezoneOffset()
是 offset between UTC and the default timezone(因此在您的情况下为 0),与您用于配置解析器的时区无关。此外,自 java 1.1 以来它已被弃用,你不应该再使用它了。
要显示不同时区的日期值,可以使用SimpleDateFormat.format()
,例如:
f.setTimeZone(TimeZone.getTimeZone("UTC"));
log.info("UTC {}", f.format(new Date()));
f.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
log.info("Europe/Madrid {}", f.format(new Date()));
感谢您的回复;在 OP 中,我正在考虑这一行,但把它写错了:
log.info("The result should be 14:34 CEST: {}", result);
我以为是"So I want it to be Madrid so the output is Madrid timezone",结果恰恰相反:
格式化程序的时区将是输入 date/string 的时区,而默认时区(如果未更改,则为 JVM 的时区,如果更改,则为 Timezone.getDefault()
, 将是输出结果(date/string)的时区。根据这两个,格式化程序将进行转换。
并且,Spring/Jackson内部使用SimpleDateFormat
来做JSON/Objectserialization/deserialization,所以这将是Spring的规则,太
而且,正如我测试的那样,spring.jackson.time-zone
和 mapper.setTimezone()
将被字段上的 JsonFormat(timezone = "xxx")
覆盖。也就是说,spring.jackson.time-zone
更通用,适用于需要"input"时区的Date的所有字段,JsonFormat(timezone = "xxx")
更具体,覆盖前者。 我猜想 spring.jackson.dateformat
和 @JsonFormat(pattern = "xx")
有相同的关系,但我没有测试过。
图形化:
我写这个测试来证明这一点:
/**
* Test the effect of setting timezone on a {@link SimpleDateFormat}. Apparently,
* <code>f.setTimezone()</code> sets the input timezone, and default timezone sets
* the output timezone.
*
*/
@Test
public void testTimezoneSettingOnSimpleDateFormat() throws ParseException {
/* *********** test parsing *********** */
log.info("********** test parsing **********");
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String d = "2018-08-08 12:34:56";
log.info("Trying to parse the date string: {}", d);
Date result = f.parse(d);
log.info("The result should be 12:34 UTC: {}", result);
f.setTimeZone(TimeZone.getTimeZone("UTC"));
result = f.parse(d);
log.info("The result should be 12:34 UTC: {}", result);
f.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
result = f.parse(d);
log.info("The result should be 10:34 UTC: {}", result);
/* ********** test formatting ********** */
log.info("********** test formatting **********");
// given
SimpleDateFormat f2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
// construct a date to represent this moment
OffsetDateTime now = OffsetDateTime.of(2018, 11, 16, 10, 22, 22, 0, ZoneOffset.of("+0100"));
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); // GMT+8, so Madrid+7
// when you construct a date without timezone, it will be the timezone of system/default!
Date nowDate = new Date(now.toEpochSecond() * 1000);
log.info("The constructed date is: {}", nowDate); // Fri Nov 16 17:22:22 CST 2018
// now formatter timezone is Madrid
f2.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
// now default timezone is Asia/Shanghai
// when
String result2 = f2.format(nowDate);
// then
log.info("The result should be 10:22: {}", result2); // 2018-11-16T10:22:22+01:00
log.info("Conclusion: the formatter's timezone sets the timezone of input; the application/default " +
"timezone sets the timezone of output. ");
}
public static Instant getInstantNow() {
Clock utcClock = Clock.systemUTC();
//ZoneId myTZ = ZoneId.of("Brazil/East");
return Instant.now(utcClock).minusSeconds(10800);
//Instant in = Instant.now(utcClock);
//return in.atZone(myTZ);
}
public static LocalDateTime getLocalDateTimeNow() {
ZonedDateTime nowBrasil = ZonedDateTime.now(ZoneId.of("Brazil/East"));
return LocalDateTime.from(nowBrasil);
}
public static LocalDate getLocalDateNow() {
ZonedDateTime nowBrasil = ZonedDateTime.now(ZoneId.of("Brazil/East"));
return LocalDate.from(nowBrasil);
}
我在 Spring 应用程序中进行了简单测试,该应用程序的默认时区设置为 UTC
:
@SpringBootApplication
public class MemberIntegrationApp {
@Autowired
private TimeZoneProperties timeZoneProperties;
@PostConstruct
void started() {
TimeZone.setDefault(TimeZone.getTimeZone(timeZoneProperties.getAppDefault())); // which is UTC
}
public static void main(String[] args) {
SpringApplication.run(MemberIntegrationApp.class, args);
}
}
而且,这个简单的测试:(测试 class 注释为 @SpringBootTest
以在主 class 中加载配置, @SpringRunner
是也应用了)
/**
* Test the effect of setting timezone
*/
@Test
public void testTimezoneSettingOnSimpleDateFormat() throws ParseException {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String d = "2018-08-08 12:34:56";
log.info("Trying to parse the date string: {}", d);
Date result = f.parse(d);
log.info("The result should be 12:34 UTC: {}", result);
f.setTimeZone(TimeZone.getTimeZone("UTC"));
result = f.parse(d);
log.info("The result should be 12:34 UTC: {}", result);
f.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
result = f.parse(d);
log.info("The result should be 10:34 CEST: {}", result);
log.info("Now the offset(depre): {}", result.getTimezoneOffset());
}
我有输出:
Trying to parse the date string: 2018-08-08 12:34:56
The result should be 12:34 UTC: Wed Aug 08 12:34:56 UTC 2018
The result should be 12:34 UTC: Wed Aug 08 12:34:56 UTC 2018
The result should be 10:34 CEST: Wed Aug 08 10:34:56 UTC 2018
Now the offset(depre): 0
现在,为什么第四行的值是正确的,但时区是错误的?应该是Europe/Madrid
。而偏移量(在Java 8中被弃用,好吧我可以原谅),它应该是+0200,而不是0。
它是 UTC,因为在 log.info()
中转换为字符串时,slf4j 受到干扰????或者是什么?我不这么认为,因为 System.out.println()
也给了我 UTC。
我知道我应该使用 OffsetDateTime
,但它是遗留的,我们暂时无法将所有日期字段更改为该日期。我想知道为什么 Java 解析错误。
用SimpleDateFormat解析时Timezone.getDefault()
有什么作用? f.getTimezone()
是什么?他们似乎在这个过程的不同部分起作用.....
我问这个问题,因为 Jackson 在内部使用 SimpleDateFormat
来处理日期 string/formatting 日期。 ObjectMapper
上的配置会影响映射器使用的 SimpleDateFormat
吗?
我不认为这是一个错误,而是对这些行的误解:
f.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
result = f.parse(d);
log.info("The result should be 10:34 CEST: {}", result);
这是什么意思?
你首先设置一个时区,告诉解析器你将要解析一个Europe/Madrid时区的时间。
然后你显示它。它无法猜测您想要它的时区,因此它以默认时区显示它,在您的情况下为 UTC。
注意:
- 它在 UTC 中实际上是 10:34,而在马德里是 12:34,而不是相反。
Date.getTimezoneOffset()
是 offset between UTC and the default timezone(因此在您的情况下为 0),与您用于配置解析器的时区无关。此外,自 java 1.1 以来它已被弃用,你不应该再使用它了。
要显示不同时区的日期值,可以使用SimpleDateFormat.format()
,例如:
f.setTimeZone(TimeZone.getTimeZone("UTC"));
log.info("UTC {}", f.format(new Date()));
f.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
log.info("Europe/Madrid {}", f.format(new Date()));
感谢您的回复;在 OP 中,我正在考虑这一行,但把它写错了:
log.info("The result should be 14:34 CEST: {}", result);
我以为是"So I want it to be Madrid so the output is Madrid timezone",结果恰恰相反:
格式化程序的时区将是输入 date/string 的时区,而默认时区(如果未更改,则为 JVM 的时区,如果更改,则为 Timezone.getDefault()
, 将是输出结果(date/string)的时区。根据这两个,格式化程序将进行转换。
并且,Spring/Jackson内部使用SimpleDateFormat
来做JSON/Objectserialization/deserialization,所以这将是Spring的规则,太
而且,正如我测试的那样,spring.jackson.time-zone
和 mapper.setTimezone()
将被字段上的 JsonFormat(timezone = "xxx")
覆盖。也就是说,spring.jackson.time-zone
更通用,适用于需要"input"时区的Date的所有字段,JsonFormat(timezone = "xxx")
更具体,覆盖前者。 我猜想 spring.jackson.dateformat
和 @JsonFormat(pattern = "xx")
有相同的关系,但我没有测试过。
图形化:
我写这个测试来证明这一点:
/**
* Test the effect of setting timezone on a {@link SimpleDateFormat}. Apparently,
* <code>f.setTimezone()</code> sets the input timezone, and default timezone sets
* the output timezone.
*
*/
@Test
public void testTimezoneSettingOnSimpleDateFormat() throws ParseException {
/* *********** test parsing *********** */
log.info("********** test parsing **********");
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String d = "2018-08-08 12:34:56";
log.info("Trying to parse the date string: {}", d);
Date result = f.parse(d);
log.info("The result should be 12:34 UTC: {}", result);
f.setTimeZone(TimeZone.getTimeZone("UTC"));
result = f.parse(d);
log.info("The result should be 12:34 UTC: {}", result);
f.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
result = f.parse(d);
log.info("The result should be 10:34 UTC: {}", result);
/* ********** test formatting ********** */
log.info("********** test formatting **********");
// given
SimpleDateFormat f2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
// construct a date to represent this moment
OffsetDateTime now = OffsetDateTime.of(2018, 11, 16, 10, 22, 22, 0, ZoneOffset.of("+0100"));
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); // GMT+8, so Madrid+7
// when you construct a date without timezone, it will be the timezone of system/default!
Date nowDate = new Date(now.toEpochSecond() * 1000);
log.info("The constructed date is: {}", nowDate); // Fri Nov 16 17:22:22 CST 2018
// now formatter timezone is Madrid
f2.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
// now default timezone is Asia/Shanghai
// when
String result2 = f2.format(nowDate);
// then
log.info("The result should be 10:22: {}", result2); // 2018-11-16T10:22:22+01:00
log.info("Conclusion: the formatter's timezone sets the timezone of input; the application/default " +
"timezone sets the timezone of output. ");
}
public static Instant getInstantNow() {
Clock utcClock = Clock.systemUTC();
//ZoneId myTZ = ZoneId.of("Brazil/East");
return Instant.now(utcClock).minusSeconds(10800);
//Instant in = Instant.now(utcClock);
//return in.atZone(myTZ);
}
public static LocalDateTime getLocalDateTimeNow() {
ZonedDateTime nowBrasil = ZonedDateTime.now(ZoneId.of("Brazil/East"));
return LocalDateTime.from(nowBrasil);
}
public static LocalDate getLocalDateNow() {
ZonedDateTime nowBrasil = ZonedDateTime.now(ZoneId.of("Brazil/East"));
return LocalDate.from(nowBrasil);
}