相关文章推荐
潇洒的电梯  ·  一个奇葩错误:Invalid ...·  1 年前    · 
腹黑的剪刀  ·  openGauss ...·  1 年前    · 

如何在安卓系统中把UTC时间戳转换为设备本地时间

52 人关注

我需要将我从服务器得到的UTC时间戳转换为本地设备时间。目前,我的时间有5个小时的差异。例如,当我向服务器发帖时,帖子的时间是5小时前,而不是一秒钟前。我怎样才能解决这个问题?

下面是我做的代码。

long timestamp = cursor.getLong(columnIndex);
            CharSequence relTime = DateUtils
                    .getRelativeTimeSpanString(timestamp * 1000
                            + TimeZone.getDefault().getRawOffset(),
                            System.currentTimeMillis(),
                            DateUtils.MINUTE_IN_MILLIS);
            ((TextView) view).setText(relTime);
    
android
timestamp
utc
cavallo
cavallo
发布于 2013-02-13
11 个回答
prgDevelop
prgDevelop
发布于 2019-09-27
已采纳
0 人赞同
int offset = TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings();
long now = System.currentTimeMillis() - offset;

Kotlin:

val offset: Int = TimeZone.getDefault().rawOffset + TimeZone.getDefault().dstSavings
val now: Long = System.currentTimeMillis() - offset
    
这正是我所寻找的。
也要考虑到夏令时: int gmtOffset = TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings()。
这应该就是答案了。
should not it be minus in here? long now = System.currentTimeMillis() - gmtOffset
对不起,这个答案是不正确的。自纪元以来的毫秒始终是自纪元以来的。根据时区和/或夏季时间(DST)来调整它们是没有意义的。如果你的代码最终给了你正确的结果,它仍然走错了路,而且肯定会迷惑未来的读者。
madhu527
madhu527
发布于 2019-09-27
0 人赞同

将一个格式为 "2011-06-23T15:11:32 "的日期字符串转换为我们的时区。

private String getDate(String ourDate)
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date value = formatter.parse(ourDate);
        SimpleDateFormat dateFormatter = new SimpleDateFormat("MM-dd-yyyy HH:mm"); //this format changeable
        dateFormatter.setTimeZone(TimeZone.getDefault());
        ourDate = dateFormatter.format(value);
        //Log.d("ourDate", ourDate);
    catch (Exception e)
        ourDate = "00-00-0000 00:00";
  return ourDate;
    
这是对整个SO的最佳答案。谢谢你!"。
这是否也照顾到了夏令时的问题?
这工作很完美。但假设我试图将2018-04-27 12:48:00 UTC时区转换成IST时区。现在在这里,转换后返回2018-04-27 06:18:00 am,而不是2018-04-27 06:18:00 pm,在这种情况下该怎么做?在这里,如果任何UTC时间在12:00:00到12:59:59之间,会返回错误的结果。在其他工作中是完美的。
@madhusudhan这个怎么转换 2018-12-01T20:21:36.793194+05:30
pestrella
pestrella
发布于 2019-09-27
0 人赞同

你的例子中的代码乍一看很好。BTW,如果服务器的时间戳是UTC(即它是一个纪元时间戳),那么你不应该应用当前的时区偏移。换句话说,如果服务器的时间戳是UTC,那么你可以简单地得到服务器时间戳和系统时间之间的差异( System.currentTimeMillis() ),因为系统时间是UTC(历时)。

我会检查从你的服务器来的时间戳是否是你所期望的。如果来自服务器的时间戳不能转换为你期望的日期(在本地时区),那么时间戳和当前系统时间之间的差异将不是你期望的。

使用 Calendar 来获取当前时区。用当前时区初始化一个 SimpleDateFormatter ;然后记录服务器的时间戳并验证它是否是你期望的日期。

