如何在Java中获取UTC或GMT中的当前日期和时间?

How can I get the current date and time in UTC or GMT in Java?

当我创建一个新的Date对象时,它被初始化为当前时间,但在本地时区中。我怎样才能以格林尼治标准时间获取当前日期和时间?


java.util.Date没有特定的时区,尽管它的值通常被认为与UTC有关。是什么让你觉得是在当地时间?

准确地说:java.util.Date中的值是自unix epoch以来的毫秒数,它发生在1970年1月1日午夜,UTC。同样的时代也可以在其他时区中描述,但传统的描述是以UTC为单位的。由于它是一个固定纪元以来的毫秒数,因此在任何特定时刻,无论本地时区如何,java.util.Date中的值在世界各地都是相同的。

我怀疑问题是您通过使用本地时区的日历实例来显示它,或者可能使用也使用本地时区的Date.toString(),或者使用默认情况下也使用本地时区的SimpleDateFormat实例。

如果这不是问题,请发布一些示例代码。

不过,我还是建议您使用joda-time,它提供了更清晰的API。


1
2
3
4
5
6
7
8
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));

//Local time zone  
SimpleDateFormat dateFormatLocal = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");

//Time in GMT
return dateFormatLocal.parse( dateFormatGmt.format(new Date()) );


DR

1
Instant.now()   // Capture the current moment in UTC.

生成表示该值的字符串:

1
Instant.now().toString()

2016-09-13T23:30:52.123Z

细节

正如jon skeet所说的正确答案,java.util.date对象没有时区?.但是它的toString实现在生成该日期时间值的字符串表示形式时应用了JVM的默认时区。对北美很困惑?ve程序员,约会似乎有时区,但没有。

EDCOX1,1,EDCOX1,2,和EDCOX1,3个与Java捆绑的类是众所周知的麻烦。避开它们。相反,请使用以下任一有效的日期时间库:

  • Java 8中的Java.Time.*包
  • 乔达时间

JavaTime:(Java 8)

Java 8带来了一个优秀的新Java .Time.*包来取代旧的JavaUTL.DATE/日历类。

用UTC/GMT计算当前时间是一个简单的一行程序…

1
Instant instant = Instant.now();

Instant类是java.time中的基本构建块,表示UTC时间轴上的一个时刻,分辨率为纳秒。

在Java 8中,当前时刻仅以毫秒分辨率捕获。Java 9带来了一个新的Clock的实现,它捕捉到当前时刻达到这个类的纳秒级能力,这取决于主机的时钟硬件的能力。

它的toString方法使用一种特定的ISO 8601格式生成其值的字符串表示。该格式根据需要输出0、3、6或9位数字(毫秒、微秒或纳秒),以表示秒的分数。

如果您想要更灵活的格式或其他附加功能,那么对UTC本身(ZoneOffset.UTC常量)应用从UTC到0的偏移量,以获得OffsetDateTime

1
OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC );

转储到控制台…

1
System.out.println("now:" + now );

跑步时…

1
now: 2014-01-21T23:42:03.522Z

Table of types of date-time classes in modern java.time versus legacy.

java.time类由jsr 310定义。他们的灵感来自于乔达时代,但完全是重新设计的。

乔达时间

更新:现在处于维护模式的joda time项目建议迁移到java.time类。

使用Joda Time第三方免费开放源码库,您只需一行代码就可以获得当前日期时间。

JoDA时间启发了Java 8中的新Java.Time.*类,但具有不同的体系结构。您可以在JAVA的旧版本中使用JoDA时间。JoDA时间继续在Java 8中工作,并继续积极维护(如2014)。然而,Joda Time团队建议迁移到java.time。

1
System.out.println("UTC/GMT date-time in ISO 8601 format:" + new org.joda.time.DateTime( org.joda.time.DateTimeZone.UTC ) );

更详细的示例代码(Joda时间2.3)

1
2
org.joda.time.DateTime now = new org.joda.time.DateTime(); // Default time zone.
org.joda.time.DateTime zulu = now.toDateTime( org.joda.time.DateTimeZone.UTC );

转储到控制台…

1
2
System.out.println("Local time in ISO 8601 format:" + now );
System.out.println("Same moment in UTC (Zulu):" + zulu );

跑步时…

1
2
Local time in ISO 8601 format: 2014-01-21T15:34:29.933-08:00
Same moment in UTC (Zulu): 2014-01-21T23:34:29.933Z

有关执行时区工作的代码的更多示例,请参阅我对类似问题的回答。

