Set timeZone in Date Java - java

Trying to set the timeZone in Date( I know Date is deprecated)
LOG.error(modifiedDate + "Date in service"); //Mon Sep 21 06:15:00 CDT 2020
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.ENGLISH);
format.setTimeZone(TimeZone.getDefault());
String dateString = format.format(modifiedDate);
LOG.error(dateString + " dateString"); //2020-09-21 06:15:00 -0500
Date date = format.parse(dateString);
LOG.error(date + " date after parsing"); //Mon Sep 21 06:15:00 CDT
timeZone isn't changing after the parse, what am I missing?

A java.util.Date object is lying. It's not a date. It is a moment in time, and it has no timezone information. It's just a wrapper around millis-since-epoch. It physically doesn't have the neccessary fields to store that.
(let's stop calling an instance of j.u.Date a 'date', because it just isn't one. You might as well call all instances of Integer a 'string', and all files 'a Grandma', about as sane). Let's call them by their right name: "crappy instant".
What you've done is set the timezone of the formatter object. This is working as intended: When you format this crappy instant object with your formatter-with-timezone-info-attached, you get the 'instant in time', translated to human-consumable form, with timezone info, adjusted for that timezone.
Then you ask to parse it back in but that's an issue: that's a very broad job; 'parse this bunch of characters into a "crappy instant"'.
In this case, the bag o' chars you've given includes timezone info already so that will be used. Remember, Date objects are crappy instants - they contain no timezone!!
Which should then raise some eyebrows - as last act you log date, which effectively logs the result of invoking the toString() method on your crappy instant instance. This will.... create a formatter with the system default timezone and use that to render it in human form, but make no mistake, that is NOT actually what is stored in that crappy instant object you have. All that is stored in there is 1600686900000. That's it.
I strongly suggest you follow deHaar's comment: No, you don't want crappy instant. If you have a value handed to you of type crappy instant (java.util.Date), convert it to Instant immediately.
If you must hand off a calculated time value back to some method or interface or field or whatnot and must be in date form, convert your nice instant instance (java.time.Instant) back to a crappy instant form.

Related

Single class to parse any Date Format in Java

I have been parsing dates in the below formats. I maintain an array of these formats and parse every date string in all these formats.
The code I used was -
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
simpleDateFormat.setTimeZone(timeZone); //timeZone is a java.util.TimeZone object
Date date = simpleDateFormat.parse(dateString);
Now I want to parse yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX format as well but using SimpleDateFormat the 6 digit microseconds are not considered. So I looked into java.time package.
To parse yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX formats I will be needing OffsetDateTime class and for other formats, I need ZonedDateTime class. The format will be set in DateTimeFormatter class.
Is there a way to use a single class like SimpleDateFormat to pass all the formats?
Since your Java 8 doesn’t behave as would be reasonably expected, I suggest that a workaround is trying to parse without zone first. If a zone or an offset is parsed from the string, this will be used. If the parsing without zone fails, try with a zone. The following method does that:
private static void parseAndPrint(String formatPattern, String dateTimeString) {
// Try parsing without zone first
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatPattern);
Instant parsedInstant;
try {
parsedInstant = formatter.parse(dateTimeString, Instant::from);
} catch (DateTimeParseException dtpe) {
// Try parsing with zone
ZoneId defaultZone = ZoneId.of("Asia/Calcutta");
formatter = formatter.withZone(defaultZone);
parsedInstant = formatter.parse(dateTimeString, Instant::from);
}
System.out.println("Parsed instant: " + parsedInstant);
}
Let’s try it:
parseAndPrint("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX", "2018-10-22T02:17:58.717853Z");
parseAndPrint("yyyy-MM-dd'T'HH:mm:ss.SSSSSS", "2018-10-22T02:17:58.717853");
parseAndPrint("EEE MMM d HH:mm:ss zzz yyyy", "Mon Oct 22 02:17:58 CEST 2018");
Output on Java 8 is:
Parsed instant: 2018-10-22T02:17:58.717853Z
Parsed instant: 2018-10-21T20:47:58.717853Z
Parsed instant: 2018-10-22T00:17:58Z
The first example has an offset in the string and the last a time zone abbreviation in the string, and in both cases are these respected: the instant printed has adjusted the time into UTC (since an Instant always prints in UTC, its toString method makes sure). The middle example has got neither offset nor time zone in the string, so uses the default time zone of Asia/Calcutta specified in the method.
That said, parsing a three or four letter time zone abbreviation like CEST is a dangerous and discouraged practice since the abbreviations are often ambiguous. I included the example for demonstration only.
Is there a way to use a single class…?
I have used Instant for all cases, so yes there is a way to use just one class. The limitation is that you do not know afterward whether any time zone or offset was in the string nor what it was. You didn’t know when you were using SimpleDateFormat and Date either, so I figured it was OK?
A bug in Java 8?
The results from your demonstration on REX tester are disappointing and wrong and do not agree with the results I got on Java 11. It seems to me that you have been hit by a bug in Java 8, possibly this one: Parsing with DateTimeFormatter.withZone does not behave as described in javadocs.