Calendar cal = Calendar.getInstance();
TimeZone tz = cal.getTimeZone();
/* debug: is it local time? */
Log.d("Time zone: ", tz.getDisplayName());
/* date formatter in local timezone */
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
sdf.setTimeZone(tz);
/* print your timestamp and double check it's the date you expect */
long timestamp = cursor.getLong(columnIndex);
String localTime = sdf.format(new Date(timestamp * 1000)); // I assume your timestamp is in seconds and you're converting to milliseconds?
Log.d("Time: ", localTime);

如果打印的服务器时间是is not你期望的那样,那么你的服务器时间就是not in UTC.

如果打印的服务器时间是is the date that you expect then you should not have to apply the rawoffset to it. So your code would be simpler (minus all the debug logging):

long timestamp = cursor.getLong(columnIndex);
Log.d("Server time: ", timestamp);
/* log the device timezone */
Calendar cal = Calendar.getInstance();
TimeZone tz = cal.getTimeZone();
Log.d("Time zone: ", tz.getDisplayName());
/* log the system time */
Log.d("System time: ", System.currentTimeMillis());
CharSequence relTime = DateUtils.getRelativeTimeSpanString(
    timestamp * 1000,
    System.currentTimeMillis(),
    DateUtils.MINUTE_IN_MILLIS);
((TextView) view).setText(relTime);
谢谢 @pestrella 这给了我一个格式化的时间格式,但我想显示像2分钟前,1小时前的时间......使用安卓工具。有什么解决方法吗 ....thanks
你的代码例子在我看来很好,这应该能给你提供时间差(例如2分钟前)。然而,我认为你不应该将rawoffset应用于服务器时间(假设服务器时间是UTC)。我需要更多的信息来帮助。按照我的代码例子,你是否打印了服务器时间,而且是你期望的时间?
cavallo
是的,你的代码给了我标准格式的时间,但我想显示17分钟前的时间,....。
pestrella 我的代码给出了我所期望的结果,但时间差是5小时,有什么想法吗?
出于好奇,你所使用的服务器时间戳是什么?另外,如果你把DateUtils.MINUTE_IN_MILLIS改为0,那么你会得到以秒为单位的经过时间(例如2秒前)。你能用0代替MINUTE_IN_MILLIS再做一次测试吗?记录服务器的时间戳。你得到了什么时间戳?你的TextView显示了什么?
Kasim Rangwala
Kasim Rangwala
发布于 2019-09-27
0 人赞同

I did it using Extension Functions in kotlin