时区

我建议您总是指定一个时区,而不是隐式地依赖于JVM的当前默认时区(随时都可以更改!)这种依赖似乎是日期时间工作中混乱和错误的常见原因。

调用now()时,通过要分配的所需/预期时区。使用DateTimeZone类。

1
2
DateTimeZone zoneMontréal = DateTimeZone.forID("America/Montreal" );
DateTime now = DateTime.now( zoneMontréal );

该类保存UTC时区的常量。

1
DateTime now = DateTime.now( DateTimeZone.UTC );

如果您真的想要使用JVM的当前默认时区,那么就进行一个显式调用,这样您的代码就可以自我记录了。

1
DateTimeZone zoneDefault = DateTimeZone.getDefault();

国际标准化组织8601

阅读ISO 8601格式。java.time和joda-time都使用该标准的合理格式作为解析和生成字符串的默认格式。

?实际上,java.util.date确实有一个时区,深深地隐藏在源代码层之下。对于大多数实际用途,该时区将被忽略。所以,简而言之,我们说java.util.date没有时区。此外,隐藏的时区不是date的toString方法使用的时区;该方法使用的是JVM当前的默认时区。更多的原因是为了避免这种混乱的类,并坚持使用joda time和java.time。


这肯定会返回UTC时间:作为字符串和日期对象!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static final String DATE_FORMAT ="yyyy-MM-dd HH:mm:ss";

public static Date getUTCdatetimeAsDate() {
    // note: doesn't check for null
    return stringDateToDate(getUTCdatetimeAsString());
}

public static String getUTCdatetimeAsString() {
    final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    final String utcTime = sdf.format(new Date());

    return utcTime;
}

public static Date stringDateToDate(String StrDate) {
    Date dateToReturn = null;
    SimpleDateFormat dateFormat = new SimpleDateFormat(DATEFORMAT);

    try {
        dateToReturn = (Date)dateFormat.parse(StrDate);
    }
    catch (ParseException e) {
        e.printStackTrace();
    }

    return dateToReturn;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    Calendar c = Calendar.getInstance();
    System.out.println("current:"+c.getTime());

    TimeZone z = c.getTimeZone();
    int offset = z.getRawOffset();
    if(z.inDaylightTime(new Date())){
        offset = offset + z.getDSTSavings();
    }
    int offsetHrs = offset / 1000 / 60 / 60;
    int offsetMins = offset / 1000 / 60 % 60;

    System.out.println("offset:" + offsetHrs);
    System.out.println("offset:" + offsetMins);

    c.add(Calendar.HOUR_OF_DAY, (-offsetHrs));
    c.add(Calendar.MINUTE, (-offsetMins));

    System.out.println("GMT Time:"+c.getTime());

实际上不是时间,但它的表示可以改变。

1
2
3
SimpleDateFormat f = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
f.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(f.format(new Date()));

地球上任何一点的时间都是一样的,但我们对时间的感知可能会因地点的不同而有所不同。


此代码打印当前时间UTC。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;


public class Test
{
    public static void main(final String[] args) throws ParseException
    {
        final SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
        f.setTimeZone(TimeZone.getTimeZone("UTC"));
        System.out.println(f.format(new Date()));
    }
}

结果

1
2013-10-26 14:37:48 UTC

Calendar aGMTCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Then all operations performed using the aGMTCalendar object will be done with the GMT time zone and will not have the daylight savings time or fixed offsets applied

错了!

1
2
Calendar aGMTCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
aGMTCalendar.getTime(); //or getTimeInMillis()

1
Calendar aNotGMTCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT-2"));aNotGMTCalendar.getTime();

将同时返回。Idem

1
new Date(); //it's not GMT.

这适用于在Android中获取UTC毫秒。

1
2
3
Calendar c = Calendar.getInstance();
int utcOffset = c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET);  
Long utcMilliseconds = c.getTimeInMillis() + utcOffset;


这是乔恩·斯基特的回答中似乎不正确的地方。他说:

java.util.Date is always in UTC. What makes you think it's in local
time? I suspect the problem is that you're displaying it via an
instance of Calendar which uses the local timezone, or possibly using
Date.toString() which also uses the local timezone.

但是,代码:

1
System.out.println(new java.util.Date().getHours() +" hours");

给出本地时间,而不是格林威治时间(UTC时间),完全不使用CalendarSimpleDateFormat

这就是为什么看起来有些不正确。

把这些答案放在一起,代码:

