在Android上将UTC转换为本地时间

75

在我的项目中,我已经以json格式获取了API响应。我获得了一个UTC时间格式的字符串值,如下所示:Jul 16, 2013 12:08:59 AM
我需要将其转换为本地时间。 无论何时使用该应用程序,它都需要显示本地时间。我该怎么做呢?

这是我尝试过的一些代码:

String aDate = getValue("dateTime", aEventJson);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss z");
simpleDateFormat.setTimeZone(TimeZone.getDefault());
String formattedDate = simpleDateFormat.format(aDate);
假设aDate包含Jul 16, 2013 12:08:59 AM

看起来不错啊。有什么问题吗? - Blackbelt
我遇到了异常 java.text.ParseException: Unparseable date: "Jul 16, 2013 4:23:37 AM" (at offset 21) - MadTech
4
AM/PM标记不是z。z代表时区。 - Blackbelt
8个回答

136

这是我的尝试:

String dateStr = "Jul 16, 2013 12:08:59 AM";
SimpleDateFormat df = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss a", Locale.ENGLISH);
df.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = df.parse(dateStr);
df.setTimeZone(TimeZone.getDefault());
String formattedDate = df.format(date);

还要注意 "a" 代表上午/下午标记...


2
我遇到了异常 java.text.ParseException: Unparseable date: "Jul 16, 2013 4:23:37 AM" (at offset 21) - MadTech
这条评论是针对这个答案的吗? - devconsole
我之前尝试过这段相同的代码,但在这一行Date date = df.parse(dateStr);中出现了异常。 - MadTech
1
别忘了使用“a”(上午/下午标记)而不是“z”(时区)。 - devconsole
1
@devconsole,将格式化为字符串完全没有问题,但不需要设置默认时区。因此,可以省略以下行,结果仍将保持不变:df.setTimeZone(TimeZone.getDefault()); - Rikin Prajapati
显示剩余8条评论

14

我想提供一个现代的答案。虽然在2013年(除了Joda-Time),我们用来解析和格式化日期时间的类是SimpleDateFormat,但它现在已经过时了,我们有更好的选择,如java.time或JSR-310,这是一个现代的Java日期和时间API,它于2014年随Java 8一同发布。

但是,大多数Android设备仍然不支持Java 8,你可能会说。幸运的是,你仍然可以通过ThreeTenABP,在Android Java 7上使用现代的Java日期和时间API。有关详细信息,请参见此问题:如何在Android项目中使用ThreeTenABP

现在的代码是:

    DateTimeFormatter formatter 
            = DateTimeFormatter.ofPattern("MMM dd, uuuu hh:mm:ss a", Locale.ENGLISH);
    String aDate = "Jul 16, 2013 12:08:59 AM";
    String formattedDate = LocalDateTime.parse(aDate, formatter)
            .atOffset(ZoneOffset.UTC)
            .atZoneSameInstant(ZoneId.systemDefault())
            .format(formatter);
    System.out.println(formattedDate);

由于我的计算机运行在欧洲/哥本哈根时区,7月份比协调世界时提前2小时,因此这将打印:

Jul 16, 2013 02:08:59 AM

进一步说明:

  • 由于您的字符串中包含AM,我假设您的小时数在上午1点至12点之间。为了正确解析和格式化它们,您需要在格式模式字符串中使用小写的h。大写的H适用于从0到23的日历时间。
  • 最好给格式化程序(无论是SimpleDateFormat还是DateTimeFormatter)指定一个显式区域设置。如果没有指定区域设置,则格式化程序将使用设备的默认区域设置。 “Jul”和“AM”都是英文,而您的代码可能在许多设备上运行良好,直到有一天在非英语区域设置的设备上运行时崩溃,并且您很难找出原因。
  • 如果可以,请明确指定所需的时区,例如ZoneId.of("Asia/Kolkata")。 JVM的默认时区可能会被程序的其他部分或在同一JVM中运行的其他程序更改,因此不可靠。

1
这是 Android API 级别 26 的最低要求。 - Boy
1
不一定,@Boy。我在答案中提到的 ThreeTenABP 适用于 API 级别低于 26 的情况。 - Ole V.V.
啊,好的!我选择了这个答案:https://dev59.com/tWUq5IYBdhLWcg3wBL7j#15666678 - Boy
对不起,@Boy,那个答案是不正确的。自纪元以来的毫秒总是自纪元以来。调整它们的时区和/或夏令时(DST)是没有意义的。如果您的代码最终给出了正确的结果,它仍然走了错误的路,将来的读者肯定会感到混乱。当然这是你的选择。 - Ole V.V.
啊,抱歉,我想我的情况不同。我有一个数字,比如说7,它代表本地时间(小时)。然后我需要加减小时数来得到UTC时间。这应该是对的吧? - Boy
1
嗯,@Boy,听起来不太错。为了确保正确,您应该知道哪一天是当地时间07:00,因为偏移量在历史上可能会发生变化,而且不仅仅是夏令时。例如,在这个问题中有一个启发性的讨论。 - Ole V.V.