fun String.toDate(dateFormat: String = "yyyy-MM-dd HH:mm:ss", timeZone: TimeZone = TimeZone.getTimeZone("UTC")): Date {
    val parser = SimpleDateFormat(dateFormat, Locale.getDefault())
    parser.timeZone = timeZone
    return parser.parse(this)
fun Date.formatTo(dateFormat: String, timeZone: TimeZone = TimeZone.getDefault()): String {
    val formatter = SimpleDateFormat(dateFormat, Locale.getDefault())
    formatter.timeZone = timeZone
    return formatter.format(this)

使用方法。

"2018-09-10 22:01:00".toDate().formatTo("dd MMM yyyy")
Output: "11 Sep 2018"

Note:

确保适当的验证。

Chintan Khetiya
Chintan Khetiya
发布于 2019-09-27
0 人赞同

我已经做了这样的事情,从UTC时间戳中获得本地设备时区的日期。

private long UTC_TIMEZONE=1470960000;
private String OUTPUT_DATE_FORMATE="dd-MM-yyyy - hh:mm a"
getDateFromUTCTimestamp(UTC_TIMEZONE,OUTPUT_DATE_FORMATE);

Here is the function

 public String getDateFromUTCTimestamp(long mTimestamp, String mDateFormate) {
        String date = null;
        try {
            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
            cal.setTimeInMillis(mTimestamp * 1000L);
            date = DateFormat.format(mDateFormate, cal.getTimeInMillis()).toString();
            SimpleDateFormat formatter = new SimpleDateFormat(mDateFormate);
            formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
            Date value = formatter.parse(date);
            SimpleDateFormat dateFormatter = new SimpleDateFormat(mDateFormate);
            dateFormatter.setTimeZone(TimeZone.getDefault());
            date = dateFormatter.format(value);
            return date;
        } catch (Exception e) {
            e.printStackTrace();
        return date;

Result :

12-08-2016 - 04:30 PM 

Hope this will work for others.

DateFormat.format是从哪个包中导入的?是 java.text.DateFormat 吗?
Arvind Kumar Avinash
Arvind Kumar Avinash
发布于 2019-09-27
0 人赞同

java.time

java.util 的日期-时间API及其格式化API, SimpleDateFormat 已经过时且容易出错。建议完全停止使用它们,转而使用 现代日期-时间API * .

Solution using java.time , the 现代日期-时间API:

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class Main {
    public static void main(String[] args) {
        // A sample timestamp as Unix epoch (i.e. seconds from 01-01-1970T00:00:00 GMT)
        long epochSeconds = 1632131465L;
        // Note: Use Instant#ofEpochMilli in case you have timestamp in milliseconds
        Instant instant = Instant.ofEpochSecond(epochSeconds);
        System.out.println(instant);
        LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        System.out.println(ldt);

在我的时区输出,欧洲/伦敦。

2021-09-20T09:51:05Z
2021-09-20T10:51:05

ONLINE DEMO

一个Instant代表时间轴上的一个瞬时点,通常以UTC time. The Z in the output is the 时区指定器为零时区偏移。它代表Zulu,并指定了Etc/UTC时区(该时区有+00:00小时的偏移)。

Learn more about the 现代日期-时间API from 路径:日期 时间.

* 由于任何原因,如果你必须坚持使用Java 6或Java 7,你可以使用三联书店-Backport它背传了大部分的java.time functionality to Java 6 & 7. If you are working for an Android project和your Android API level is still not compliant with Java-8, check 通过解ugaring提供的Java 8+ APIs如何在Android项目中使用ThreeTenABP.

Anand Jagtap
Anand Jagtap
发布于 2019-09-27
0 人赞同

UTC本地

DateTime dateTimeNew = new DateTime(date.getTime(),
DateTimeZone.forID("Asia/Calcutta"));
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
String datetimeString = dateTimeNew.toString("yyyy-MM-dd HH:mm:ss");
long milis = 0;
try {
     milis = simpleDateFormat.parse(datetimeString).getTime();
} catch (ParseException e) {
   e.printStackTrace();
    
0 人赞同

在我的Android Marsmallow上,@prgDevelop的答案返回0。必须返回7200000。这些改变使其工作正常。

int offset = TimeZone.getTimeZone(Time.getCurrentTimezone()).getRawOffset() + TimeZone.getTimeZone(Time.getCurrentTimezone()).getDSTSavings();
    
CLIFFORD P Y
CLIFFORD P Y
发布于 2019-09-27
0 人赞同

这可能有助于有相同需求的人

private String getDate(long time){
        SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:mm a");
        String dateString = formatter.format(new Date(time));
        String date = ""+dateString;
        return date;
    
stanley
stanley
发布于 2019-09-27
0 人赞同

我有一个类似的问题。只需将你的utc时间戳设置为你的时区日历,然后将日期格式化。无论哪个时区,时间戳都是一样的。

val local = Calendar.getInstance()   // get your device timezone calendar
local.timeInMillis = <timestamp>
val sdf = SimpleDateFormat("dd/MM/yyyy HH:mm:ss")
val formatted = sdf.format(Date(local.timeInMillis))
    
Keshav Gera
Keshav Gera
发布于 2019-09-27
0 人赞同

It's Working

调用这个方法,在你使用

SntpClient client = new SntpClient();
if (client.requestTime("ntp.ubuntu.com", 30000)) {
  long now = client.getNtpTime();
  Date current = new Date(now);
  date2 = sdf.parse(new Date(current.getTime()).toString());
 // System.out.println(current.toString());
  Log.e(TAG, "testing SntpClient time current.toString() "+current.toString()+" , date2 = "+date2);
=====================================================   
import android.os.SystemClock;
import android.util.Log;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
 * {@hide}
 * Simple SNTP client class for retrieving network time.
 * Sample usage:
 * <pre>SntpClient client = new SntpClient();
 * if (client.requestTime("time.foo.com")) {
 *     long now = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference();
 * </pre>
public class SntpClient
  private static final String TAG = "SntpClient";
  private static final int REFERENCE_TIME_OFFSET = 16;
  private static final int ORIGINATE_TIME_OFFSET = 24;
  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;
  // system time computed from NTP server response
  private long mNtpTime;
  // value of SystemClock.elapsedRealtime() corresponding to mNtpTime
  private long mNtpTimeReference;
  // round trip time in milliseconds
  private long mRoundTripTime;
   * Sends an SNTP request to the given host and processes the response.
   * @param host host name of the server.
   * @param timeout network timeout in milliseconds.
   * @return true if the transaction was successful.
  public boolean requestTime(String host, int timeout) {
    DatagramSocket socket = null;
    try {
      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);
      // set mode = 3 (client) and version = 3
      // mode is in low 3 bits of first byte
      // version is in bits 3-5 of first byte
      buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
      // get current time and write it to the request packet
      long requestTime = System.currentTimeMillis();
      long requestTicks = SystemClock.elapsedRealtime();
      writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
      socket.send(request);
      // read the response
      DatagramPacket response = new DatagramPacket(buffer, buffer.length);
      socket.receive(response);
      long responseTicks = SystemClock.elapsedRealtime();
      long responseTime = requestTime + (responseTicks - requestTicks);
      // extract the results
      long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
      long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
      long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
      long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);
      // receiveTime = originateTime + transit + skew
      // responseTime = transmitTime + transit - skew
      // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2
      //             = ((originateTime + transit + skew - originateTime) +
      //                (transmitTime - (transmitTime + transit - skew)))/2
      //             = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2
      //             = (transit + skew - transit + skew)/2
      //             = (2 * skew)/2 = skew
      long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;
      // if (false) Log.d(TAG, "round trip: " + roundTripTime + " ms");
      // if (false) Log.d(TAG, "clock offset: " + clockOffset + " ms");
      // save our results - use the times on this side of the network latency
      // (response rather than request time)
      mNtpTime = responseTime + clockOffset;
      mNtpTimeReference = responseTicks;
      mRoundTripTime = roundTripTime;
    } catch (Exception e) {
      if (false) Log.d(TAG, "request time failed: " + e);
      return false;
    } finally {
      if (socket != null) {
        socket.close();
    return true;
   * Returns the time computed from the NTP transaction.
   * @return time value computed from NTP server response.
  public long getNtpTime() {
    return mNtpTime;
   * Returns the reference clock value (value of SystemClock.elapsedRealtime())
   * corresponding to the NTP time.
   * @return reference clock corresponding to the NTP time.
  public long getNtpTimeReference() {
    return mNtpTimeReference;
   * Returns the round trip time of the NTP transaction
   * @return round trip time in milliseconds.
  public long getRoundTripTime() {
    return mRoundTripTime;
   * 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 system time (milliseconds since January 1, 1970) as an NTP time stamp
   * at the given offset in the buffer.
  private void writeTimeStamp(byte[] buffer, int offset, long time) {
    long seconds = time / 1000L;
    long milliseconds = time - seconds * 1000L;
    seconds += OFFSET_1900_TO_1970;
    // write seconds in big endian format
    buffer[offset++] = (byte)(seconds >> 24);
    buffer[offset++] = (byte)(seconds >> 16);
    buffer[offset++] = (byte)(seconds >> 8);
    buffer[offset++] = (byte)(seconds >> 0);
    long fraction = milliseconds * 0x100000000L / 1000L;
    // write fraction in big endian format
    buffer[offset++] = (byte)(fraction >> 24);
    buffer[offset++] = (byte)(fraction >> 16);
    buffer[offset++] = (byte)(fraction >> 8);