1
2
System.out.println(Calendar.getInstance(TimeZone.getTimeZone("GMT"))
                           .get(Calendar.HOUR_OF_DAY) +" Hours");

显示的是格林尼治标准时间而不是当地时间--请注意,缺少getTime.getHours(),因为这将创建一个Date()对象,该对象理论上以格林尼治标准时间存储日期,但返回当地时区的时间。


如果您想要一个日期对象,其字段根据UTC进行调整,您可以使用Joda时间这样做:

1
2
3
4
5
6
7
8
9
10
import org.joda.time.DateTimeZone;
import java.util.Date;

...

Date local = new Date();
System.out.println("Local:" + local);
DateTimeZone zone = DateTimeZone.getDefault();
long utc = zone.convertLocalToUTC(local.getTime(), false);
System.out.println("UTC:" + new Date(utc));


1
2
3
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd");
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(dateFormatGmt.format(date));


你可以使用:

1
Calendar aGMTCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

然后,使用agmtcalendar对象执行的所有操作都将使用GMT时区完成,并且不会应用夏令时或固定偏移量。我认为前面的海报是正确的,日期()对象总是返回一个格林威治标准时间,直到你对日期对象做一些事情,它才被转换到本地时区。


你可以直接用这个

1
2
3
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("dd:MM:yyyy HH:mm:ss");
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(dateFormatGmt.format(new Date())+"");


用:

1
Calendar cal = Calendar.getInstance();

那么,cal就有了当前的日期和时间。您还可以通过以下方式获取时区的当前日期和时间:

1
Calendar cal2 = Calendar.getInstance(TimeZone.getTimeZone("GMT-2"));

您可以向cal.get(Calendar.DATE);或其他日历常量询问其他详细信息。日期和时间戳在Java中被弃用。日历类不是。


下面是获取GMT时间戳对象的其他建议:

1
2
3
4
5
6
7
8
9
10
11
import java.sql.Timestamp;
import java.util.Calendar;

...

private static Timestamp getGMT() {
   Calendar cal = Calendar.getInstance();
   return new Timestamp(cal.getTimeInMillis()
                       -cal.get(Calendar.ZONE_OFFSET)
                       -cal.get(Calendar.DST_OFFSET));
}

这是另一种获取字符串格式的GMT时间的方法

1
2
3
4
String DATE_FORMAT ="EEE, dd MMM yyyy HH:mm:ss z" ;
final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
String dateTimeString =  sdf.format(new Date());

为了简单起见,要在UTC中创建Date,可以使用Calendar

1
Calendar.getInstance(TimeZone.getTimeZone("UTC"));

它将使用"UTC"TimeZoneCalendar构建一个新实例。

如果您需要日历中的Date对象,您可以使用getTime()


以特定时区和特定格式呈现系统时间的示例代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class TimZoneTest {
    public static void main (String[] args){
        //<GMT><+/-><hour>:<minutes>
        // Any screw up in this format, timezone defaults to GMT QUIETLY. So test your format a few times.

        System.out.println(my_time_in("GMT-5:00","MM/dd/yyyy HH:mm:ss") );
        System.out.println(my_time_in("GMT+5:30","'at' HH:mm a z 'on' MM/dd/yyyy"));

        System.out.println("---------------------------------------------");
        // Alternate format
        System.out.println(my_time_in("America/Los_Angeles","'at' HH:mm a z 'on' MM/dd/yyyy") );
        System.out.println(my_time_in("America/Buenos_Aires","'at' HH:mm a z 'on' MM/dd/yyyy") );


    }

    public static String my_time_in(String target_time_zone, String format){
        TimeZone tz = TimeZone.getTimeZone(target_time_zone);
        Date date = Calendar.getInstance().getTime();
        SimpleDateFormat date_format_gmt = new SimpleDateFormat(format);
        date_format_gmt.setTimeZone(tz);
        return date_format_gmt.format(date);
    }

}

产量

1
2
3
4
10/08/2011 21:07:21
at 07:37 AM GMT+05:30 on 10/09/2011
at 19:07 PM PDT on 10/08/2011
at 23:07 PM ART on 10/08/2011


以UTC格式转换当前日期时间:

1
2
3
4
5
6
7
8
9
10
11
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

DateTimeZone dateTimeZone = DateTimeZone.getDefault(); //Default Time Zone

DateTime currDateTime = new DateTime(); //Current DateTime

long utcTime = dateTimeZone.convertLocalToUTC(currDateTime .getMillis(), false);

String currTime = formatter.print(utcTime); //UTC time converted to string from long in format of formatter

