Midnight Time zone conversion - android

I'm trying to convert midnight of one timezone to midnight of another time zone. Kotlin pretty much made it easy for the conversion of time zones but it does not work the same way when converting date and time to milliseconds.
Problem:
Indian Time: Mon Sep 28 00:00:00 GMT+5:30 2020
Vancouver Time: Sunday Sep 27 11:30:00 GMT-7:00 2020
What I need
Indian Time: Mon Sep 28 00:00:00 GMT+5:30 2020
Vancouver Time: Sunday Sep 27 11:30:00 GMT-7:00 2020
Here is what I tried:
val today = DateTime().withTimeAtStartOfDay().toDate() //current date and time converted to Date format
val dateOutputFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss") // formatting the output as SimpleDateFormat
dateOutputFormat.setTimeZone(TimeZone.getTimeZone("America/Vancouver")) //Setting the timezone
Log.d("Datey2", "Before conversion ${today}") // Before conversion Mon Sep 28 00:00:00 GMT+5:30 2020
val Vancouver = Date(dateOutputFormat.format(today)).time //formatting the timezone
Log.d("Datey2", "After conversion $Vancouver") // After conversion Sun Sep 27 11:30:00 GMT+05:30 2020
val VancouverMnight = DateTime(Vancouver).withTimeAtStartOfDay().millis
Log.d("Datey2", "MidNight $VancouverMnight") // MidNight Sun Sep 27 00:00:00 GMT+05:30 2020
Output:
Before conversion Mon Sep 28 00:00:00 GMT+5:30 2020
After conversion Sun Sep 27 11:30:00 GMT+05:30 2020 // Note the GMT+5:30 in vacouver time
MidNight Sun Sep 27 00:00:00 GMT+05:30 2020
Then I convert these into milliseconds as follows
Log.d("Datey2", "After conversion {$VancouverMnight.time}") // using time function gives output in milliseconds
But when I convert those Vancouver outputs to milliseconds, I get the following:
1601186400000 // Sep 26 23:00:00 2020 - Goes 2 days before the given time
1601145000000 // Sep 26 11:30:00 2020
What I need:
Sep 27 11:30:00 2020
Sep 27 00:00:00 2020 (I need these in milliseconds)

Since you are using Joda-Time, I recommend you either stick to that or move on to java.time, the modern Java date and time API. Stay away from Date and SimpleDateFormat. They are poorly designed and long outdated, and there is absolutely no reason why you should want to touch them.
For a Joda-Time solution in Java because this is what I can write and run:
DateTimeZone vancouverTimeZone = DateTimeZone.forID("America/Vancouver");
DateTimeFormatter dateOutputFormat = DateTimeFormat.forPattern("yyyy/MM/dd HH:mm:ss")
.withZone(vancouverTimeZone);
DateTime today = new DateTime().withTimeAtStartOfDay();
System.out.println("Today: " + today);
System.out.println("Today as seen in Vancouver: " + today.toString(dateOutputFormat));
long millisBeforeConversion = today.getMillis();
System.out.println("Millis before conversion: " + millisBeforeConversion);
DateTime vancouverMidnight = today.withZone(vancouverTimeZone)
.withTimeAtStartOfDay();
long millisAfterConversion = vancouverMidnight.getMillis();
System.out.println("Millis after conversion: " + millisAfterConversion);
Output when running today in Asia/Kolkata time zone:
Today: 2020-09-29T00:00:00.000+05:30
Today as seen in Vancouver: 2020/09/28 11:30:00
Millis before conversion: 1601317800000
Millis after conversion: 1601276400000
A day has passed since you asked your questions, so you cannot compare the millisecond values from my output with your own from the question, but you can verify that they agree with what you would want for today (already September 29 in India).
Note that the count of milliseconds since the epoch is independent of time zone. So whether you get the milliseconds before or after conversion to America/Vancouver time zone makes no difference.
If you want to move on to java.time, follow the good answer by Arvind Kumar Avinash.

