Manto
Manto

Reputation: 1117

Java/Android - Convert a GMT time string to local time

Ok, so I have a string, say "Tue May 21 14:32:00 GMT 2012" I want to convert this string to local time in the format May 21, 2012 2:32 pm. I tried SimpleDateFormat("MM dd, yyyy hh:mm a").parse(), but it threw an exception. So what should I do?

The exception is "unreported exception java.text.ParseException; must be caught or declared to be thrown."

in the line Date date = inputFormat.parse(inputText);

The code I ran on TextMate:

public class test{
    public static void main(String arg[]) {
        String inputText = "Tue May 22 14:52:00 GMT 2012";
        SimpleDateFormat inputFormat = new SimpleDateFormat(
            "EEE MMM dd HH:mm:ss 'GMT' yyyy", Locale.US);
        inputFormat.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
        SimpleDateFormat out = new SimpleDateFormat("MMM dd, yyyy h:mm a");
        Date date = inputFormat.parse(inputText);
        String output = out.format(date);
       System.out.println(output);
    }
}

Upvotes: 14

Views: 26262

Answers (5)

Jalal Jan Khan
Jalal Jan Khan

Reputation: 123

const val DATE_HYPHEN_FORMAT = "yyyy-MM-dd"
const val DATE_MMM_DD_YYYY_FORMAT = "MMM dd, yyyy"
const val DATE_MMMM_DD_YYYY_FORMAT = "MMMM dd, yyyy"

const val FULL_DAY_NAME_FORMAT = "EEEE"
const val DATE_EEE_DD_MMMM_YYYY_FORMAT = "EEE dd, MMMM yyyy"
const val DATE_EEEE_DD_MMMM_YYYY_FORMAT = "EEEE dd, MMMM yyyy"

const val DATETIME_24_FORMAT = "dd-MM-yyyy'T'HH:mm:ss"

const val DATETIME_24_YMD_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"
const val DATE_WITH_MONTH_NAME_MMM_DY_FORMAT = DateFormat.MEDIUM
const val DATE_WITH_MONTH_FULL_NAME_MMMM_DY_FORMAT = DateFormat.LONG

const val TIME_24H_FORMAT = "HH:mm:ss"
const val TIME_FORMAT_AM_PM = "hh:mm aa"
const val TIME_24H_FORMATWithoutSS = "HH:mm"

enum class TimeZoneTo {
    NONE, UTC, LOCAL
}


fun changeFormat(
    dateTime: String,
    fromFormat: Int = DATE_WITH_MONTH_NAME_MMM_DY_FORMAT,
    toFormat: String = DATETIME_24_FORMAT,
    convertIn: TimeZoneTo = TimeZoneTo.NONE,
    needOnlyTime: Boolean = false
): String {
    try {
        val parser = DateFormat.getDateInstance(fromFormat)
        val finalDateTime = trimDateTime(dateTime)

        val date = parser.parse(finalDateTime)
        val sdf = SimpleDateFormat(toFormat)

        return format(
            date = date!!, formatter = sdf, convertIn = convertIn, needOnlyTime = needOnlyTime
        )
    } catch (e: AssertionError) {
        e.printStackTrace()
    } catch (e: Exception) {
        e.printStackTrace()
    }

    return dateTime // Return Same DateTime - only iff unable to change format
}



fun changeFormat(
    dateTime: String,
    fromFormat: String = DATETIME_24_FORMAT,
    toFormat: Int = DATE_WITH_MONTH_NAME_MMM_DY_FORMAT,
    convertIn: TimeZoneTo = TimeZoneTo.NONE,
    needOnlyTime: Boolean = false
): String {
    try {
        val sfdInput = SimpleDateFormat(fromFormat, Locale.ROOT)
        val finalDateTime = trimDateTime(dateTime)

        val date: Date = sfdInput.parse(finalDateTime)!!
        val outputFormatter = DateFormat.getDateInstance(toFormat)

        return format(
            date = date,
            formatter = outputFormatter,
            convertIn = convertIn,
            needOnlyTime = needOnlyTime
        )
    } catch (e: AssertionError) {
        e.printStackTrace()
    } catch (e: Exception) {
        e.printStackTrace()
    }

    return dateTime // Return Same DateTime - only iff unable to change format
}

fun changeFormat(
    dateTime: String,
    fromFormat: String = DATETIME_24_FORMAT,
    toFormat: String = DATE_HYPHEN_FORMAT,
    convertInTimeZone: TimeZoneTo = TimeZoneTo.NONE,
    needOnlyTime: Boolean = false
): String {
    try {
        val sfdInput = SimpleDateFormat(fromFormat, Locale.ROOT)

        val finalDateTime = trimDateTime(dateTime)

        val date: Date = sfdInput.parse(finalDateTime)!!
        val sdfOutput = SimpleDateFormat(toFormat)

        return format(
            date = date,
            formatter = sdfOutput,
            convertIn = convertInTimeZone,
            needOnlyTime = needOnlyTime
        )
    } catch (e: AssertionError) {
        e.printStackTrace()
    } catch (e: Exception) {
        e.printStackTrace()
    }

    return dateTime // Return Same DateTime - only iff unable to change format
}