How to check LocalDateTime instance is local or UTC in java?

I have want to find whether my LocalDateTime instance holds local date&time or UTC date&time (like DateTime.Kind property in C#)
LocalDateTime date1=LocalDateTime.now(); // it is local
LocalDateTime date2=LocalDateTime.now(ZoneId.of("UTC")); // it is UTC
Anything like (date1.getKind() == Kind.UTC || date1.getKind() == Kind.Local) in Java?
The LocalDateTime object itself doesn't store the timezone information - it has only the date and time related fields: day, month and year; hour, minute, seconds and nanoseconds. But the now method uses a timezone or an offset to get the correct values for those fields.
That's because the answer to the questions "What day is today?" and "What time is it?" is not as simple as we might think.
It's common to think that the answer is as simple as taking a look at our calendar/cell phone/whatever and seeing the current date/time. But the technically correct answer is: "It depends".
It depends, basically, on where you are. At this moment, each place in the world has its own local date and time. For example, in July 5th, 2017: while it was 14h (or 2 PM) in São Paulo, it was 6 PM in London and 5 PM in UTC, but in Tokyo it was 2 AM of the next day (July 6th).
Each region in the world has specific rules to determine what's their local time during history, and of course it affects their local date.
And the concept that maps a country/city/region to these rules is a timezone.
That's why the now method needs a timezone. The ZoneId object loads all the timezone data to check what's the current date and time in that zone and adjust the day/month/year/hour/minute/second/nanosecond values accordingly. The version that receives no parameters (LocalDateTime.now()) will use the system's default timezone, so the API always uses some timezone in the end.
The timezone (or the offset, such as ZoneOffset.UTC) is used to get the correct values for day, month, year, hour, minute, second and nanosecond, and then - in the case of LocalDateTime and any other classes that don't keep the zone - discarded.
So, the concept might be a little different from what you're thinking. If I do:
// ZoneOffset.UTC is equivalent to ZoneId.of("UTC")
LocalDateTime date = LocalDateTime.now(ZoneOffset.UTC);
What this code does is: "take the current date and time in UTC, and get just the date and time fields, discarding the timezone/offset information".
When I ran this code, the current date/time in UTC was 2017-09-25T12:15:43.570Z, so the LocalDateTime has the value equivalent to 2017-09-25T12:15:43.570 (without any timezone information, just the date and time fields). If I call now() without arguments, it'll use the JVM default timezone (in my case, it's America/Sao_Paulo), and the value will be 2017-09-25T09:15:43.570.
So, with a LocalDateTime you can get the values, but you can't know from which timezone those values came from, because it doesn't keep this information.
If you want a UTC date, you must use another classes, designed to keep this information:
Instant.now() - this will always get the current UTC instant
OffsetDateTime.now(ZoneOffset.UTC) - with this you can query for date and time fields (such as getDayOfMonth() or getHour())
ZonedDateTime.now(ZoneOffset.UTC) - for UTC, it's the same as OffsetDateTime, but if you use a different timezone, it handles all timezone specific data, such as Daylight Saving Time changes.
To check if such object is in UTC, one way is to use the getZone() method:
ZonedDateTime z = ZonedDateTime.now(ZoneOffset.UTC);
System.out.println(z.getZone().equals(ZoneOffset.UTC)); // true
But if you use equivalents like ZoneId.of("UTC"), the equals method return false. So you could also check if z.getZone().getId() is equals to Z or UTC. With OffsetDateTime, it's similar:
OffsetDateTime odt = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(odt.getOffset().equals(ZoneOffset.UTC)); // true
With Instant you don't need to check, because it's always in UTC.
You can check all the available types in Oracle's date/time tutorial.
Both ZonedDateTime and OffsetDateTime can be converted to a LocalDateTime using the toLocalDateTime() method:
// dt will have the current date and time in UTC
LocalDateTime dt = ZonedDateTime.now(ZoneOffset.UTC).toLocalDateTime();
// or
LocalDateTime dt = OffsetDateTime.now(ZoneOffset.UTC).toLocalDateTime();
With this, the dt variable will have all the date and time fields (day/month/year, hour/minute/second/nanosecond) that corresponds to the current date/time in UTC. But it won't keep any timezone/offset information, so the LocalDateTime object itself can't know from which timezone those values came from.
I realize this question is a bit old, but I am learning Java and found myself trying to do something very similar. After some reading I found I could do what you are asking with this:
public static void main (String args[]) {
LocalDateTime now = LocalDateTime.now(Clock.systemDefaultZone()); // The clock argument is not really needed here.
String pattern = "dd MMM yyyy HH:mm:ss"; // Setup your format for output
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
System.out.println("Local time is: " + dtf.format(now));
LocalDateTime utc = LocalDateTime.now(Clock.systemUTC()); // Define alternate timezone
System.out.println("GMT/UTC is: "+dtf.format(utc));
}