The problem is that you are trying to write the java.util.Date object directly which outputs the value of java.util.Date#toString. Note that a date-time object is supposed to store the information about date, time, time-zone etc. but not about the formatting. The java.util.Date object is not a real date-time object like the modern date-time classes; rather, it represents the milliseconds from the Epoch of January 1, 1970. When you print an object of java.util.Date, its toString method returns the date-time calculated from this milliseconds value. Since java.util.Date does not have time-zone information, it applies the time-zone of your JVM and displays the same. If you need to print the date-time in a different time-zone, you will need to set the time-zone to SimpleDateFormat and obtain the formatted string from it e.g.
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ssZ z");
Date date = new Date();
// Date and time in India
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"));
// Output to be written to the log file
System.out.println(sdf.format(date));
// Date and time in Vancouver
sdf.setTimeZone(TimeZone.getTimeZone("America/Vancouver"));
// Output to be written to the log file
System.out.println(sdf.format(date));
}
}
Output:
2020/09/29 17:57:06+0530 IST
2020/09/29 05:27:06-0700 GMT-07:00
I recommend you switch from the outdated and error-prone java.util date-time API and SimpleDateFormat to the modern java.time date-time API and the corresponding formatting API (package, java.time.format). Learn more about the modern date-time API from Trail: Date Time. If 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.
Using the modern date-time API:
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ssZ z");
// Date and time in India
ZonedDateTime zdtNowIST = LocalDate.now().atStartOfDay((ZoneId.of("Asia/Calcutta")));
// Output in the default format
System.out.println(zdtNowIST);
// Output in the custom format
System.out.println(zdtNowIST.format(dtf));
// Date and time in Vancouver
ZonedDateTime zdtNowVancouver = zdtNowIST.withZoneSameInstant(ZoneId.of("America/Vancouver"));
// Output in the default format
System.out.println(zdtNowVancouver);
// Output in the custom format
System.out.println(zdtNowVancouver.format(dtf));
}
}
Output:
2020-09-29T00:00+05:30[Asia/Calcutta]
2020/09/29 00:00:00+0530 IST
2020-09-28T11:30-07:00[America/Vancouver]
2020/09/28 11:30:00-0700 GMT-07:00
I do not know Kotlin but I believe you should be able to use Java code directly in Kotlin. If not, at least you should be able to convert it using Kotlin syntax.

Related

Can't convert from local date to London date object

