Possible bug in Java 8 SimpleDateFormat? [duplicate] - java

This question already has answers here:
Get GMT Time in Java
(12 answers)
How to Parse Date from GMT TimeZone to IST TimeZone and Vice Versa in android
(4 answers)
Getting the current time millis from device and converting it into a new date with different timezone [duplicate]
(2 answers)
How to set time zone of a java.util.Date?
(12 answers)
Closed 3 years ago.
Java 8 here, I have the following code:
public class PossibleBug {
public static void main(String[] args) {
new PossibleBug().run();
}
public void run() {
buildDate("20181205");
}
public Date buildDate(final String yyyyMmDd) throws ParseException {
TimeZone expectedTz = TimeZone.getTimeZone("America/New_York");
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.setTimeZone(expectedTz);
TimeZone actualTz = sdf.getTimeZone();
Date answer = sdf.parse(yyyyMmDd);
return answer;
}
}
So pretty basic stuff:
create a SimpleDateFormat and set its timezone to EST
Use the SDF to parse a date string
Result should be a date in EST as well
However at runtime, look at the debugger results:
How is this possible?!?! sdf.parse(yyyyMmDd) is returning a date formatted in GMT. Is there something I'm missing on my end or is this a bug in SimpleDateFormat?
I am able to invoke buildDate and run it from inside a different class and it seems to work fine:

Date stores no timezone. It's essentially just a wrapper around a long, storing millis after epoch.
When you print it (or when your debugger invokes the toString() method to get a string representation to display), your JVM's default timezone is used, irrespective of how it was created.
Date, despite the name, doesn't model a date: it's an instant in time.
Given that your input is "20181205", don't use Date: use classes from java.time like java.time.LocalDate.

If you take a look at the Java-Doc for SimpleDateFormat.parse(), you can see that the TimeZone might be overwritten:
The TimeZone value may be overwritten, depending on the given pattern and the time zone value in text. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.

The documentation says: "This parsing operation uses the calendar to produce a Date. All of the calendar's date-time fields are cleared before parsing, and the calendar's default values of the date-time fields are used for any missing date-time information. For example, the year value of the parsed Date is 1970 with GregorianCalendar if no year value is given from the parsing operation. The TimeZone value may be overwritten, depending on the given pattern and the time zone value in text. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations."
In short, SimpleDateFormat is a formatter/parser, not a utility for performing time zone conversions. If there's no TZ in the string you are parsing, you get the default value from Calendar.
Consider what would happen if you called setTimeZone, then parsed a string that actually contained a time zone itself? What would you expect to happen?
Also, note that Date doesn't contain a time zone. It's specifically defined as being the number of milliseconds since January 1, 1970, 00:00 UTC. Library functions apply a time zone when needed (like when converting to a String) and if you don't specify one, you'll get the default time zone. You see GMT because your default time zone is GMT or because your IDE always displays Date objects in GMT, and the person who said he got EST must have is default time zone set to EST.
In your case, you're parsing a string that does not contain a time zone at all. In fact, it doesn't even contain a time. Using Date to handle, uh, dates (I realize this is confusing, I mean dates without times), is likely to lead to mistakes, especially when your default time zone isn't UTC/GMT. I recommend using LocalDate and the LocalDate.parse method.

Related

How to change miillisecond to Date Object in yyyy-MM-dd HH:mm:ss format [duplicate]

