【问题标题】:How do I convert local time zone to utc and utc local time zone?如何将本地时区转换为 utc 和 utc 本地时区?
【发布时间】:2017-10-11 03:42:12
【问题描述】:

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

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

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

  1. 从数据库中获取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;
    }
}

【问题讨论】:

标签: java datetime utc datetime-conversion


【解决方案1】:

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

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

  • 在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.printlnDate,或者记录它,或者在调试器中查看它的值,你会看到类似:

2017 年 10 月 12 日星期三 12:33:99 IST

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

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


那该怎么办?

如果数据库字段是 date 类型,则 JDBC 驱动程序通常会处理它,因此您只需直接保存 Date 对象即可。无需关心格式和时区,因为Date 没有格式,也没有任何时区信息。

日期类型的问题是任何语言/数据库/供应商/应用程序都以不同的方式显示它。 Java 的 Date.toString() 将其转换为 JVM 默认时区,一些数据库以特定格式(dd/mm/yyyy 或 yyyy-mm-dd 等)显示并转换为数据库中配置的任何时区,等等。

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

所以,如果你在处理Date对象,数据库字段是一个与日期相关的类型,它返回并保存Date对象,直接使用就行了。


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

打印日期(或将其转换为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(),它会返回时间戳值,它仍然是1507722750315。我只是更改了此日期的表示(格式和时区)。但Date 本身的值不变。

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

您可以致电TimeZone.getAvailableIDs() 获取可用时区列表(并选择最适合您系统的时区)。

你也可以使用系统的默认时区TimeZone.getDefault(),但是这个can be changed without notice, even at runtime,所以最好明确地使用一个特定的。


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

在我所做的测试中,09/10/2017 被解释为“月/日/年”,所以我将使用相同的。我也在考虑输入是 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 日th 21:53:17,UTC。如果输入在另一个时区,只需将“UTC”更改为相应的时区名称即可。

Check the javadoc 用于SimpleDateFormat 使用的所有可能格式。


Java 新的日期/时间 API

旧的类(DateCalendarSimpleDateFormat)有 lots of problemsdesign issues,它们正在被新的 API 取代。

如果您使用的是 Java 8,请考虑使用 new java.time API。更简单,less bugged and less error-prone than the old APIs

如果您使用的是 Java 6 或 7,则可以使用 ThreeTen Backport,这是 Java 8 新日期/时间类的一个很好的向后移植。对于Android,您还需要ThreeTenABP(详细了解如何使用它here)。

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

最新的 JDBC 驱动程序已经支持新的 Java 8 类型(检查您是否已经支持)。但如果您仍然需要使用java.util.Date,您可以轻松地从/转换到新的 API。

在 Java 8 中,Date 具有新的 fromtoInstant 方法,用于从/转换为新的 java.time.Instant 类:

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

在 ThreeTen Backport 中,您可以使用 org.threeten.bp.DateTimeUtils 类从/到 org.threeten.bp.Instant 进行转换:

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

通过Instant,您可以轻松地转换为您想要的任何时区(使用ZoneId 类将其转换为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 使用的格式并不总是相同)。

【讨论】:

  • timestamp 比较含糊,不同的意思是各种计算情况。我建议将纪元参考日期中的秒数/毫秒数等称为“从纪元计数”。
  • 这是一个很棒的答案,尤其是关于打印日期的部分。
【解决方案2】:

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

}

【讨论】:

  • 感谢您的回复。 return gmt 和 return local 显示如下 wed oct 12 12:33:99 ist 2017 但响应值想要存储在 db 我想要响应 2017-10-12 12:54:54
猜你喜欢
  • 2011-09-16
  • 2019-05-31
  • 2014-08-12
  • 1970-01-01
  • 2014-10-05
  • 2014-08-18
  • 2010-11-19
  • 2013-08-04
  • 1970-01-01
相关资源
最近更新 更多