Android Studio 4.0, Java 6.
My GMT is GMT + 03:00
defaultConfig {
minSdkVersion 17
targetSdkVersion 28
const val LONDON_TIME_ZONE_ID = "Europe/London"
const val DEFAULT_DATE_JSON_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.000'Z'"
fun fromDateToLondonDate(localDate : Date) : Date? {
val dateLondonAsString = fromDate2LondonDateAsString(localDate)
val timeZoneLondon : TimeZone = TimeZone.getTimeZone(LONDON_TIME_ZONE_ID)
val dateLondon = getDateFromString(dateLondonAsString, timeZoneLondon)
if (BuildConfig.DEBUG)
Log.d("", "fromDateToLondonDate:" +
"\nlocalDate = $localDate"+
"\ndateLondonAsString = $dateLondonAsString"+
"\ndateLondon = $dateLondon")
return dateLondon
}
fun fromDate2LondonDateAsString(date : Date) : String? {
val timeZoneLondon : TimeZone = TimeZone.getTimeZone(LONDON_TIME_ZONE_ID)
val formatter: DateFormat = SimpleDateFormat(DEFAULT_DATE_JSON_FORMAT)
formatter.setTimeZone(timeZoneLondon)
val dateAsString : String = formatter.format(date)
return dateAsString
}
fun getDateFromString(str: String?, tz: TimeZone?): Date? {
return try {
val sdf = SimpleDateFormat(DEFAULT_DATE_JSON_FORMAT);
sdf.setTimeZone(tz)
val date = sdf.parse(str)
return date
} catch (e: ParseException) {
//e.printStackTrace();
null
}
}
and here result:
fromDateToLondonDate:
localDate = Tue Jun 16 13:14:59 GMT+03:00 2020
dateLondonAsString = 2020-06-16T11:14:59.000Z
dateLondon = Tue Jun 16 13:14:59 GMT+03:00 2020
as you can see the local date is Tue Jun 16 13:14:59 GMT+03:00 2020
and success convert to London date as String -> dateLondonAsString = 2020-06-16T11:14:59.000Z
Nice.
But I need to convert String to London Date. And I use method getDateFromString and result is not correct:
dateLondon = Tue Jun 16 13:14:59 GMT+03:00 2020
The correct London Date must be : Tue Jun 16 11:14:59 GMT+03:00 2020
Why getDateFromString not correct convert from string to date?
java.time and ThreeTenABP
To get a date-time object with a specific time zone turn to java.time, the modern Java date and time API. I am writing Java code, it’s what I can, and I trust you to translate:
ZoneId london = ZoneId.of("Europe/London");
Date date = getOldfashionedDateFromSomewhere();
System.out.println("Original Date: " + date);
ZonedDateTime dateTimeLondon = date.toInstant().atZone(london);
System.out.println("Date-time in London: " + dateTimeLondon);
Example output:
Original Date: Tue Jun 16 13:14:59 GMT+03:00 2020
Date-time in London: 2020-06-16T11:14:59+01:00[Europe/London]
A Date hasn’t got, as in cannot have a time zone. Yes, I know, when you print it, thereby implicitly calling its toString method, it prints a time zone. What happens is that it grabs the default time zone of your JVM and uses it for rendering the string. So as long as your JVM’s time zone setting is GMT+03:00, all of your Date objects will always print this time zone.
So if (in some another project) I need date with time zone
the better way is use java.time.ZonedDateTime (java 8) ?
It's at least the way I would recommend. As I said, using Date is not a way. Some ways are:
The poor and old-fashioned way: using GregorianCalendar.
The better way: using DateTime from Joda-Time. If accepting an external dependency, I'd prefer ThreeTenABP, though.
The good way: ZonedDateTime from java.time.
The advanced way: using Time4J. I haven't got experience, so I'd rather not recommend for or against. If your project has requirements that go beyond what java.time offers, I'd certainly research this option.
What went wrong in your code
First, you should probably not set your time zone to GMT+03:00. While your time zone uses this GMT offset now, that doesn’t mean that it always has nor that it always will. For correct results for historic and future dates use a real time zone ID such as Africa/Nairobi or Europe/Moscow, that is, in the region/city format. Just as you used Europe/London for British time (fortunately not GMT+00:00).
I have already mentioned the second point: You cannot use a Date for a date and time with a time zone because a Date hasn’t got a time zone. A Date is a point in time, no more, no less.
Next, 2020-06-16T11:14:59.000Z is wrong for the time in London. 11:14:59 is correct for the time of day. Z means UTC or offset 0 from UTC and is wrong since Great Britain uses summer time (DST) and hence is at offset +01:00 in June (as the output from my code above also says). The time at offset Z would have been 10:14:59. In other words, your time is 1 hour off. This definition of Z is part of the ISO 8601 standard. Your JSON format is ISO 8601 format. I include a link at the bottom. Since Z is an offset, you should always format and parse it as such and never hardcode it as a literal in your format pattern string.
Your conversion back from string to Date exhibits the same error, so it balances out with the error in converting to a String, and you succeed in getting an equivalent Date object back.
It's old Android project. And I can't use java.time. I can use only java.util.Date
You can certainly use java.time in old Android projects for old Android versions too.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Wikipedia article: ISO 8601
All about java.util.Date on Jon Skeet’s coding blog
A related question about getting a Date with a time zone: TimeZone problem in Java
Joda-Time
Time4J - Advanced Date, Time, Zone and Interval Library for Java

how to format date string from input could be of multiple string formats

Having input date string could be possible as
"2020-01-25T21:59:27Z"
or
"Sat Jan 25 20:06:07 +0000 2020"
or a Long
and expect one to display is like
Jan 25, 2020
how to get the desired formatted date string?
update:
#Ole V.V provided a very good suggestion, it's just cannt apply it with android lib case.
but I guess there is no single format for all these three cases, so have to try out one by one. such as for the ISO8601 one to do something like:
return try {
val dateStr = "Sat Jan 25 20:06:07 +0000 2020". //ISO8601
val format = SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.getDefault())
val dsipFormat = SimpleDateFormat("MMM dd yyyy", Locale.getDefault()) // for display result
val date = format.parse(dateStr) // parse it to date
dsipFormat.format(date) // returning the display result
} catch (e: Exception) {
Log.e("+++", "+++ error: $e")
""
}
If there is better approach?
java.time and ThreeTenABP
My solution is to build three formatters for the three possible input formats and then for each input try the formatters in turn. For a simple demomstration of the idea:
DateTimeFormatter[] inputFormatters = {
DateTimeFormatter.ISO_INSTANT,
DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss xx yyyy", Locale.ROOT),
new DateTimeFormatterBuilder()
.appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter()
};
DateTimeFormatter displayFormatter
= DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.US);
for (String inputString : new String[] {
"2020-01-25T21:59:27Z",
"Sat Jan 25 20:06:07 +0000 2020",
"1566777888999"
}) {
// try the formatters in turn and see which one works
for (DateTimeFormatter formatter : inputFormatters) {
try {
ZonedDateTime dateTime = formatter.parse(inputString, Instant.FROM)
.atZone(ZoneId.systemDefault());
System.out.format("%-30s was parsed to %s%n",
inputString, dateTime.format(displayFormatter));
break;
} catch (DateTimeParseException ignore) {
// Ignore, try next format
}
}
}
In my time zone (Europe/Copenhagen) output from this snippet is:
2020-01-25T21:59:27Z was parsed to Jan 25, 2020
Sat Jan 25 20:06:07 +0000 2020 was parsed to Jan 25, 2020
1566777888999 was parsed to Aug 26, 2019
Since it is never the same date in all time zones, output will vary with time zone.
I am recommending java.time, the modern Java date and time API. I saw that you tagged the question simpledateformat, but the SimpleDateFormat class is notoriously troublesome and long outdated, so I recommend against using it. And I am exploiting the fact that your first format is standard ISO 8601 and that java.time has a built-in formatter for it, DateTimeFormatter.ISO_INSTANT.
My third input formatter, the one for the long value, regards the last three characters as milliseconds of the second and everything before it as seconds since the epoch. The net result is that it parses milliseconds since the epoch. A DateTimeFormatterBuilder was required to build this formatter.
A no-lib solution
I admit that I hate to write this. I would really have hoped that you could avoid the notoriously troublesome SimpleDateFormat class and its long outdated cronies like Date. Since I understand that yours is a no-lib app, both of Joda-Time and ThreeTenABP seem out of the question. Sorry. In this case since there is no way that SimpleDateFormat can parse a long, my approach is to take a taste of the string to determine the format and choose my way of parsing based on that.
DateFormat inputIso = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
// This format resembles the output from Date.toString
DateFormat inputDatelike
= new SimpleDateFormat("EEE MMM dd HH:mm:ss ZZZ yyyy", Locale.ROOT);
DateFormat displayFormat
= DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US);
displayFormat.setTimeZone(TimeZone.getDefault());
for (String inputString : new String[] {
"2020-01-25T21:59:27Z",
"Sat Jan 25 20:06:07 +0000 2020",
"1566777888999"
}) {
Date parsedDate;
if (Character.isDigit(inputString.charAt(0))) {
if (inputString.contains("-")) {
parsedDate = inputIso.parse(inputString);
} else {
// long number of millis
parsedDate = new Date(Long.parseLong(inputString));
}
} else {
parsedDate = inputDatelike.parse(inputString);
}
System.out.format("%-30s was parsed to %s%n",
inputString, displayFormat.format(parsedDate));
}
Output is exactly the same as before:
2020-01-25T21:59:27Z was parsed to Jan 25, 2020
Sat Jan 25 20:06:07 +0000 2020 was parsed to Jan 25, 2020
1566777888999 was parsed to Aug 26, 2019
Please be aware that here invalid input may cause either a NumberFormatException or a ParseException, so catch both. And only resort to this solution if there is no way that you can avoid it.
The line displayFormat.setTimeZone(TimeZone.getDefault()); is technically superfluous, but it makes explicit that the output depends on the time zone, and maybe more importantly, it tells you where you need to modify the code if you want output in a different time zone.
Question: Doesn’t java.time require Android API level 26?
java.time works nicely on both older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in. Only in this case use the method reference Instant::from instead of the constant Instant.FROM.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.