This question already has answers here:
Unix epoch time to Java Date object
(7 answers)
Convert timestamp in milliseconds to string formatted time in Java
(10 answers)
Closed 5 years ago.
How to change milliseconds to Date object in yyyy-MM-dd HH:mm:ss format like 2017-04-12 23:14:52?
You cannot do that. For a couple of reasons.
TL;DR: Don’t use Date, use Instant. Neither of these can have a format in them. Formatting into a string is dependent on time zone, so you need to choose a time zone.
First, I understand from the discussion that you are asking for a java.util.Date object having the format yyyy-MM-dd HH:mm:ss. A Date object does not have and cannot have a format in it. The thing you should try to understand here is the difference between data itself and presentation of data to a user. An int may hold the value 25389, but it doesn’t hold it in the format 25389 (in fact the internal representation is quite different from 25389). The same int may be presented to a user as 25389, 000025389, 25,389 or +25389, just to mention a few out of many possibilities. The formatting happens outside the int while the int stays just the same.
Similarly, a Date object holds a point in time. The same date may be formatted into for example 2017-04-12 23:14:52 or April 12, 2017 11:14:52 PM. It may even be formatted for different time zones, which would be a good idea if the system has users in different time zones. Alternatively we may show the user a calendar leaf and/or a clock showing the time. Again, formatting happens outside of the Date while the Date stays just the same.
Elaborating on the time zone issue, the same point in time represented by the same millisecond value could be formatted to 2017-04-12 17:44:52 in UTC, 2017-04-12 19:44:52 in my time zone, 2017-04-12 23:14:52 in Asia/Kolkata time zone or even 2017-04-13 05:44:52 in Pacific/Auckland time zone. Note that in the last case not even the date is the same. So there is not just one way to change your milliseconds into the format you asked for. We need to know which time zone you want it for before we can help you.
So what I believe you need is not one thing, but two
A way to store your point in time in your program.
A way to format your point in time into a string in yyyy-MM-dd HH:mm:ss format for a user in some time zone.
For storing your point in time, use either of
A long for the milliseconds value you already have
A java.time.Instant object.
Why didn’t I mention java.util.Date? Because this class is long outdated. Its design turned out to be troublesome very quickly. They tried to repair it by deprecating most of the methods and introducing java.util.Calendar, but that didn’t work very well either. Finally, drawing on the experiences from a library known as Joda-Time they introduced the java.time classes in Java 8 in 2014. That’s three years ago as of writing, and counting. So IMHO we should by now have thrown Date and friends overboard and started using the newer classes. So prefer Instant over Date.
Changing your milliseconds to an Instant is straightforward:
long milliseconds = 1492019092000L;
Instant pointInTime = Instant.ofEpochMilli(milliseconds);
For formatting your instant into a string for the user, as I said, we require a time zone. Then do
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
String formattedDateTimeString = pointInTime.atZone(ZoneId.of("Asia/Kolkata"))
.format(formatter);
So you need to fill in the desired time zone where I put ZoneId.of("Asia/Kolkata"). If you want to use the JVM’s current time zone setting, just fill in ZoneId.systemDefault(). Beware, though, that the time zone setting may be changed, even by an unrelated program running in the same JVM, so relying on this may be fragile.
The result of the above code snippet is a string like
2017-04-12 23:14:52
PS If after reading the above you really insist, here’s how to get a java.util.Date from the above:
Date myOutdatedDateInstance = Date.from(pointInTime);
(and excuse me for repeating, it still doesn’t have the desired format, that is not possible).
You can try this sample code.
public class MillDateConverter {
public static String dFormat = "yyy-MM-dd HH:mm:ss";
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dFormat);
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
long milliSec=Long.parseLong("1086073200000");
System.out.println(milliSec);
calendar.setTimeInMillis(milliSec);
System.out.println(simpleDateFormat.format(calendar.getTime()));
}
}

Java Date has shifted hours after parsing

