将UTC时间转换为当前时区时间

39

我正在从一个webservice下载一些JSON数据。在这个JSON中,我有一些日期/时间值。所有时间都是按照UTC计算的。

我该如何解析这个日期字符串,使得结果Date对象符合当前的语言环境?

例如:服务器返回了“2011-05-18 16:35:01”,我的设备应该显示为“2011-05-18 18:35:01”(GMT +2)。

我的当前代码:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date myDate = simpleDateFormat.parse(rawQuestion.getString("AskDateTime"));

3
FYI:Locale与时区无关。Locale主要用于生成两种形式的日期时间字符串表示:(a) 确定用于本地化月份名称等的人类语言,以及 (b) 确定用于决定缩写、大写、标点符号等方面的文化习惯。而时区是完全独立的问题。例如,你可能有一个在印度的加拿大用户,因此你需要使用Locale.CANADA_FRENCH和印度时区 ZoneId.of("Asia/Kolkata") - Basil Bourque
7个回答

90

它有一个设置时区的方法:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date myDate = simpleDateFormat.parse(rawQuestion.getString("AskDateTime"));

全部完成!


8
可以,请问“rawQuestion”和“AskDateTime”是什么意思? "rawQuestion"指的是未经处理或编辑的问题文本,而"AskDateTime"是提问的日期和时间。 - Bhavin Patel
FYI,此答案使用了现已被java.time类替代的老旧日期时间类。请参见现代答案 - Basil Bourque

20

所以你想要告诉SimpleDateFormat你使用UTC时区:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone utcZone = TimeZone.getTimeZone("UTC");
simpleDateFormat.setTimeZone(utcZone);
Date myDate = simpleDateFormat.parse(rawQuestion.getString("AskDateTime"));

显示:

simpleDateFormat.setTimeZone(TimeZone.getDefault());
String formattedDate = simpleDateFormat.format(myDate);

1
添加本地化,即SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); - Raj Karekar

3
public static String toLocalDateString(String utcTimeStamp) {
    Date utcDate = new Date(utcTimeStamp);
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    df.setTimeZone(TimeZone.getTimeZone("PST"));
    return df.format(utcDate);
}

干杯!


2

java.time

我想提供现代的答案。其他大部分答案都是正确的,也是2011年的好答案。今天,SimpleDateFormat早已过时,并且总是带来一些意外情况。而今我们有更好的选择:即Java日期和时间API的现代版本,也称为JSR-310。java.time。这个答案适用于任何接受外部依赖项(只需等待java.time到达您的Android设备)或已经使用Java 8或更高版本的人。

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
    String dateTimeFromServer = "2011-05-18 16:35:01";
    String localTimeToDisplay = LocalDateTime.parse(dateTimeFromServer, formatter)
            .atOffset(ZoneOffset.UTC)
            .atZoneSameInstant(ZoneId.of("Europe/Zurich"))
            .format(formatter);

这段代码的结果符合要求:
2011-05-18 18:35:01

如果可以,请提供明确的时区

我已经提供了欧洲/苏黎世的明确时区。请将您所需的本地时区替换为此时区。要依赖您设备的时区设置,请使用ZoneId.systemDefault()。但是,请注意,这种方法很脆弱,因为它会从JVM中获取时区设置,并且该设置可能会被程序的其他部分或在同一JVM中运行的其他程序更改。

在Android上使用java.time

我向您承诺一个外部依赖项。要在Android上使用上述内容,您需要ThreeTenABP,即JSR-310的后移植到Java 6和7的ThreeTen Backport的Android版本。如何操作已在此问题:如何在Android项目中使用ThreeTenABP中得到详细而完整的解释。


无法使用此功能。调用需要API 26。 - Zeeshan
1
@ShanXeeshi 如果要在Android中使用,您需要像@Ole V.V.所说的那样使用ThreeTenABP(JSR-310的后备)。添加此依赖项implementation 'com.jakewharton.threetenabp:threetenabp:1.2.1',然后您就可以使用它了! - Ryan Amaral
1
已经两年了 ;) 我不记得当时如何修复本地时间转换问题。但是你的评论会帮助其他人。 - Zeeshan

1

你的字符串 myUTC = "2016-02-03T06:41:33.000Z"


SimpleDateFormat existingUTCFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
SimpleDateFormat requiredFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

try{
Date getDate = existingFormat.parse(myUTC);
String mydate = requiredFormat.format(getDate)
}
catch(ParseException){
}

0
如果您的输入是时间戳(Long)而不是UTC时间,由于这是在Android上,您可以执行以下操作:
fun DateFormat.formatUtcEpochSecond(epochSecond: Long): String = format(CalendarEx.getFromEpochSecond(epochSecond).time)

fun DateFormat.formatUtcEpochMs(epochMilli: Long): String = format(CalendarEx.getFromEpochMilli(epochMilli).time)

CalendarEx.kt

object CalendarEx {

    @JvmStatic
    fun getFromEpochMilli(epochMilli: Long): Calendar {
        val cal = Calendar.getInstance()
        cal.timeInMillis = epochMilli
        return cal
    }

    @JvmStatic
    fun getFromEpochSecond(epochSecond: Long): Calendar {
        val cal = Calendar.getInstance()
        cal.timeInMillis = epochSecond * 1000L
        return cal
    }
}

DateHelper.kt

/**
 * gets the default date formatter of the device
 */
@JvmStatic
fun getFormatDateUsingDeviceSettings(context: Context): java.text.DateFormat {
    return DateFormat.getDateFormat(context) ?: SimpleDateFormat("yyyy/MM/dd", Locale.getDefault())
}

/**
 * gets the default time formatter of the device
 */
@JvmStatic
fun getFormatTimeUsingDeviceSettings(context: Context): java.text.DateFormat {
    return DateFormat.getTimeFormat(context) ?: SimpleDateFormat("HH:mm", Locale.getDefault())
}

使用示例:

    val dateFormat = DateHelper.getFormatDateUsingDeviceSettings(this)
    val timeFormat = DateHelper.getFormatTimeUsingDeviceSettings(this)
    val timeStampSecondsInUtc = 1516797000L
    val localDateTime = Instant.ofEpochSecond(timeStampSecondsInUtc).atZone(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0))).toLocalDateTime()
    Log.d("AppLog", "input time in UTC:" + localDateTime)
    Log.d("AppLog", "formatted date and time in current config:${dateFormat.formatUtcEpochSecond(timeStampSecondsInUtc)} ${timeFormat.formatUtcEpochSecond(timeStampSecondsInUtc)}")

0

一个日期对象始终是对自纪元0毫秒的包装,它在UTC中。它从不表示本地时间。

这意味着您需要创建第二个SimpleDateFormatter,以创建一个显示字符串,该字符串处于本地时间。

编辑:@Op。请注意,Android上用于日期/时间格式化的首选类是java.text.DateFormat


是的,但您解析输入字符串的区域上下文决定了该特定“挂钟时间”表示多少毫秒。 - Affe
1
是的,我的错,我没有注意到他没有为第一个格式化程序设置时区。 - Kaj

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