Date add GMT to hour

val startHour = SimpleDateFormat("HH:mm").format(pickup.begin())
The pickup.begin value is "Wed Apr 10 10:00:00 GMT+03:00 2019", so I need the start hour to be 10:00 +3h -> 13:00, but I get startHour value of 10:00.
I don't know how to add the GMT value to hour.
No, you’ve misunderstood. Wed Apr 10 10:00:00 GMT+03:00 2019 is (the textual representation of) a java.util.Date the value of which equals 2019-04-10T07:00 UTC. It seems your default time zone is GMT+03:00, and Date is trying to be friendly to you and print the time in this time zone, which is why it prints 10:00:00. 13:00 would certainly be incorrect no matter if you wanted the time in UTC or in your own default time zone.
The Date class returned from pickup.begin() is poorly designed and long outdated, so you may want to consider if a type from java.time, the modern Java date and time API, could be returned instead. It may also make the matter clearer.
Alternatively, convert that java.util.Date object to its modern counterpart, a java.time.Instant. Look for new conversion methods added to the old classes.
Instant instant = pickup.begin().toInstant() ; // Converting legacy `Date` object to modern `Instant` object.
Search Stack Overflow and read the Oracle Tutorial to learn more about Instant, OffsetDateTime, and ZonedDateTime classes.
You can use java.time on older Android versions if you add ThreeTenABP to your Android project. It’s the Android adaptation of the backport of java.time.
Links
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Try adding a Locale to your simpledateformat, matching the +3 timezone.
val startHour = SimpleDateFormat("HH:mm", Locale.Germany).format(pickup.begin())
You can try something like this
I take 'date' as string here
String date="something"
val sdf = SimpleDateFormat("HH:mm")
sdf.timeZone = TimeZone.getTimeZone("UTC")
val gmt = sdf.parse(date)
If this is your case
Date date="somevalue"
then
val gmt = sdf.format(date)
gmt will return you your local time