I have a rest service that takes date as a string and an adapter parses it to java.util.date:
private static final String FORMAT_DATE = "dd.MM.yyyy";
/*
* Omitted.
*/
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE);
sdf.setLenient(false);
try {
setTime(sdf.parse(stringRealizationDate).getTime());
invalidDate = false;
} catch (ParseException parseException) {
invalidDate = true;
LOG.error("Instantiation failed");
}
The stringRealizationDate is: 23.02.2017 and after parsing it it becomes 22.02.2017 23:00.
How to make it always be 23.02.2017 00:00 without any time zones aspects? It cannot be shifted under no circumstances.
You cannot rely on parsing a String not explicitly containing timezone information without somehow defining the timezone.
The SimpleDateFormat internally uses a Calendar to store the parsing result. This calendar will be instantiated with system locale and therefore system timezone. Your stringRealizationDate will be treated as being passed with the context of system timezone which seems to be +01:00.
Now, SimpleDateFormat.parse() returns a Date. Date however is intended to store UTC values, so the +01:00 timezone offset causes to have one hour being substracted during conversion to UTC.
One solution would be having the correct timezone (+01:00) set when using your Date so the calculation from UTC to +01:00 would result in the correct value being e.g. printed on screen. But that is some kind of tricky to manage in larger applications.
Another solution would be using SimpleDateFormat.setTimeZone() before calling parse() to explicitly define the timezone the formatter should assume your input has (if not defined there explicitly). However, the problem with having a Datethat always carries UTC timezone values is not solved at all.
The best solution would be using the new Time API which is available since Java8. There you have classes explicitly holding non-timezoned values, in your case LocalDate.
The interesting method there would be LocalDate.parse(CharSequence text,
DateTimeFormatter formatter) since it allows you to pass your input String and define your expected date format. But be alert, you cannot convert a LocalDate to a Unix timestamp without passing a timezone (and making assumptions about the time parts of it) since it does not represent a point-in-time but a fragment of it, the date part.

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 :)).

How to check a date (in UTC format) is 48 hours before today date [duplicate]