currDateTime = formatter.parseDateTime(currTime); //Converted to DateTime in UTC


以下是我对TOUTC的实现:

1
2
3
4
5
6
    public static Date toUTC(Date date){
    long datems = date.getTime();
    long timezoneoffset = TimeZone.getDefault().getOffset(datems);
    datems -= timezoneoffset;
    return new Date(datems);
}

可能有几种方法可以改进它,但它对我有用。


这对我有效,返回格林威治时间戳!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    Date currDate;
    SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
    dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
    SimpleDateFormat dateFormatLocal = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");

    long currTime = 0;
    try {

        currDate = dateFormatLocal.parse( dateFormatGmt.format(new Date()) );
        currTime = currDate.getTime();
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

1
2
3
4
public static void main(String args[]){
    LocalDate date=LocalDate.now();  
    System.out.println("Current date ="+date);
}

使用此类从联机NTP服务器获取正确的UTC时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


class NTP_UTC_Time
{
private static final String TAG ="SntpClient";

private static final int RECEIVE_TIME_OFFSET = 32;
private static final int TRANSMIT_TIME_OFFSET = 40;
private static final int NTP_PACKET_SIZE = 48;

private static final int NTP_PORT = 123;
private static final int NTP_MODE_CLIENT = 3;
private static final int NTP_VERSION = 3;

// Number of seconds between Jan 1, 1900 and Jan 1, 1970
// 70 years plus 17 leap days
private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;

private long mNtpTime;

public boolean requestTime(String host, int timeout) {
    try {
        DatagramSocket socket = new DatagramSocket();
        socket.setSoTimeout(timeout);
        InetAddress address = InetAddress.getByName(host);
        byte[] buffer = new byte[NTP_PACKET_SIZE];
        DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);

        buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);

        writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET);

        socket.send(request);

        // read the response
        DatagramPacket response = new DatagramPacket(buffer, buffer.length);
        socket.receive(response);          
        socket.close();

        mNtpTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);            
    } catch (Exception e) {
      //  if (Config.LOGD) Log.d(TAG,"request time failed:" + e);
        return false;
    }

    return true;
}


public long getNtpTime() {
    return mNtpTime;
}


/**
 * Reads an unsigned 32 bit big endian number from the given offset in the buffer.
 */

private long read32(byte[] buffer, int offset) {
    byte b0 = buffer[offset];
    byte b1 = buffer[offset+1];
    byte b2 = buffer[offset+2];
    byte b3 = buffer[offset+3];

    // convert signed bytes to unsigned values
    int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
    int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
    int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
    int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);

    return ((long)i0 << 24) + ((long)i1 << 16) + ((long)i2 << 8) + (long)i3;
}

/**
 * Reads the NTP time stamp at the given offset in the buffer and returns
 * it as a system time (milliseconds since January 1, 1970).
 */
   
private long readTimeStamp(byte[] buffer, int offset) {
    long seconds = read32(buffer, offset);
    long fraction = read32(buffer, offset + 4);
    return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);        
}

/**
 * Writes 0 as NTP starttime stamp in the buffer. --> Then NTP returns Time OFFSET since 1900
 */
   
private void writeTimeStamp(byte[] buffer, int offset) {        
    int ofs =  offset++;

    for (int i=ofs;i<(ofs+8);i++)
      buffer[i] = (byte)(0);            
}

}

并将其用于:

1
2
3
4
5
6
7
        long now = 0;

        NTP_UTC_Time client = new NTP_UTC_Time();

        if (client.requestTime("pool.ntp.org", 2000)) {              
          now = client.getNtpTime();
        }

如果需要将UTC时间"now"作为日期时间字符串,请使用函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private String get_UTC_Datetime_from_timestamp(long timeStamp){

    try{

        Calendar cal = Calendar.getInstance();
        TimeZone tz = cal.getTimeZone();

        int tzt = tz.getOffset(System.currentTimeMillis());

        timeStamp -= tzt;

        // DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.getDefault());
        DateFormat sdf = new SimpleDateFormat();
        Date netDate = (new Date(timeStamp));
        return sdf.format(netDate);
    }
    catch(Exception ex){
        return"";
     }
    }

并将其用于:

1
String UTC_DateTime = get_UTC_Datetime_from_timestamp(now);


使用java.time包并包含以下代码-

ZonedDateTime now = ZonedDateTime.now( ZoneOffset.UTC );

LocalDateTime now2 = LocalDateTime.now( ZoneOffset.UTC );

取决于您的应用程序需要。