Showing correct time from Milliseconds with desired TimeZone

I'm developing an application which takes data from Google TimeZone API.
Simply I have time in milliseconds of desired place on Earth.
For Example : 1504760156000 it's showing Date time In London which is
Thu Sep 07 2017 09:55:56
As I'm in TimeZone +05:00 from UTC if I manipulate 1504760156000 these milliseconds it will show me whole date time like below:
Thu Sep 07 2017 09:55:56 GMT+0500 (Pakistan Standard Time)
but I want to show:
Thu Sep 07 2017 09:55:56 GMT+0100 (British Summer Time)
The problem is: I have correct date and time for London but enable to show/change TimeZone without changing Time because Time is correct according to London.
UPDATED
After getting some comments. You are not getting me here in my example.
Suppose I am in Pakistan and Time here is 1:55 PM so I asked GOOGLE API via my application to tell me whats the time in London at moment. Google API tell me time in London is 1504760156000 (9:55 AM) in milliseconds if I convert these milliseconds to Date Object it will print out like below:
Date date =new Date(1504760156000)
Thu Sep 07 2017 09:55:56 GMT+0500 (Pakistan Standard Time)
It will manipulate it according to my Local TimeZone but I want results like below
Thu Sep 07 2017 09:55:56 GMT+0100 (British Summer Time)
Updated 2
I prepared timestamp in seconds in UTC as Google Time Zone API needed timestamp UTC in form of seconds
"https://maps.googleapis.com/maps/api/timezone/json?location="+Latitude +","+Longitude+"&timestamp="+currentTimeUTCinSeonds+"&key="API KEY"
Google API respond me with below JSON against London.
{
"dstOffset" : 3600,
"rawOffset" : 0,
"status" : "OK",
"timeZoneId" : "Europe/London",
"timeZoneName" : "British Summer Time"
}
According to Docs:
Calculating the Local Time
The local time of a given location is the sum of the timestamp
parameter, and the dstOffset and rawOffset fields from the result.
I Sum up result timestamp+rawoffset+dstoffset*1000='1504760156000' (at moment when I tried it)
Code from Project
Long ultimateTime=((Long.parseLong(timeObject1.getDstOffset())*1000)+(Long.parseLong(timeObject1.getRawOffset())*1000)+timestamp*1000);
timeObject1.setTimestamp(ultimateTime); //its Sime POJO object to save current time of queried Location
Date date=new Date(ultimateTime);
date1.setText("Date Time : "+date);
As I said I'm manipulating result in Local Time Zone so it gave me below result at that time:
Thu Sep 07 2017 09:55:56 GMT+0500 (Pakistan Standard Time)
But I knew API gave me correct time. The problem is Local offset from UTC . I just want to change GMT+0500 to GMT+0100
Timestamps represent an "absolute" value of a time elapsed since epoch. Your currentTimeUTCinSeconds, for example, represent the number of seconds since unix epoch (which is 1970-01-01T00:00Z, or January 1st 1970 at midnight in UTC). Java API's usually work with the number of milliseconds since epoch.
But the concept is the same - those values are "absolute": they are the same for everyone in the world, no matter where they are. If 2 people in different parts of the world (in different timezones) get the current timestamp at the same time, they'll all get the same number.
What changes is that, in different timezones, this same number represents a different local date and time.
For example, the timestamp you're using, that corresponds to Sep 7th 2017 08:55:56 UTC, which value is 1504774556 (the number of seconds since epoch). This same number corresponds to 09:55 in London, 13:55 in Karachi, 17:55 in Tokyo and so on. Changing this number will change the local times for everyone - there's no need to manipulate it.
If you want to get a java.util.Date that represents this instant, just do:
int currentTimeUTCinSeconds = 1504774556;
// cast to long to not lose precision
Date date = new Date((long) currentTimeUTCinSeconds * 1000);
This date will keep the value 1504774556000 (the number of milliseconds since epoch). This value corresponds to 09:55 in London, 13:55 in Karachi and 17:55 in Tokyo.
But printing this date will convert it to your JVM default timezone (here is a good explanation about the behaviour of Date::toString() method). When you do "Date Time : "+date, it implicity calls toString() method, and the result is the date converted to your default timezone.
If you want the date in a specific format and in a specific timezone, you'll need a SimpleDateFormat. Just printing the date (with System.out.println or by logging it) won't work: you can't change the format of the date object itself, because a Date has no format.
I also use a java.util.Locale to specify that the month and day of week must be in English. If you don't specify a locale, it'll use the system default, and it's not guaranteed to always be English (and this can be changed, even at runtime, so it's better to always specify a locale):
// use the same format, use English for month and day of week
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'Z (zzzz)", Locale.ENGLISH);
// set the timezone I want
sdf.setTimeZone(TimeZone.getTimeZone("Europe/London"));
// format the date
System.out.println(sdf.format(date));
The output will be:
Thu Sep 07 2017 09:55:56 GMT+0100 (British Summer Time)
Note that I don't need to manipulate the timestamp value. I don't use the google API, but I think their explanation is too confusing and the code above achieve the same results with less complication.
In your specific case, you can do:
date1.setText("Date Time : "+sdf.format(date));
Java new Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).
To get a date from a timestamp, I use a org.threeten.bp.Instant with a org.threeten.bp.ZoneId to convert it to a timezone, creating a org.threeten.bp.ZonedDateTime. Then I use a org.threeten.bp.format.DateTimeFormatter to format it:
int currentTimeUTCinSeconds = 1504774556;
// get the date in London from the timestamp
ZonedDateTime z = Instant.ofEpochSecond(currentTimeUTCinSeconds).atZone(ZoneId.of("Europe/London"));
// format it
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEE MMM dd yyyy HH:mm:ss 'GMT'XX (zzzz)", Locale.ENGLISH);
System.out.println(fmt.format(z));
The output is the same:
Thu Sep 07 2017 09:55:56 GMT+0100 (British Summer Time)
In your case, just do:
date1.setText("Date Time : "+fmt.format(z));

