我想在java中使用SimpleDateFormat类将日期从一个时区转换为另一个时区。但不知何故,它产生了不同的结果,假设它们位于同一个TimeZone中。
这是一个测试用例,它生成一个结果作为IST,另一个结果作为GMT。我认为它应该只为这两种情况产生GMT。
public class TestOneCoreJava {
public static void main(String[] args) throws ParseException {// Asia/Calcutta
DateFormat formatter = new SimpleDateFormat("dd-MMM-yy hh:mm:ss a");
System.out.println(getDateStringToShow(formatter.parse("26-Nov-10 03:31:20 PM +0530"),"Asia/Calcutta", "Europe/Dublin", false));
System.out.println(getDateStringToShow(formatter.parse("02-Oct-10 10:00:00 AM +0530"),"Asia/Calcutta", "Europe/Dublin", false));
//------Output--
//26-Nov-10 GMT
//02-Oct-10 IST
}
public static String getDateStringToShow(Date date,
String sourceTimeZoneId, String targetTimeZoneId, boolean includeTime) {
String result = null;
// System.out.println("CHANGING TIMEZONE:1 "+UnitedLexConstants.SIMPLE_FORMAT.format(date));
String date1 = new SimpleDateFormat("dd-MMM-yy hh:mm:ss a").format(date);
SimpleDateFormat sourceTimeZoneFormat = new SimpleDateFormat("Z");
sourceTimeZoneFormat.setTimeZone(TimeZone.getTimeZone(sourceTimeZoneId));
date1 += " " + sourceTimeZoneFormat.format(date);
// Changed from 'Z' to 'z' to show IST etc, in place of +5:30 etc.
SimpleDateFormat targetTimeZoneFormat = new SimpleDateFormat("dd-MMM-yy hh:mm:ss a z");
targetTimeZoneFormat.setTimeZone(TimeZone.getTimeZone(targetTimeZoneId));
SimpleDateFormat timeZoneDayFormat = null;
if (includeTime) {
timeZoneDayFormat = targetTimeZoneFormat;
} else {
timeZoneDayFormat = new SimpleDateFormat("dd-MMM-yy z");
}
timeZoneDayFormat.setTimeZone(TimeZone.getTimeZone(targetTimeZoneId));
try {
result = timeZoneDayFormat.format(targetTimeZoneFormat.parse(date1));
// System.out.println("CHANGING TIMEZONE:3 "+result);
} catch (ParseException e) {
e.printStackTrace();
}
return result;
}
}
答案 0 :(得分:1)
使用现代 java.time 类,特别是ZonedDateTime
和ZoneId
。请参阅Oracle Tutorial。
ZonedDateTime // Represent a date and time-of-day in a specific time zone.
.now( // Capture the current moment as seen in the wall-clock time used by the people of a particular region (a time zone).
ZoneId.of( "Pacific/Auckland" ) // Specify time zone using proper name in `Continent/Region` format. Never use 3-4 letter pseudo-zone such as IST or PST or EST.
) // Returns a `ZonedDateTime` object.
.withZoneSameInstant( // Adjust from one time zone to another. Same point on the timeline, same moment, but different wall-clock time.
ZoneId.of( "Africa/Tunis" )
) // Returns a new fresh `ZonedDateTime` object rather than altering/“mutating” the original, per immutable objects pattern.
.toString() // Generate text in standard ISO 8601 format, extended to append name of zone in square brackets.
2018-09-18T21:47:32.035960 + 01:00 [非洲/突尼斯]
对于UTC,请致电ZonedDateTime::toInstant
。
避免使用这三个字母的时区代码。它们既不标准也不独特。例如,您使用“IST”可能意味着印度标准时间,爱尔兰标准时间以及其他可能。
使用正确的time zone names。时区及其名称的定义经常更改,因此请保持您的源代码最新。例如,旧的“亚洲/加尔各答”现在是“亚洲/加尔各答”。而不只是名字;政府因改变时区的规则/行为而臭名昭着,偶尔会在最后一刻。
避免使用捆绑的java.util.Date和Calendar类。众所周知,它们很麻烦,将被新的java.time.* package(受Joda-Time启发)在Java 8中取代。
Instant
学习以UTC而不是自己的狭隘时区思考和工作。记录,数据交换和数据存储通常应以UTC格式完成。
Instant instant = Instant.now() ; // Capture the current moment in UTC.
instant.toString():2018-09-18T20:48:43.354953Z
ZonedDateTime
调整为时区。同一时刻,时间轴上的同一点,不同的挂钟时间。应用ZoneId
(时区)来获取ZonedDateTime
对象。
ZoneId zMontreal = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdtMontreal = instant.atZone( zMontreal ) ; // Same moment, different wall-clock time.
我们可以使用Instant
或ZonedDateTime
再次进行调整。
ZoneId zKolkata = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdtKolkata = zdtMontreal.withZoneSameInstant( zKolkata ) ;
在任何这些类上调用toString
会生成标准ISO 8601类中的文本。 ZonedDateTime
类通过在方括号中附加时区名称来明智地扩展标准。
将日期时间值作为文本交换时,请始终使用ISO 8601格式。不要使用问题中所示的自定义格式或本地化格式。
java.time 类默认使用标准格式来解析和生成字符串。
Instant instant = Instant.parse( "2018-01-23T01:23:45.123456Z" ) ;
使用标准格式可以避免在问题中看到的所有混乱的字符串操作。
您始终可以通过提取ZonedDateTime
将Instant
带回UTC。
Instant instant = zdtKolkata.toInstant() ;
DateTimeFormatter
以其他格式表示您的日期时间值search Stack Overflow for DateTimeFormatter
。你会发现很多例子和讨论。
更新: Joda-Time 项目现在处于维护模式,并建议迁移到 java.time 类。我将此部分保留为历史。
小心java.util.Date对象,它们看起来像是时区但实际上却没有。在Joda-Time中,DateTime确实知道其指定的时区。通常应指定所需的时区。否则,将分配JVM的默认时区。
Joda-Time主要使用不可变对象。不是修改实例,而是创建新的新实例。在调用诸如toDateTime
之类的方法时,会返回一个新的DateTime实例,使原始对象保持原样并保持不变。
//DateTime now = new DateTime(); // Default time zone automatically assigned.
// Convert a java.util.Date to Joda-Time.
java.util.Date date = new java.util.Date();
DateTime now = new DateTime( date ); // Default time zone automatically assigned.
DateTimeZone timeZone = DateTimeZone.forID( "Asia/Kolkata" );
DateTime nowIndia = now.toDateTime( timeZone );
// For UTC/GMT, use built-in constant.
DateTime nowUtcGmt = nowIndia.toDateTime( DateTimeZone.UTC );
// Convert from Joda-Time to java.util.Date.
java.util.Date date2 = nowIndia.toDate();
转储到控制台...
System.out.println( "date: " + date );
System.out.println( "now: " + now );
System.out.println( "nowIndia: " + nowIndia );
System.out.println( "nowUtcGmt: " + nowUtcGmt );
System.out.println( "date2: " + date2 );
跑步时......
date: Sat Jan 25 16:52:28 PST 2014
now: 2014-01-25T16:52:28.003-08:00
nowIndia: 2014-01-26T06:22:28.003+05:30
nowUtcGmt: 2014-01-26T00:52:28.003Z
date2: Sat Jan 25 16:52:28 PST 2014
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
答案 1 :(得分:0)
在Google API中处理时区问题时。我遇到过这样的问题。
看看你的这段代码: -
System.out.println(getDateStringToShow(formatter.parse("26-Nov-10 03:31:20 PM
+0530"),"Asia/Calcutta", "Europe/Dublin", false));
System.out.println(getDateStringToShow(formatter.parse("02-Nov-10 10:00:00 AM
+0530"),"Asia/Calcutta", "Europe/Dublin", false));
如果我将上面的内容作为输入,它将以我们想要的方式运行。
如果您仍然想要这样做,那么您必须根据需要进行计算。
喜欢在数学上调整时间和类似的东西。
或者您的案例的简单修复将是这样的
SimpleDateFormat d =new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
d.setTimeZone(TimeZone.getTimeZone("Europe/Dublin"));
Date firsttime = d.parse("2013-12-19T03:31:20");
Date seondtime = d.parse("2013-12-19T10:00:00");
System.out.println(getDateStringToShow(firsttime,"Asia/Calcutta",
"Europe/Dublin", false));
System.out.println(getDateStringToShow(seondtime,"Asia/Calcutta",
"Europe/Dublin", false));
我的建议是提及JODA API。比旧学校日期更受欢迎。