如何将本地时区转换为 utc 和 utc 本地时区?

How do I convert local time zone to utc and utc local time zone?

我想在数据库中存储 UTC 时间(yyyy-mm-dddd-mm-yyyy

  1. 我想将本地时区转换为 UTC 并将其存储在数据库中

我尝试了不同的方法,但都不能正常工作。下面是我将本地转换为 UTC

的代码
  1. 从 DB 获取 UTC 时间并转换为本地时区

下面是我的代码:

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class TestDate {

    public static void main(String[] args) {                
        /*Date de = new Date();
        DateFormat converter1 = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
        System.err.println(converter1.format(de));
        Date der =*/
                localToGMT();
//          System.err.println("0000 : "+converter1.format(der));
        Date d=  new Date("09/10/2017 21:53:17");
        gmttoLocalDate(d);
    }

    public static Date localToGMT() {
//      Date gmt = new Date(sdf.format(date));
        Date gmt = null;
        Date localTime =  new Date();
         //creating DateFormat for converting time from local timezone to GMT
         DateFormat converter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");

         //getting GMT timezone, you can get any timezone e.g. UTC
         converter.setTimeZone(TimeZone.getTimeZone("GMT"));
         System.out.println("local time : " + localTime);;
         System.out.println("time in GMT : " + converter.format(localTime));

         try {
            gmt =converter.parse((converter.format(localTime)));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         /*Date date = new Date("2017/10/10 12:04:28");
         System.err.println(date);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        gmt = sdf.parse(sdf.format(date));*/
        System.err.println("converted to utc :" + gmt);
        return gmt;
    }

    public static Date gmttoLocalDate(Date date) {
        String timeZone = Calendar.getInstance().getTimeZone().getID();
        Date local = new Date(date.getTime() + TimeZone.getTimeZone(timeZone).getOffset(date.getTime()));
        System.err.println("converted to local timezone :" + local);
        return local;
    }
}

UTC 本地

 public static Date localToUTC() {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date gmt = new Date(sdf.format(date));
    return gmt;
    }

UTC 到本地

public static Date utctoLocalDate(Date date) {

String timeZone = Calendar.getInstance().getTimeZone().getID();
Date local = new Date(date.getTime() + TimeZone.getTimeZone(timeZone).getOffset(date.getTime()));
return local

}

一个java.util.Datehas no timezone information。它只有一个 long 值,这是自 unix 纪元(即 1970-01-01T00:00:00ZJanuary 1st 1970 午夜以来的毫秒数在 UTC)。 这个值(自纪元以来的毫秒数)有,我简称为timestamp

时间戳是一个“绝对”值,全世界都一样。
例子:我刚得到当前时间(System.currentTimeMillis()),结果是1507722750315(这是自纪元以来的当前毫秒数)。这个价值对世界上的每个人都是一样的。但是这个相同的值可以对应每个时区的不同date/time:

  • 在UTC中对应2017-10-11T11:52:30.315Z
  • 在圣保罗(巴西),2017-10-11T08:52:30.315-03:00
  • 在伦敦,是 2017-10-11T12:52:30.315+01:00
  • 在东京,2017-10-11T20:52:30.315+09:00
  • 在印度,是 2017-10-11T17:22:30.315+05:30
  • 在奥克兰,2017-10-12T00:52:30.315+13:00(那里已经“明天”了)
  • 等等...

因此,当您执行接收 java.util.Date 并尝试将其转换为另一个 java.util.Date 的方法时,您没有转换任何东西。 Date 仅包含时间戳值(对应于特定时间点的“绝对”值)。但是Date本身对时区一无所知,所以做这样的转换是没有意义的(全世界的时间戳都是一样的,不需要转换)。


“但是当我打印日期时,我看到的是本地时间的值”

嗯,这个就解释好了in this article。如果你 System.out.println 一个 Date,或者记录它,或者在调试器中查看它的值,你会看到类似的东西:

Wed Oct 12 12:33:99 IST 2017

但那是因为隐式调用了 toString() 方法,并且此方法将时间戳值转换为 JVM 默认时区(因此它看起来像“本地 date/time”)。

但是请记住,这个 Wed Oct etc 字符串 而不是 Date 对象持有的实际值:它所拥有的只是对应于该对象的时间戳date/time 显示。但是日期对象本身没有任何本地 date/time 的概念,也没有任何时区相关信息。您看到的只是时间戳值的表示,以特定格式,针对特定时区。


那怎么办?

如果数据库字段是date类型,那么JDBC驱动一般会处理,所以直接保存Date对象即可。无需关心格式和时区,因为 Date 没有格式,也没有任何时区信息。

日期类型的问题在于任何 language/database/vendor/application 都以不同的方式显示它。 Java 的 Date.toString() 将其转换为 JVM 默认时区,一些数据库以特定格式显示(dd/mm/yyyy,或 yyyy-mm-dd 等)并转换为在中配置的任何时区数据库等。

但请记住,日期本身没有格式。它只有一个值,问题是相同的日期可以 表示 不同的格式。示例:2017 年 10 月 15 日 可以表示为 2017-10-1510/15/201715th Oct 201715 de Outubro de 2017(在 pt_BR(葡萄牙语)语言环境)等等。格式(表示)不同,但Date 相同

所以,如果你正在处理 Date 对象,数据库字段是与日期相关的类型,它 returns 并保存 Date 对象,只需使用它直接。


如果你想显示这个日期,但是,(显示给用户,或记录它),或者如果数据库字段是String(或varchar,或任何其他与文本相关的类型),那就完全不同了。

打印日期(或将其转换为 String)时,您可以使用 SimpleDateFormat:

选择要转换的格式和时区
// a java.util.Date with the same timestamp above (1507722750315)
Date date = new Date(1507722750315L);
// choose a format
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// choose a timezone to convert to
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
// convert to a String
String formattedDate = sdf.format(date);
System.out.println(formattedDate); // 2017-10-11 17:22:30

在这种情况下,输出是:

2017-10-11 17:22:30

如果我将时区更改为另一个时区,结果将转换为:

sdf.setTimeZone(TimeZone.getTimeZone("Europe/London"));
// convert to a String
String formattedDate = sdf.format(date);
System.out.println(formattedDate); // 2017-10-11 12:52:30

现在的结果是:

2017-10-11 12:52:30

但是 Date 值仍然相同:如果您调用 date.getTime(),它将 return 时间戳值,它仍然是 1507722750315。我刚刚更改了这个日期的表示(格式和时区)。但是 Date 本身的值没有改变。

另请注意,我使用了 IANA timezones names(始终采用 Region/City 格式,例如 Asia/KolkataEurope/Berlin)。 避免使用 3 个字母的缩写(如 ISTCET),因为它们是 ambiguous and not standard.

您可以通过调用 TimeZone.getAvailableIDs().

获取可用时区列表(并选择最适合您的系统的时区)

您也可以使用系统的默认时区TimeZone.getDefault(),但是这个,所以最好明确使用一个特定的时区。


要从 String 获得 Date,请不要使用已弃用的构造函数 (new Date("09/10/2017 21:53:17"))。最好使用 SimpleDateFormat 并设置此日期所指的时区。

在我所做的测试中,09/10/2017 被解释为“month/day/year”,所以我将使用相同的值。我也在考虑输入是 UTC,但您可以将其更改为您需要的任何时区:

String s = "09/10/2017 21:53:17";
SimpleDateFormat parser = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
// the input is in UTC (change it to the timezone you need)
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
Date d = parser.parse(s);

以上日期相当于 2017 年 9 月 10 日,在 UTC 的 21:53:17。如果输入的是其他时区,只需将“UTC”更改为相应的时区名称即可。

Check the javadoc SimpleDateFormat.

使用的所有可能格式

Java新Date/TimeAPI

旧的 classes(DateCalendarSimpleDateFormat)有 lots of problems and design issues,它们正在被新的 APIs.

如果您使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.

如果您使用 Java 6 或 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it ).

下面的代码适用于两者。 唯一的区别是包名称(在 Java 8 中是 java.time,在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但是 classes和方法名称是一样的。

最新的 JDBC 驱动程序已经支持新的 Java 8 种类型(检查您的驱动程序是否已经支持)。但是如果你还需要使用java.util.Date,你可以很方便的将from/to转换成新的API.

在Java8中,Date有新的fromtoInstant方法,将from/to转换为新的java.time.Instantclass:

Date date = Date.from(instant);
Instant instant = date.toInstant();

在 ThreeTen Backport 中,您可以使用 org.threeten.bp.DateTimeUtils class 转换 from/to org.threeten.bp.Instant:

Date date = DateTimeUtils.toDate(instant);
Instant instant = DateTimeUtils.toInstant(date);

通过 Instant,您可以轻松转换为您想要的任何时区(使用 ZoneId class 将其转换为 ZonedDateTime),然后使用 DateTimeFormatter 更改格式:

// convert Date to Instant, and then to a timezone
ZonedDateTime z = date.toInstant().atZone(ZoneId.of("Asia/Kolkata"));
// format it
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = fmt.format(z);
System.out.println(formatted); // 2017-10-11 17:22:30

解析一个String是相似的。您使用 DateTimeFormatter,然后解析为 LocalDateTime(因为输入 String 中没有时区),然后将其转换为时区(您可以选择转换如果你需要,它到 Date):

String input = "09/10/2017 21:53:17";
DateTimeFormatter parser = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss");
// parse the input
LocalDateTime dt = LocalDateTime.parse(input, parser);
// convert to a timezone
ZonedDateTime z = dt.atZone(ZoneId.of("Asia/Kolkata"));
// you can format it, just as above

// you can also convert it to a Date
Date d = Date.from(z.toInstant());

如果要转换为 UTC,请使用常量 ZoneOffset.UTC 而不是 ZoneId。此外,check the javadoc 有关格式的更多详细信息(它们并不总是与 SimpleDateFormat 使用的相同)。