Create Joda datetime UTC from String

I have the following String that I would like to change to UTC:
Thu Aug 24 07:38:32 GMT+01:00 2017
I'm using Joda-Time library.
I know how to create a new Datetime eg new dateTime(DateTimeZone.UTC) but how can I create a DateTime object from the above String?
I have tried the following but get an exception. Surely there must be another way to create a DT obect without chopping the original String up? What if the external API changes how it sends my app the orignal String, my String manipulation code would fail.
DateTimeFormatter df = DateTimeFormat.forPattern("dd-MMM-YYYY HH:mm");
String strOrigTime = "Thu Aug 24 07:38:32 GMT+01:00 2017";
DateTime dt = DateTime.parse(strOrigTime, df);
Log.e(TAG, "dt after parse = " + dt.toString());
Error:
Caused by: java.lang.IllegalArgumentException: Invalid format: "Thu Aug 24 07:38:32 GMT+01:00 2017"
at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:866)
at org.joda.time.DateTime.parse(DateTime.java:144)
The format used (dd-MMM-YYYY HH:mm) means: day (dd) followed by -, followed by month (MMM), followed by -, followed by year (YYYY) and so on (check the javadoc for more details).
This format doesn't match the input string (which has day-of-week followed by month, followed by day, then hour/minute/second, etc). So the first thing is to use a format that matches the input, otherwise you'll always get "Invalid format" errors.
Another detail is that day of week and month names are in English, so you must also use a java.util.Locale to specify the language you're using to parse the input. If you don´t use a locale, the system default will be used, and it's not guaranteed to always be English (and it can also be changed, even at runtime, so it's always better to specify one).
I also had to add "GMT" as a literal and call withOffsetParsed() to make it include the offset (+01:00) in the parsed object:
DateTimeFormatter df = DateTimeFormat
// use a pattern that matches input
.forPattern("EEE MMM dd HH:mm:ss 'GMT'Z yyyy")
// use English locale for day of week and month
.withLocale(Locale.ENGLISH)
// include the offset (+01:00) in the parsed object
.withOffsetParsed();
String strOrigTime = "Thu Aug 24 07:38:32 GMT+01:00 2017";
DateTime dt = DateTime.parse(strOrigTime, df);
System.out.println(dt.toString());
The output is:
2017-08-24T07:38:32.000+01:00
Then, you can set the UTC timezone to this object:
dt = dt.withZone(DateTimeZone.UTC);
System.out.println(dt.toString());
The output will be:
2017-08-24T06:38:32.000Z
Note that withZone method preserves the same instant (both dates represent the same point in time), just the timezone used in the output is changed. But both dates are equivalent (they represent the same instant, as 07:38 in offset +01:00 is the same as 06:38 in UTC).
If you want all dates to be converted to UTC, you can also set this in the formatter:
// set UTC to the formatter
df = df.withZone(DateTimeZone.UTC);
Then you don't need to call withZone in the DateTime objects: all parsed dates will be converted to UTC.
You also told that "if the external API changes how it sends my app the orignal String, my String manipulation code would fail".
Well, if the input String changes, you'll have to change your format as well - there's no other way, Joda-Time can't just guess what's the format, you have to tell it.
If you want to parse more than one format, there's a way to create a formatter that uses lots of different patterns and try to parse each one, until one of them works (or throw exception if none works). You could do something like that:
// format 1
DateTimeFormatter f1 = DateTimeFormat
// use a pattern that matches input
.forPattern("EEE MMM dd HH:mm:ss 'GMT'Z yyyy")
// use English locale for day of week and month
.withLocale(Locale.ENGLISH)
// include the offset (+01:00) in the parsed object
.withOffsetParsed();
// format 2
DateTimeFormatter f2 = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss Z");
// array of all possible formats
DateTimeParser[] parsers = { f1.getParser(), f2.getParser() };
// formatter that uses all the possible formats
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// append array of possible formats
.append(null, parsers)
// create formatter
.toFormatter().withLocale(Locale.ENGLISH).withOffsetParsed()
// set all parsed objects to UTC
.withZone(DateTimeZone.UTC);
// parse first format
System.out.println(DateTime.parse("Thu Aug 24 07:38:32 GMT+01:00 2017", formatter));
// parse second format
System.out.println(DateTime.parse("24/08/2017 07:38:32 +01:00", formatter));
Both dates will be parsed to:
2017-08-24T06:38:32.000Z
Then you can add new formats to the array, as needed.
Java new Date/Time API
Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
If you can't (or don't want to) migrate from Joda-Time to the new API, you can ignore this section.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
The code to parse the inputs is very similar, with minor changes in the format.
And I'm using the Instant class, because you want the output in UTC, and Instant represents a UTC instant:
// format 1
DateTimeFormatter f1 = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss O yyyy", Locale.ENGLISH);
// format 2
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss XXX");
// formatter with both formats
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// add format 1
.appendOptional(f1)
// add format 2
.appendOptional(f2)
// create formatter
.toFormatter(Locale.ENGLISH);
// parse first format
System.out.println(Instant.from(formatter.parse("Thu Aug 24 07:38:32 GMT+01:00 2017")));
// parse second format
System.out.println(Instant.from(formatter.parse("24/08/2017 07:38:32 +01:00")));
This will output:
2017-08-24T06:38:32Z
2017-08-24T06:38:32Z

Categories

Resources