为了显示可重现的场景,我正在执行以下操作
获取当前系统时间(当地时间)
将当地时间转换为UTC //直到此处工作
将UTC时间反转回当地时间。遵循3种不同的方法(如下所列),但所有3种方法仅保留UTC时间。
{
long ts = System.currentTimeMillis();
Date localTime = new Date(ts);
String format = "yyyy/MM/dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat (format);
// Convert Local Time to UTC (Works Fine)
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date gmtTime = new Date(sdf.format(localTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Reverse Convert UTC Time to Locale time (Doesn't work) Approach 1
sdf.setTimeZone(TimeZone.getDefault());
localTime = new Date(sdf.format(gmtTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Reverse Convert UTC Time to Locale time (Doesn't work) Approach 2 using DateFormat
DateFormat df = new SimpleDateFormat (format);
df.setTimeZone(TimeZone.getDefault());
localTime = df.parse((df.format(gmtTime)));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Approach 3
Calendar c = new GregorianCalendar(TimeZone.getDefault());
c.setTimeInMillis(gmtTime.getTime());
System.out.println("Local Time " + c.toString());
}
答案 0 :(得分:56)
我还建议使用前面提到的 Joda 。
仅使用标准 Java Date
对象解决问题可以按如下方式完成:
// **** YOUR CODE **** BEGIN ****
long ts = System.currentTimeMillis();
Date localTime = new Date(ts);
String format = "yyyy/MM/dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(format);
// Convert Local Time to UTC (Works Fine)
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date gmtTime = new Date(sdf.format(localTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:"
+ gmtTime.toString() + "," + gmtTime.getTime());
// **** YOUR CODE **** END ****
// Convert UTC to Local Time
Date fromGmt = new Date(gmtTime.getTime() + TimeZone.getDefault().getOffset(localTime.getTime()));
System.out.println("UTC time:" + gmtTime.toString() + "," + gmtTime.getTime() + " --> Local:"
+ fromGmt.toString() + "-" + fromGmt.getTime());
输出:
Local:Tue Oct 15 12:19:40 CEST 2013,1381832380522 --> UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000
UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000 --> Local:Tue Oct 15 12:19:40 CEST 2013-1381832380000
答案 1 :(得分:4)
更新:Joda-Time项目现在位于maintenance mode,团队建议迁移到java.time课程。请参阅Tutorial by Oracle。
使用行业领先的 java.time 类查看my other Answer。
通常我们认为StackOverflow.com上的错误形式是通过建议替代技术来回答特定问题。但是对于与Java 7及更早版本捆绑在一起的日期,时间和日历类,这些类在设计和执行方面都非常糟糕,我不得不建议使用第三方库:Joda-Time
Joda-Time通过创建immutable objects来工作。因此,我们只是实例化一个分配了不同时区的新DateTime,而不是改变DateTime对象的时区。
在Joda-Time中,使用本地和UTC时间的中心问题非常简单,仅需3行代码。
org.joda.time.DateTime now = new org.joda.time.DateTime();
System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
在北美西海岸运行时的输出可能是:
Local time in ISO 8601 format: 2013-10-15T02:45:30.801-07:00
UTC (Zulu) time zone: 2013-10-15T09:45:30.801Z
这是一个有几个例子和进一步评论的课程。使用Joda-Time 2.5。
/**
* Created by Basil Bourque on 2013-10-15.
* © Basil Bourque 2013
* This source code may be used freely forever by anyone taking full responsibility for doing so.
*/
public class TimeExample {
public static void main(String[] args) {
// Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 8 and earlier.
// http://www.joda.org/joda-time/
// Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
// JSR 310 was inspired by Joda-Time but is not directly based on it.
// http://jcp.org/en/jsr/detail?id=310
// By default, Joda-Time produces strings in the standard ISO 8601 format.
// https://en.wikipedia.org/wiki/ISO_8601
// You may output to strings in other formats.
// Capture one moment in time, to be used in all the examples to follow.
org.joda.time.DateTime now = new org.joda.time.DateTime();
System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
// You may specify a time zone in either of two ways:
// • Using identifiers bundled with Joda-Time
// • Using identifiers bundled with Java via its TimeZone class
// ----| Joda-Time Zones |---------------------------------
// Time zone identifiers defined by Joda-Time…
System.out.println( "Time zones defined in Joda-Time : " + java.util.Arrays.toString( org.joda.time.DateTimeZone.getAvailableIDs().toArray() ) );
// Specify a time zone using DateTimeZone objects from Joda-Time.
// http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeZone.html
org.joda.time.DateTimeZone parisDateTimeZone = org.joda.time.DateTimeZone.forID( "Europe/Paris" );
System.out.println( "Paris France (Joda-Time zone): " + now.toDateTime( parisDateTimeZone ) );
// ----| Java Zones |---------------------------------
// Time zone identifiers defined by Java…
System.out.println( "Time zones defined within Java : " + java.util.Arrays.toString( java.util.TimeZone.getAvailableIDs() ) );
// Specify a time zone using TimeZone objects built into Java.
// http://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html
java.util.TimeZone parisTimeZone = java.util.TimeZone.getTimeZone( "Europe/Paris" );
System.out.println( "Paris France (Java zone): " + now.toDateTime(org.joda.time.DateTimeZone.forTimeZone( parisTimeZone ) ) );
}
}
答案 2 :(得分:2)
您的日期包含已知时区(此处为Europe/Madrid
)和目标时区(UTC
)
你只需要两个SimpleDateFormats:
long ts = System.currentTimeMillis(); Date localTime = new Date(ts); SimpleDateFormat sdfLocal = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss"); sdfLocal.setTimeZone(TimeZone.getTimeZone("Europe/Madrid")); SimpleDateFormat sdfUTC = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss"); sdfUTC.setTimeZone(TimeZone.getTimeZone("UTC")); // Convert Local Time to UTC Date utcTime = sdfLocal.parse(sdfUTC.format(localTime)); System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + utcTime.toString() + "-" + utcTime.getTime()); // Reverse Convert UTC Time to Locale time localTime = sdfUTC.parse(sdfLocal.format(utcTime)); System.out.println("UTC:" + utcTime.toString() + "," + utcTime.getTime() + " --> Local time:" + localTime.toString() + "-" + localTime.getTime());
因此,看到它工作后,您可以将此方法添加到您的utils:
public Date convertDate(Date dateFrom, String fromTimeZone, String toTimeZone) throws ParseException { String pattern = "yyyy/MM/dd HH:mm:ss"; SimpleDateFormat sdfFrom = new SimpleDateFormat (pattern); sdfFrom.setTimeZone(TimeZone.getTimeZone(fromTimeZone)); SimpleDateFormat sdfTo = new SimpleDateFormat (pattern); sdfTo.setTimeZone(TimeZone.getTimeZone(toTimeZone)); Date dateTo = sdfFrom.parse(sdfTo.format(dateFrom)); return dateTo; }
答案 3 :(得分:2)
我正在加入合唱团,建议您跳过现在已久的过时课程Date
,Calendar
,SimpleDateFormat
和朋友。特别是我会警告不要使用Date
类的已弃用方法和构造函数,例如您使用的Date(String)
构造函数。它们被弃用,因为它们不能跨时区可靠地工作,所以不要使用它们。是的,该类的大多数构造函数和方法都已弃用。
当你问这个问题时,Joda-Time(据我所知)是一个明显更好的选择,时间再次开始。今天Joda-Time是一个基本完成的项目,它的开发人员建议你使用现代Java日期和时间API java.time
。我会告诉你如何。
ZonedDateTime localTime = ZonedDateTime.now(ZoneId.systemDefault());
// Convert Local Time to UTC
OffsetDateTime gmtTime
= localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
System.out.println("Local:" + localTime.toString()
+ " --> UTC time:" + gmtTime.toString());
// Reverse Convert UTC Time to Local time
localTime = gmtTime.atZoneSameInstant(ZoneId.systemDefault());
System.out.println("Local Time " + localTime.toString());
对于初学者来说,请注意,代码不仅只是你的代码的一半,读起来也更清晰。
在我的电脑上打印代码:
Local:2017-09-02T07:25:46.211+02:00[Europe/Berlin] --> UTC time:2017-09-02T05:25:46.211Z
Local Time 2017-09-02T07:25:46.211+02:00[Europe/Berlin]
我遗漏了纪元的毫秒数。您可以随时从问题中System.currentTimeMillis();
获取它们,并且它们与时区无关,因此我没有发现它们在此处。
我犹豫地保留了你的变量名localTime
。我认为这是一个好名字。现代API有一个名为LocalTime
的类,因此对于没有类型为LocalTime
的对象,使用该名称时,只是不大写,可能会混淆一些(LocalTime
不成立时区信息,我们需要保留在这里才能进行正确的转换;它还只保存时间,而不是日期。
您从当地时间到UTC的转换不正确且不可能
过时的Date
类不保存任何时区信息(您可能会说它在内部始终使用UTC),因此没有将Date
从一个时区转换为另一个。当我在计算机上运行代码时,它打印的第一行是:
Local:Sat Sep 02 07:25:45 CEST 2017,1504329945967 --> UTC time:Sat Sep 02 05:25:45 CEST 2017-1504322745000
当然, 07:25:45 CEST
是正确的。正确的UTC时间应为05:25:45 UTC
,但它再次显示CEST
,这是不正确的。
现在你永远不会再需要Date
课程,:-)但是如果你要去的话,那就必须在Jon Skeet的编码博客上阅读All about java.util.Date。
问题:我可以在Java版本中使用现代API吗?
如果至少使用Java 6 ,则可以。
答案 4 :(得分:2)
Instant.now() // Capture the current moment in UTC.
.atZone( ZoneId.systemDefault() ) // Adjust into the JVM's current default time zone. Same moment, different wall-clock time. Produces a `ZonedDateTime` object.
.toInstant() // Extract a `Instant` (always in UTC) object from the `ZonedDateTime` object.
.atZone( ZoneId.of( "Europe/Paris" ) ) // Adjust the `Instant` into a specific time zone. Renders a `ZonedDateTime` object. Same moment, different wall-clock time.
.toInstant() // And back to UTC again.
现代方法使用 java.time 类,这些类取代了麻烦的旧遗留日期时间类(Date
,Calendar
等。)。
您对“local”一词的使用与 java.time 类中的用法相矛盾。在 java.time 中,“local”表示任何位置或所有位置,但不是任何一个特定位置。名称以“Local ...”开头的 java.time 类都缺少时区或从UTC偏移的概念。所以他们不代表一个特定的时刻,他们不时间轴上的一个点,而你的问题是关于时刻,通过各种挂钟查看时间轴上的点次。
获取当前系统时间(当地时间)
如果要捕获UTC中的当前时刻,请使用Instant
。 Instant
类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位)。
Instant instant = Instant.now() ; // Capture the current moment in UTC.
通过应用ZoneId
来调整时区以获得ZonedDateTime
。同一时刻,时间轴上的同一点,不同的挂钟时间。
以continent/region
的格式指定proper time zone name,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, different wall-clock time.
作为一种快捷方式,您可以跳过使用Instant
获取ZonedDateTime
。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
将本地时间转换为UTC //在这里工作正常
您可以从Instant
中提取ZonedDateTime
,从分区日期时间调整为UTC。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Instant instant = zdt.toInstant() ;
将UTC时间反转回当地时间。
如上所示,应用ZoneId
将同一时刻调整为特定区域(时区)的人使用的另一个挂钟时间。
Instant instant = Instant.now() ; // Capture current moment in UTC.
ZoneId zDefault = ZoneId.systemDefault() ; // The JVM's current default time zone.
ZonedDateTime zdtDefault = instant.atZone( zDefault ) ;
ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ; // The JVM's current default time zone.
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;
ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ; // The JVM's current default time zone.
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;
从分区日期时间返回UTC,请致电ZonedDateTime::toInstant
。从概念上考虑它: ZonedDateTime = Instant + ZoneId 。
Instant instant = zdtAuckland.toInstant() ;
所有这些对象,Instant
和三个ZonedDateTime
对象都代表同一时刻,即历史中的同一时刻。
遵循3种不同的方法(如下所列),但所有3种方法仅保留UTC时间。
忘记尝试使用那些糟糕的Date
,Calendar
和GregorianCalendar
类来修复代码。他们是一个糟糕的设计和缺陷的可怜混乱。你永远不需要再碰他们了。如果必须与尚未更新为 java.time 的旧代码连接,则可以通过添加到旧类的新转换方法来回转换。
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。
答案 5 :(得分:1)
我强烈建议使用Joda Time http://joda-time.sourceforge.net/faq.html