Antonio 的The Answer 是正确的,应该被接受(点击大的空复选标记)。
此答案添加了一些想法和示例代码。
避免使用 3 个字母的时区代码
避免使用或甚至考虑那些 3 或 4 个字母的代码,例如 IST 或 PST。它们不是标准化的,它们不是唯一的,它们进一步混淆了Daylight Saving Time (DST) 周围的问题。例如,IST 表示“印度标准时间”、“爱尔兰标准时间”等。
使用proper time zone names。其中大部分是“大陆”+“/”+“城市/地区”模式。城市/地区名称并非专门针对该城镇,而是作为一个易于识别的名称,用于尽可能广泛的区域,该名称与 time zone rules 和异常(包括 @987654325)共享相同的过去、现在和未来规则集@)。
使用 UTC
一般来说,您应该使用UTC time zone 进行所有业务逻辑、数据存储和数据交换。调整到特定时区,仅在用户期望时进行演示。
使用合适的日期时间框架
旧的 java.util.Date/.Calendar 类是处理日期时间工作的大胆尝试,但最终失败了。它们是出了名的麻烦,在设计和实现上都有缺陷。避开他们。
第 3 方 Joda-Time 库是一种解决方案。它适用于许多版本的 Java 和 Android。 Joda-Time 启发了另一个解决方案,即 Java 8 及更高版本中的java.time package (Tutorial)。
解决方案
这个问题的目标似乎是获取一个 java.util.Date 对象,分配所需的时区,并生成一个 java.util.Calendar 对象。
幸运的是 java.time 框架有转换方法。见this Tutorial page。
示例代码如下,使用 Java 8 Update 45 中的 java.time。
您可能需要以下导入:
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
让我们模拟传入一个 java.util.Date。我们将基于“now”实例化一个 Date。
Date inputDate = new Date( ); // Simulate getting a java.util.Date object.
然后我们使用proper time zone names 定义所需的时区。让我们把蒙特利尔以及问题中提到的美国太平洋时区和印度时区抛在一边。
ZoneId zoneLosAngeles = ZoneId.of( "America/Los_Angeles" );
ZoneId zoneMontréal = ZoneId.of( "America/Montreal" );
ZoneId zoneKolkata = ZoneId.of( "Asia/Kolkata" );
然后我们将其转换为Instant,即时间线上的一个点,与时区无关。
Instant instant = inputDate.toInstant( );
然后我们分配不同的时区来创建ZonedDateTime 实例。看看我们如何以两种方式实例化 ZonedDateTime:[a] 从 Instant,或 [b] 通过 withZoneSameInstant 方法从另一个 ZonedDateTime。两种方式如下所示。
请注意,java.time(和 Joda-Time)使用immutable objects,这是一种设计模式,我们基于旧实例创建新实例,而不是更改(“变异”)旧实例。 Thread-safety 是主要好处之一。
ZonedDateTime zdtLosAngeles = ZonedDateTime.ofInstant( instant, zoneLosAngeles );
ZonedDateTime zdtMontréal = ZonedDateTime.ofInstant( instant, zoneMontréal );
ZonedDateTime zdtKolkata = ZonedDateTime.ofInstant( instant, zoneKolkata );
ZonedDateTime zdtUtc = zdtKolkata.withZoneSameInstant( ZoneOffset.UTC );
最后,我们将其中一个转换为GregorianCalendar 对象,它是java.util.Calendar 的子类。
GregorianCalendar calendarKolkata = GregorianCalendar.from( zdtKolkata );
转储到控制台。
System.out.println( "inputDate: " + inputDate );
System.out.println( "zdtLosAngeles: " + zdtLosAngeles );
System.out.println( "zdtMontréal: " + zdtMontréal );
System.out.println( "zdtKolkata: " + zdtKolkata );
System.out.println( "zdtUtc: " + zdtUtc );
System.out.println( "calendarKolkata: " + calendarKolkata );
运行时。
inputDate: Wed Jun 24 15:12:12 PDT 2015
zdtLosAngeles: 2015-06-24T15:12:12.153-07:00[America/Los_Angeles]
zdtMontréal: 2015-06-24T18:12:12.153-04:00[America/Montreal]
zdtKolkata: 2015-06-25T03:42:12.153+05:30[Asia/Kolkata]
zdtUtc: 2015-06-24T22:12:12.153Z
calendarKolkata: java.util.GregorianCalendar[time=1435183932153,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Kolkata",offset=19800000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2015,MONTH=5,WEEK_OF_YEAR=26,WEEK_OF_MONTH=4,DAY_OF_MONTH=25,DAY_OF_YEAR=176,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=4,AM_PM=0,HOUR=3,HOUR_OF_DAY=3,MINUTE=42,SECOND=12,MILLISECOND=153,ZONE_OFFSET=19800000,DST_OFFSET=0]