How to keep original timezone with JodaTime

I have date in String format I need to parse. The format is as following with timezone from all over the world :
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
It seems that java.util.Date doesn't accept timezone with : separator. So I'm trying with Jodatime library :
DateTime formattedDate = DateTimeFormat.forPattern(pattern).parseDateTime(stringDate);
LocalDateTime formattedDate2 = DateTimeFormat.forPattern(pattern).parseLocalDateTime(stringDate);
MutableDateTime formattedDate3 = DateTimeFormat.forPattern(pattern).parseMutableDateTime(stringDate);
System.out.println(formattedDate);
System.out.println(formattedDate2);
System.out.println(formattedDate3);
These lines output :
2016-04-29T09:46:49.222+02:00
2016-04-29T12:16:49.222
2016-04-29T09:46:49.222+02:00
As far as I understand the formatter modify output timezone to comply on mine (I'm in Paris, UTC+2), but I want the output keep its original timezone. Is it possible to do it with Jodatime library? Or should I change for another?
Edit :
Actually I need to get a Date object on which the timezone offset would be 270 (the timezone offset of the stringDate : 4 hour and 30 minutes) in place of 120 (my local timezone offset):
System.out.println(formattedDate.toDate().getTimezoneOffset()); // I expect 270 but I get 120
What you missed is DateTimeFormatter#withOffsetParsed:
Returns a new formatter that will create a datetime with a time zone equal to that of the offset of the parsed string.
Otherwise the formatter will parse it into your local time zone (surprising, I know).
#Test
public void preserveTimeZone() {
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
DateTime dt = DateTimeFormat.forPattern(pattern).withOffsetParsed().parseDateTime(stringDate);
System.out.println(dt); // prints "2016-04-29T12:16:49.222+04:30"
}
As for your edit - java.util.Date does not hold time zone information and the deprecated getTimezoneOffset() method only
Returns the offset, measured in minutes, for the local time zone relative to UTC that is appropriate for the time represented by this Date object.
So you'd better use Joda Time or java.time classes to handle time zones properly.
When I run the same code that you have posted, I end up with
2016-04-29T02:46:49.222-05:00
2016-04-29T12:16:49.222
2016-04-29T02:46:49.222-05:00
which if you will notice, has different hour values AND time-zone values. However, if you look at their millis:
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
you'll see the output
1461916009222
1461950209222
1461916009222
So they have the same epoch time, but are printed out differently. This is due to the mechanism of toString() on DateTime objects, and how they are to be interpreted.
DateTime and LocalDateTime(MutableDateTime is just a mutable version of DateTime) deal with the same epoch time in different ways. LocalDateTime will always assume that epoch time is UTC time(per the javadoc for LocalDateTime), while DateTime will assume that epoch is represented in the time zone of the Chronology which it holds(per the javadoc again). If the TimeZone is not specified at construction time, then the Chronology will assume that you want the timezone of your default Locale, which is set by the JVM. In your case, the default Locale is Paris France, while mine is St. Louis USA. Paris currently holds a +2:00 time zone offset, while St. Louis has -5:00, leading to the different time zone representations when we print it.
To get even more annoying, those offsets can change over time. If I come back in 6 months and try to answer this again, my values will show -6:00 (stupid Daylight savings time!)
The important thing to remember is that these two dates have the same epoch time: we are talking about the same instant in time, we are just representing that time differently when we print it out.
If you want to use a different time zone for representing the output of the parse result, then you can set the DateTimeZone during formatting using DateTimeFormat.withZone() or DateTimeFormat.withLocale:
DateTimeFormatter sdf = DateTimeFormat.forPattern(pattern).withZone(DateTimeZone.forOffsetHoursMinutes(4,30));
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
which will print
2016-04-29 12:16:49.222+0430
2016-04-29 12:16:49.222
2016-04-29 12:16:49.222+0430
notice that the LocalDateTime version still prints out without the TimeZone. That's kind of the feature of LocalDateTime: it is represented without having to deal with all this business.
So that is why your printing values look weird. To further your question about getting a java.util.Date object from the parsed DateTime object: toDate will give you a java.util.Date which represents the same epoch time. However, java.util.Date behaves similarly to DateTime, in that unless otherwise stated, it will use the TimeZone of the default Locale. If you know the Locale ahead of time, then you can use the toDate(Locale) method to ensure you use that Locale's TimeZone offset.
It gets a lot harder if you don't know the TimeZone ahead of time; in the past, I've had to hand-parse the TimeZone hour and minute offsets to determine the proper TimeZone to use. In this exact case that's not too difficult, since the last 6 characters are extremely well-formed and regular(unless, of course, they aren't :)).