// Format Given Date as per specified timeZone
private fun format(
    date: Date,
    formatter: DateFormat,
    convertIn: TimeZoneTo,
    needOnlyTime: Boolean
): String {
    return when (convertIn) {
        TimeZoneTo.LOCAL -> {
            val zone = TimeZone.getTimeZone(Calendar.getInstance().timeZone.id)
            val newDate = Date(date.time + zone.getOffset(date.time))
            formatter.timeZone = zone

            val result = formatter.format(newDate)
            if (needOnlyTime) return result.substringAfter('T')
            else result
        }
        TimeZoneTo.UTC -> {
            formatter.timeZone = TimeZone.getTimeZone("UTC")
            val result = formatter.format(date)

            if (needOnlyTime) return result.substringAfter('T')
            else result
        }
        else -> {
            val result = formatter.format(date)
            if (needOnlyTime) return result.substringAfter('T')
            else result
        }
    }
}

I've created 3 overloaded methods namely changeFormat(...) with following params;

  1. dateTime (String -> date or time or dateTime)
  2. fromFormat (String/Int -> Current format of dateTime) - optional (i.e. if not provided default will be used)
  3. toFormat (String/Int -> New format for dateTime) - optional (i.e. if not provided default will be used)
  4. convertInTimeZone (Enum -> Convert in specified TimeZone)
  5. needOnlyTime (Boolean -> Will return only converted Time if true else converted dateTime

Hopefully, this may help someone in future.

Upvotes: 0

Basil Bourque
Basil Bourque

Reputation: 338730

You are using troublesome old date-time classes now supplanted by the java.time classes.

Wrong input data

Your first example string is incorrect, as the 21st is a Monday not a Tuesday. The second example string with 22nd is correct, and used in my example code below.

Using java.time

Avoid using this kind of format for textual representations of date-time values. In particular, never use the 3-4 letter abbreviation such as EST or IST seen in this kind of format, as they are not true time zones, not standardized, and not even unique(!). Specify a proper time zone name. In this particular case, java.time is able to translate that as GMT as UTC, but other values may fail.

String input = "Tue May 22 14:52:00 GMT 2012";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "EEE MMM dd HH:mm:ss z uuuu" ).withLocale ( Locale.US );
ZonedDateTime zdt = ZonedDateTime.parse ( input , f );

System.out.println ( "zdt: " + zdt );

Dump to console. The toString method generates a String in standard ISO 8601 format, extended by appending the name of the zone in brackets. These standard formats are a much better choice for when you need to serialize date-time values to text.

System.out.println ( "zdt: " + zdt );

zdt: 2012-05-22T14:52Z[GMT]

Generate String

You can generate a String to represent this value in any format you desire. Generally best to let java.time localize automatically using a Locale and DateTimeFormatter.

Your desired format uses a medium-length style for the date portion but a short-length style for the time-of-day portion. Fortunately the DateTimeFormatter allows you to localize each portion separately, as seen here where we pass a pair of FormatStyle objects.

Locale l = Locale.US;  // Or Locale.CANADA_FRENCH, or Locale.ITALY, etc.
DateTimeFormatter fOutput = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.MEDIUM , FormatStyle.SHORT ).withLocale ( l );
String output = zdt.format ( fOutput );

Dump to console.

System.out.println ( "zdt: " + zdt + " | output: " + output );

zdt: 2012-05-22T14:52Z[GMT] | output: May 22, 2012 2:52 PM

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to java.time.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.

Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500675

The format string you provided for parsing doesn't correspond with the text format you've actually got. You need to parse first, then format. It looks like you want:

SimpleDateFormat inputFormat = new SimpleDateFormat(
    "EEE MMM dd HH:mm:ss 'GMT' yyyy", Locale.US);
inputFormat.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));

SimpleDateFormat outputFormat = new SimpleDateFormat("MMM dd, yyyy h:mm a");
// Adjust locale and zone appropriately

Date date = inputFormat.parse(inputText);
String outputText = outputFormat.format(date);

EDIT: Here's the same code in the form of a short but complete program, with your sample input:

import java.util.*;
import java.text.*;

public class Test {
    public static void main(String[] args) throws ParseException {
        String inputText = "Tue May 21 14:32:00 GMT 2012";
        SimpleDateFormat inputFormat = new SimpleDateFormat
            ("EEE MMM dd HH:mm:ss 'GMT' yyyy", Locale.US);
        inputFormat.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));

        SimpleDateFormat outputFormat =
            new SimpleDateFormat("MMM dd, yyyy h:mm a");
        // Adjust locale and zone appropriately
        Date date = inputFormat.parse(inputText);
        String outputText = outputFormat.format(date);
        System.out.println(outputText);
    }
}

Can you compile and run that exact code?

Upvotes: 28

GHz
GHz

Reputation: 471

The method SimpleDateFormat.parse throws a parse exception.

The exception you're getting is telling you this...

The exception is "unreported exception java.text.ParseException; must be caught or declared to be thrown."

Wrap the line that does the parsing with try-catch and you should be golden..

Date d=null;
try{
    d = inputFormat.parse(date);
catch(ParseException e){
   // handle the error here....
}

R

Upvotes: 1

LINEMAN78
LINEMAN78

Reputation: 2562

The formatter you use to parse must be defined to the format you expect. Here is an example that works for the values you provided however you may need to change it depending on how some edge cases act for the input:

String date = "Tue May 21 14:32:00 GMT 2012";
DateFormat inputFormat = new SimpleDateFormat("EE MMM dd HH:mm:ss zz yyy");
Date d = inputFormat.parse(date);
DateFormat outputFormat = new SimpleDateFormat("MMM dd, yyy h:mm a zz");
System.out.println(outputFormat.format(d));

Upvotes: 3

Related Questions