52

I need to convert the UTC time stamp that I get from the server to local device time. Currently, I get 5 hrs difference in my time. For example, when I post to the server, the post time says 5 hours ago instead of a second ago. How could I fix this issue?

Below is the code that I do:

long timestamp = cursor.getLong(columnIndex);
            CharSequence relTime = DateUtils
                    .getRelativeTimeSpanString(timestamp * 1000
                            + TimeZone.getDefault().getRawOffset(),
                            System.currentTimeMillis(),
                            DateUtils.MINUTE_IN_MILLIS);
            ((TextView) view).setText(relTime);
Patrick Yoder
  • 1,065
  • 4
  • 14
  • 19
cavallo
  • 4,414
  • 9
  • 39
  • 61

10 Answers10

73

Java:

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
prgDevelop
  • 1,557
  • 2
  • 15
  • 26
  • 1
    exactly what i was looking for. – Anirudh Sharma Jun 18 '15 at 05:56
  • 19
    To also account for daylight savings: int gmtOffset = TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings(); – Farshid T Aug 26 '15 at 12:01
  • 1
    This should be the answer. – Alexandr Nov 23 '16 at 21:36
  • I noticed that this approach doesn't take into account the daylight savings. @FarshidT answer covers this short-coming but I think that the less verbose of achieving the same is by using `int utcOffset = Timezone.getDefault.getOffset(System.currentTimeMillis())` – Nicolás Carrasco-Stevenson Mar 22 '17 at 15:55
  • 5
    should not it be minus in here? `long now = System.currentTimeMillis() - gmtOffset` – vigilancer Apr 28 '17 at 11:55
  • 4
    Sorry, this answer is not correct. Milliseconds since the epoch are always since the epoch. Adjusting them for time zone and/or summer time (DST) does not make sense. If you code ends up giving you the correct result, it has still gone the wrong way there and will be sure to confuse future readers. – Ole V.V. May 31 '18 at 07:27
  • A mixture of pestrella's answer and this worked for me. – Mostafa Arian Nejad Jun 08 '19 at 20:12
56

Converting a date String of the format "2011-06-23T15:11:32" to our time zone.

private String getDate(String ourDate)
{
    try
    {
        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;
}
MatPag
  • 41,742
  • 14
  • 105
  • 114
madhu527
  • 4,644
  • 1
  • 28
  • 29
  • 1
    This is the best answer on the whole SO. Thank you! – Lampione Aug 24 '16 at 23:13
  • Does this take care of daylight savings as well? –  Jan 28 '18 at 13:49
  • This works perfect. But suppose i m trying convert 2018-04-27 12:48:00 UTC time zone to IST time zone. Now here it return after convert 2018-04-27 06:18:00 am instead of 2018-04-27 06:18:00 pm what to do in this scenario? Here if any UTC time come between 12:00:00 to 12:59:59. this return wrong result. In other work perfect. – Dharmik Mavani Apr 26 '18 at 12:27
  • @madhusudhan how to convert this one 2018-12-01T20:21:36.793194+05:30 – kartheeki j Dec 03 '18 at 07:05
45

The code in your example looks fine at first glance. BTW, if the server timestamp is in UTC (i.e. it's an epoch timestamp) then you should not have to apply the current timezone offset. In other words if the server timestamp is in UTC then you can simply get the difference between the server timestamp and the system time (System.currentTimeMillis()) as the system time is in UTC (epoch).

I would check that the timestamp coming from your server is what you expect. If the timestamp from the server does not convert into the date you expect (in the local timezone) then the difference between the timestamp and the current system time will not be what you expect.

Use Calendar to get the current timezone. Initialize a SimpleDateFormatter with the current timezone; then log the server timestamp and verify if it's the date you expect:

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);

If the server time that is printed is not what you expect then your server time is not in UTC.

If the server time that is printed 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
  • 9,786
  • 4
  • 39
  • 44
  • thanks @pestrella that gives me a formatted time format but i want to show time like 2 min ago, 1 hour ago .. using the android utils. any workaround for that ....thanks – cavallo Feb 14 '13 at 05:37
  • Your code example looks good to me and that should give you the time difference (e.g. 2 min ago). However, I don't believe you should have to apply the rawoffset to the server time (assuming that the server time is in UTC). I need more information to help. As per my code example, did you print the server time, and is it the time you expect? – pestrella Feb 14 '13 at 11:43
  • yes ur code gives me the time in standard format,, but i want to show the time like 17 mins ago.... – cavallo Feb 14 '13 at 12:00
  • pestrella My code gives me what i expect, but the time difference is 5 hrs. any idea – cavallo Feb 14 '13 at 12:07
  • Out of curiosity what is the server timestamp that you are working with? Also, if you change DateUtils.MINUTE_IN_MILLIS to 0 then you will get the elapsed time in seconds (e.g. 2 seconds ago). Could you do another test using 0 instead of MINUTE_IN_MILLIS? Log the server timestamp. What timestamp did you get? and what did your TextView display? – pestrella Feb 14 '13 at 12:17
  • the server time i get in string format is in UTC when i tried your code i got something like 14/02/2013 16:50:16 and when i changed DateUtils.minute_in_millis to 0 i got in 4 hours and 20 hours ago and with Minute_in_mills i get 5 hrs ago – cavallo Feb 14 '13 at 12:31
  • In that case either: 1. your device is in GMT+4, or 2. the server is returning UTC timestamps in the future, or 3. your server is not actually return UTC timestamps. In order to help debug the issue, I would log the server timestamp (the Long not the String formatted date), and also log the System.currentTimeMillis() (again the Long value). I would additionally log the device timezone (cal.getTimeZone().getDisplayName). I will update the code above to contain the required logging. – pestrella Feb 14 '13 at 13:20
  • Hi, What is "cursos" variable here? – Daniel Krzyczkowski May 06 '15 at 21:12
  • Hi @danny, the [`cursor`](https://developer.android.com/reference/android/database/Cursor.html) object is not essential for the solution. It is used by the OP to read data from the database; in this case the timestamp is being read from the database. – pestrella May 15 '15 at 10:16
  • For show time as Just Now or xx min ago or xx days etc. use this: https://github.com/curioustechizen/android-ago – Kalu Khan Luhar Jun 05 '15 at 07:07
9

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)
}

Usage:

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

Output: "11 Sep 2018"

Note:

Ensure the proper validation.

Kasim Rangwala
  • 1,765
  • 2
  • 23
  • 44
3

I have do something like this to get date in local device timezone from UTC time stamp.

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.

Chintan Khetiya
  • 15,962
  • 9
  • 47
  • 85
3

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Solution using java.time, the modern Date-Time 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);
    }
}

Output in my timezone, Europe/London:

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

ONLINE DEMO

An Instant represents an instantaneous point on the timeline, normally represented in UTC time. The Z in the output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).

Learn more about the modern Date-Time API from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

Local to 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();
}
Fenil
  • 1,194
  • 10
  • 11
1

The answer from @prgDevelop returns 0 on my Android Marsmallow. Must return 7200000. These changes make it work fine:

int offset = TimeZone.getTimeZone(Time.getCurrentTimezone()).getRawOffset() + TimeZone.getTimeZone(Time.getCurrentTimezone()).getDSTSavings();
oml
  • 115
  • 2
  • 8
0

This may help some one with same requirement

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;
    }
CLIFFORD P Y
  • 16,974
  • 6
  • 30
  • 45
0

I had a similar problem. Simply set your utc timestamp to your timezone calendar and format the date. Timestamp is still the same no matter timezone.

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))
stanley
  • 1
  • 2