SimpleDateFormat change date while converting it to String

In my app, I am using SimpleDateFormat to convert the Date object to a string. But sometime when I change the time zone one by one to test whether the date I enter is the same as the date converted to a string, I found that it shows a different date. For example, suppose I have Thu Mar 15 00:00:00 GMT+08:00 2012 in my Date object, Now when I convert it to a string using SimpleDateFormat it works fine, but when I change the time zone one by one and check whether the date converted to string is same as it stored in Date object then in some cases it shows as 14-Mar-2012 instead of showing 15-Mar-2012. Why this happen? Can anyone please suggest me how to solve this out?
Code I have used:
SimpleDateFormat m_sdFormatter = new SimpleDateFormat("dd-MMM-yyyy");
String selected_date = m_sdFormatter.format(btnSelectedDt.getTime());
try this ,hope it may help you..
private String getDate(long timeStamp) {
DateFormat objFormatter = new SimpleDateFormat("dd-MM-yyyy");
objFormatter.setTimeZone(TimeZone.getDefault());
Calendar objCalendar = Calendar.getInstance(TimeZone.getDefault());
objCalendar.setTimeInMillis(timeStamp * 1000);
String result = objFormatter.format(objCalendar.getTime());
objCalendar.clear();
return result;
}
For example suppose i have Thu Mar 15 00:00:00 GMT+08:00 2012 in my Date object,
You haven't got that (even though that's no doubt what toString displays). A Date object doesn't contain a time zone. It contains an instant in time, which can be interpreted as different dates and times based on the calendar and time zone you use to interpret it. So that's midnight in once specific time zone - but the Date object itself is just a number of milliseconds since the unix epoch.
It's not clear exactly what you're doing, but you shouldn't be surprised that changing the time zone used in SimpleDateFormat will change the date written out. If you can describe in more detail what the larger goal is, we may be able to help you more. Note that if you can use Joda Time instead, that's a much better date/time API - but I know that it's quite large for use in an Android app.
In your SimpleDateFormat, mention the locale,
DateFormat objFormatter = new SimpleDateFormat("dd-MM-yyyy", locale);
where locale is of your choice. For eg, I use Locale.ENGLISH as it contains the date format that I require. Otherwise when you change ure locale in the device, the simpledateformat changes to current locale and you end up getting the wrong date.

Java - Store GMT time

My server has GMT+7, so if i move to another server has another GMT timezone, all date stored in db will incorrect?
Yes Q1 is correct, how about i will store date in GMT+0 timezone and display it in custom GMT timezone chosen by each member
How i get date with GMT+0 in java
1) To quote from the javadocs, Java millisecond timestamps are
the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
Therefore, as the timestamp is UTC/GMT, if you store it in the database this date will be correct no matter the time zone of the server.
2) Formatting the timestamp for display depending on the timezone would be the correct approach.
3) Creating a new java.util.Date instance and calling getTime() will get you the timestamp for GMT+0 in Java.
To add to mark's point, once you have a java.util.Date, you can "convert" it to any Timezone (as chosen by the User). Here's a code sample in Groovy (will work in Java with slight mods):
// This is a test to prove that in Java, you can create ONE java.util.Date
// and easily re-display it in different TimeZones
fmtStr = "MM/dd/yyyy hh:mm aa zzz"
myDate = new java.util.Date()
sdf = new java.text.SimpleDateFormat(fmtStr)
// Default TimeZone
println "Date formatted with default TimeZone: " + sdf.format(myDate)
// GMT
sdf.setTimeZone(TimeZone.getTimeZone("GMT"))
println "Date formatted with GMT TimeZone: " + sdf.format(myDate)
// America/New_York
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"))
println "Date formatted with America/New_York TimeZone: " + sdf.format(myDate)
// PST
sdf.setTimeZone(TimeZone.getTimeZone("PST"))
println "Date formatted with PST TimeZone: " + sdf.format(myDate)
By default when you get a date in java its in your current timezone, you can use the TimeZone class to get the timezone your system time is running in. Most databases supports that dates can be stored WITH timezone, which probably would be the right way to do this, but exactly what the format is can be dependant on which database you use.
I would suggest that you use Date only as a "holder" and propose that you use Calendar instances instead. To get a Calendar in the GMT time zone just use this:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+00"))
A Calendar will provide a great deal of benefit when you work with times and dates. For instance, adding an hour to this is simple:
cal.add(Calendar.HOUR, 1);
Lastly, when you need a Date you just use this:
Date date = cal.getTime();
For Q3, consider using ZonedDateTime class.

Categories