8

1.本地时间转UTC时间转换器

public static String localToUTC(String dateFormat, String datesToConvert) {


        String dateToReturn = datesToConvert;

        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        sdf.setTimeZone(TimeZone.getDefault());
        Date gmt = null;

        SimpleDateFormat sdfOutPutToSend = new SimpleDateFormat(dateFormat);
        sdfOutPutToSend.setTimeZone(TimeZone.getTimeZone("UTC"));

        try {

            gmt = sdf.parse(datesToConvert);
            dateToReturn = sdfOutPutToSend.format(gmt);

        } catch (ParseException e) {
            e.printStackTrace();
        }
        return dateToReturn;
    }

2. UTC转本地时间转换器

public static String uTCToLocal(String dateFormatInPut, String dateFomratOutPut, String datesToConvert) {


    String dateToReturn = datesToConvert;

    SimpleDateFormat sdf = new SimpleDateFormat(dateFormatInPut);
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

    Date gmt = null;

    SimpleDateFormat sdfOutPutToSend = new SimpleDateFormat(dateFomratOutPut);
    sdfOutPutToSend.setTimeZone(TimeZone.getDefault());

    try {

        gmt = sdf.parse(datesToConvert);
        dateToReturn = sdfOutPutToSend.format(gmt);

    } catch (ParseException e) {
        e.printStackTrace();
    }
    return dateToReturn; }

3
我看不到你对问题的贡献,因为已经有4个现有答案中的一个或多个已经回答了这个问题。另外,在2018年,请不要教年轻人使用过时且出了名的麻烦的SimpleDateFormat类。至少不是作为第一选择。而且不应该没有任何保留地使用。今天我们有更好的选择,java.time,现代Java日期和时间API - Ole V.V.
1
我同意Ole V.V.的观点,不要使用SimpleDateFormat。 - joehinkle11

7
//your UTC time var
long time = UTCtime;

//convert it
Time timeFormat = new Time();
timeFormat.set(time+TimeZone.getDefault().getOffset(time));

//use the value
long localTime = timeFormat.toMillis(true);

我喜欢你的答案,因为它比其他答案更短更简洁。 - RanSh

3
请使用以下代码。
TimeZone defaultTimeZone = TimeZone.getDefault();
String strDefaultTimeZone = defaultTimeZone.getDisplayName(false, TimeZone.SHORT);

//The code you use
String aDate = getValue("dateTime", aEventJson);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss z");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone(strDefaultTimeZone));
String formattedDate = simpleDateFormat.format(aDate);

这应该可以运作。

我遇到了异常 java.text.ParseException: Unparseable date: "Jul 16, 2013 4:23:37 AM" (at offset 21) - MadTech
我收到了 java.lang.IllegalArgumentException: Cannot format given Object as a Date - Ole V.V.
请分享您尝试格式化的日期值,因为仅当输入提供了非正确日期时,才会出现此错误。 - Salman Khakwani

1

我在 Android 平台中这样做,如果 Build.VERSION.SDK_INT 小于 26:

int offset = TimeZone.getDefault().getRawOffset();
String str_date='20:30 12-01-2021';
DateFormat formatter = new SimpleDateFormat("HH:mm dd-MM-yyy",Locale.US);
Date date = formatter.parse(str_date);
long utcTime = date.getTime() + (3600000*3);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm dd/MM/yyyy", Locale.US);
String dateStr = sdf.format(utcTime + offset);
System.out.println(dateStr);

由于我的服务器发送的时间带有-3时区,因此我必须在getTime中添加(3600*3),并将其保存到utcTime中,以便utcTime处于UTC。然后,我将当前手机时区的偏移量添加到utcTime中。 在我的情况下,由于我的时区是-3,它打印出:
20:30 12/01/2021

但是如果我改变时区,日期也会改变。


0

我想将UTC时间戳转换为本地时间戳进行一些计算。 这是我所做的。

  val refreshed = Prefs.getLong(requireContext(), "refreshed_time")
  val calendar = Calendar.getInstance()
  calendar.timeInMillis = refreshed
  calendar.timeZone = TimeZone.getDefault()

  val localTimestamp = calendar.timeInMillis

0
请使用以下代码:
public static String stringDateWithTimezone(Date date, String pattern, TimeZone timeZone) {
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, Locale.US);
            if (timeZone != null) {
                simpleDateFormat.setTimeZone(timeZone);
            }
            return simpleDateFormat.format(date);
        } catch (Exception e) {
            Timber.e(e);
            return null;
        }
    }

在另一个类中调用:

String dateUtc = DateUtil.stringDateWithTimezone(new Date(), "yyyy-MM-dd hh:mm:ss", TimeZone.getTimeZone("UTC"));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接