问题描述
我想将 java.util.Date 对象设置为 java.util.Calendar 对象以更改 Date 对象的年份,如下所示:
TimezoneId 等于“Europe/Istanbul”。年份是 2018。
日期对象为 1969-12-31 22:00:00.0 (1969-12-31T22:00:00.000Z),区域信息如下:
"sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]"
欧洲/伊斯坦布尔时区是 UTC+3,但是当我用这个日期对象运行代码时,新的 GregorianCalendar 将日期对象的时间更改为 -1 小时。有些地方工作不正常,我找不到为什么这不将 UTC 转换为 UTC+3 .Returned Date 对象等于 Sun Dec 31 21:00:00 UTC 2017.UTC-1 对于欧洲/伊斯坦布尔不正确。我该如何解决这个问题?
public static Date setYearOfDate(Date date,int year,String timeZoneId)
{
Calendar customCalendar = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId));
customCalendar.setTime(date);
customCalendar.set(Calendar.YEAR,year);
return customCalendar.getTime();
}
注意:customCalendar zoneinfo 如下: "sun.util.calendar.ZoneInfo[id="Europe/Istanbul",offset=10800000,transitions=130,lastRule=null]"
解决方法
java.time
我建议您使用 java.time API。
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
class Main {
public static void main(String[] args) {
String dateTimeString = "1969-12-31T22:00:00.000Z";
System.out.println(setYearOfDate(dateTimeString,2018,"Europe/Istanbul"));
}
public static String setYearOfDate(String dateTimeString,int year,String zoneId) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX",Locale.ENGLISH)
.withZone(ZoneId.of(zoneId));
ZonedDateTime zdt = ZonedDateTime.parse(dateTimeString);
zdt = zdt.withYear(year);
return dtf.format(zdt);
}
}
输出:
2019-01-01T01:00:00.000+03
java.util
日期时间 API 及其格式化 API SimpleDateFormat
已过时且容易出错。建议完全停止使用它们并切换到 modern date-time API。
- 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7。
- 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。
SimpleDateFormat
的错误行为的一个很好的例子如下所示:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
class Main {
public static void main(String[] args) throws ParseException {
String dateTimeString = "1969-12-31T22:00:00.000Z";
String timeZoneId = "Europe/Istanbul";
System.out.println(setYearOfDate(dateTimeString,timeZoneId));
}
public static String setYearOfDate(String dateTimeString,String timeZoneId) throws ParseException {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX",Locale.ENGLISH);
calendar.setTime(sdf.parse(dateTimeString));
calendar.set(Calendar.YEAR,year);
sdf.setTimeZone(TimeZone.getTimeZone(timeZoneId));
return sdf.format(calendar.getTime());
}
}
输出:
2019-01-01T02:00:00.000+03
您还将在 1968 年和 1970 年观察到 SimpleDateFormat
的这种行为。但是,如果您将输入字符串中的年份更改为其他年份,它将以预期的方式运行,例如相同的代码将为输入字符串 2019-01-01T01:00:00.000+03
输出 1979-12-31T22:00:00.000Z
。
我认为@Live_and_Let_Live 的答案是正确的,要打印 Date
,您需要将 TimeZone
提供给 SimpleDateFotmat
,实际上您不需要使用 SimpleDateFotmat
设置年份;使用您的代码并正确应用 TimeZone
,您将获得预期的结果:
public static void main (String[] args) throws java.text.ParseException
{
String initialDate = "1969-12-31T22:00:00.000Z";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
Date startDate = sdf.parse(initialDate);
System.out.println("Start date: " + startDate);
System.out.println("Start date formatted: " + sdf.format(startDate));
Date resultDate = setYearOfDate(startDate,"Europe/Istanbul");
System.out.println("Result date: " + resultDate);
System.out.println("Result date formatted: " + sdf.format(resultDate));
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Istanbul"));
System.out.println("Result date formatted with TimeZone: " + sdf2.format(resultDate));
}
输出:
Start date: Wed Dec 31 22:00:00 GMT 1969
Start date formatted: 1969-12-31T22:00:00.000Z
Result date: Sun Dec 31 21:00:00 GMT 2017
Result date formatted: 2017-12-31T21:00:00.000Z
Result date formatted with TimeZone: 2018-01-01T00:00:00.000+03
编辑: 这是 Java 8 日期和时间 API 替代方案:
public static void main(String[] args) {
String initialDate = "1969-12-31T22:00:00.000Z";
ZoneId zone = ZoneId.of("Europe/Istanbul");
DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(zone);
ZonedDateTime startDate = ZonedDateTime.parse(initialDate,formatter);
System.out.println("Start date: " + startDate);
System.out.println("Start date formatted with TimeZone: " + formatter.format(startDate));
ZonedDateTime resultDate = setYearOfDate(startDate,"Europe/Istanbul");
System.out.println("Result date: " + resultDate);
System.out.println("Result date formatted with TimeZone: " + formatter.format(resultDate));
}
static ZonedDateTime setYearOfDate(ZonedDateTime date,String timeZoneId)
{
GregorianCalendar calendar = GregorianCalendar.from(date);
calendar.set(GregorianCalendar.YEAR,year);
return calendar.toZonedDateTime();
}
输出:
Start date: 1970-01-01T00:00+02:00[Europe/Istanbul]
Start date formatted with TimeZone: 1970-01-01T00:00:00+02:00[Europe/Istanbul]
Result date: 2018-01-01T00:00+03:00[Europe/Istanbul]
Result date formatted with TimeZone: 2018-01-01T00:00:00+03:00[Europe/Istanbul]