This question already has answers here:
How to check if a date Object equals yesterday?
(9 answers)
Closed 8 years ago.
I tried to convert the new today's date object to UTC. But no luck since new date object is always in local time zone. I have been trying to verify two dates using 'before' method. date1 is in UTC format. date2 is today.
The below code always prints the today's date object in local time zone.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class test {
/**
* #param args
*/
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String todayStr = sdf.format(new Date());// This string is in UTC. But I need date object in UTC.
try {
System.out.println(sdf.parse(todayStr));// This is still printing in local time zone.
} catch (ParseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
A Date object isn't in any time zone - it's just a wrapper around the number of milliseconds since the Unix epoch. The Unix epoch is typically described in terms of UTC (namely midnight at the start of January 1st 1970) but it's really just a point in time.
To get the point in time 48 hours before "now", you can use something like:
Date earlier = new Date(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(48));
You can compare that with any Date object to see which occurred earlier in time - again, with no reference to any particular time zone.
(This isn't an easily testable solution, admittedly - I prefer to abstract System.currentTimeMillis() behind a Clock interface, so that I can test with a fake clock set to whatever time I want.)
Unless you need a string representation, ignore it - as soon as you want a string representation, you need to think about both which calendar system and time zone you're interested in.
Note that you talk about "today's date" in the question - if you mean the start of a particular day, then the time zone is relevant, and you'll need to do more work.
Finally, I'd suggest that if you possibly can, you use either java.time.* from Java 8, or Joda Time instead of java.util.Date etc. The first two are both much better APIs than the last.
The below code always prints the today's date object in local time zone.
Yes, that's because Date.toString() uses the local time zone. That's just what it (unfortunately) does, and it doesn't mean that the Date object is "in" the local time zone.
See the correct answer by Jon Skeet for details.
Yesterday and Day Before
Your requirement is unclear, but it seems you want to test if the target date-time string represents a date-time that lies within the 48-hour period before the beginning of today as defined by UTC time zone. In other words, yesterday or day before.
Avoid java.util.Date
Avoid using the java.util.Date and .Calendar classes bundled with Java. They are notoriously troublesome. Instead use either Joda-Time or the new java.time in Java 8 (inspired by Joda-Time). Both libraries have a date-time class that knows its own assigned time zone, unlike java.util.Date.
Half-Open Span Of Time
And both libraries have classes to represent a span of time. Comparisons are done by the "Half-Open" approach where the beginning is inclusive and the ending exclusive. So your two-day period is defined as the first moment of day-before-yesterday (inclusive) and running up to, but not including, the first moment of today (exclusive).
ISO 8601
Your string format is defined by the ISO 8601 standard. Both Joda-Time and java.time use ISO 8601 for defaults in parsing and generating strings. So they have a built-in formatter to parse your string. Merely pass the string.
Immutable Objects
Joda-Time uses immutable objects. Rather than modify ("mutate") an existing object, a new object is instantiated with values based on the original. The reason is for thread-safety.
Joda-Time
Here is some example code in Joda-Time 2.4.
DateTime target = new DateTime( "2014-01-02T03:04:05.789Z", DateTimeZone.UTC );
DateTime nowUtc = DateTime.now( DateTimeZone.UTC );
DateTime today = nowUtc.withTimeAtStartOfDay();
DateTime dayBeforeYesterday = today.minusDays( 2 ).withTimeAtStartOfDay();
Interval interval = new Interval( dayBeforeYesterday, today ); // Half-Open.
boolean hit = interval.contains( target );

Timezone confusion with getRawOffset method

One of my project implemented the below method, i am looking into the one of the date issue and trying to understand below method which converts given date to GMT, but confused with the output.
Input Date value : 2010-11-29 04:00:00.0
Output Date value : Sun Nov 28 20:00:00 PST 2010
My machine is running in pacific timezone(PST), if it is returning GMT, i would expect "2010-11-29 11:00:00.0", can you please clarify what is the purpose of getRawOffset() method and why it is returning that output?
public static Date convertToGMT(Date date) {
TimeZone jvmTimeZone = TimeZone.getDefault();
long newTime = date.getTime() + jvmTimeZone.getRawOffset();
if (jvmTimeZone.inDaylightTime(date)) {
newTime = newTime + jvmTimeZone.getDSTSavings();
}
return new Date(newTime);
}
PST is UTC-8, therefore getRawOffset() returns a negative value:
2010-11-29 04:00:00.0 + (-8 hours) = 2010-11-28 20:00:00.0
However, the whole thing you are trying to do is wrong.
Date represents an instant, a point on the timeline that's not associated with any timezone. Therefore it makes no sense to convert Date from one timezone to another. The only thing you can do with Date is to convert it to local date/time in certain timezone, and vice versa.
Also I'd suggest you to use Joda Time too. Jode Time makes distinction between an instant (DateTime) and a local representation of that instant (LocalDateTime) more clear.
The code is just crap because a java.util.Date is always GMT. It is never a local timestamp so trying to convert it from an imaginary local timestamp to GMT is conceptual nonsense.
The original intention was probably to misuse a Date as a kind of local timestamp (in contradiction to its specification) that is as thin wrapper around local time millis. Remember following relation: [UTC-millis] + [offset-millis] = [local-millis] I would just have used a long primitive for this calculation, not j.u.Date.
So you can see many inconsistencies in the code. The newTime variable appears to be a kind of local millis but is wrapped then as j.u.Date and returned result of a method which pretends to convert to GMT (more chaos is hardly possible).
Edit 8 years later when we have now got the java.time-package.
An instance of java.util.Date can be converted to a java.time.Instant. New conversion methods had been added to java.util.Date for this purpose. And we should all know that an instant is related to GMT, and nothing else. So will someone try to tell us that following code makes any sense?
public static Instant convertToGMTNew(Instant instant) {
java.util.Date date = java.util.Date.from(instant);
return convertToGMTOld(date).toInstant(); // using the method above
}
Not really. It is the same error. The new changed!!! instant would indeed be based on a fake calculation. Instant and java.util.Date have no conversion methods involving any zone calculations for a good reason. Both are just related to GMT and not to any "local" instants.

Categories