I've run into a strange behavior of SimpleDateFormat, i don't know how to deal with.
I need to parse a date in a specific format (Day of Week, then day, then Month, then Year, Then time).
However, I've run into a behaviour, when parsing a date gives me a very strnge result (other date). Here is a small, self-contained example, and it's output on my machine.
public static void main(String[] args) throws Exception {
test("E YYYY kk:mm:ss");
test("E d YYYY kk:mm:ss");
test("E d MMMM YYYY kk:mm:ss");
}
public static void test(String format) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat(format);
Date now = new Date();
System.out.println(now);
String formattedNow = sdf.format(now);
System.out.println(formattedNow);
Date parsedFormattedNow = sdf.parse(formattedNow);
String formattedParsedNow = sdf.format(parsedFormattedNow);
System.out.println(formattedParsedNow);
System.out.println(formattedNow.equals(formattedParsedNow));
}
Output:
Sat Apr 27 13:48:07 MSK 2013
Sat 2013 13:48:07
Sat 2013 13:48:07
true
Sat Apr 27 13:48:07 MSK 2013
Sat 27 2013 13:48:07
Sat 5 2013 13:48:07
false
Sat Apr 27 13:48:07 MSK 2013
Sat 27 April 2013 13:48:07
Sat 5 January 2013 13:48:07
false
Why do then 27 transforms into 5, and April to January?
Well, there are two aspects here:
The pattern E d YYYY kk:mm:ss doesn't contain a month indicator at all. So after formatting to "Sat 27 2013 13:48:07" how are you expecting the parsing part to work out the month?
All your patterns use YYYY which is the week-year, not calendar year. This should usually be used with "day of week, week of week-year" patterns. If you use yyyy instead, the final pattern will work.
The only reason the first pattern appears to work is that you're not actually setting anything other than the "day of week" and the year (and time, of course). If you print out parsedFormattedNow (with no other formatting) you'll see that the parse result is actually January 5th. It's just you don't notice it, because it's still a Saturday.
Related
I'm having an issue with SimpleDateFormat in Java. My code is returning the wrong date. Help please.
String date_str = "Tue Mar 08 09:44:55 EST 2022";
Date date = new SimpleDateFormat("EEE MMM D HH:mm:ss z yyyy").parse(date_str);
// Output: Sat Jan 08 14:44:55 GMT 2022
d, not D for the day of the month. D is the day in the year, so 8 is the 8 of January.
String date_str = "Tue Mar 08 09:44:55 EST 2022";
Date date = new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy").parse(date_str);
// ^---Here
// Output: Tue Mar 08 15:44:55 CET 2022
Check the full list of patterns here.
This caused a Y2K-style bug in my software if you can imagine. Strange thing is the off-by-one year calculation only occurs for two days in the year, which I'm less sure how to troubleshoot.
The output:
03-Jan-2013
02-Jan-2013
01-Jan-2013
31-Dec-2013 ** strange
30-Dec-2013 ** strange
29-Dec-2012
28-Dec-2012
27-Dec-2012
26-Dec-2012
25-Dec-2012
I am not sure which part of the Java date utilities could cause such an error.
The code (since the test is so small I included a complete working program):
import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat;
public class DateT {
private static String getFormattedBackscanStartTime(int days) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-YYYY");
Calendar workingDate = Calendar.getInstance();
workingDate.add(Calendar.DATE, -1 * days);
String formattedStartTime = dateFormat.format(workingDate.getTime());
return formattedStartTime;
}
public static void main(String args[]) {
for(int i = 35; i < 45; i++) {
System.out.println(getFormattedBackscanStartTime(i));
}
}
}
This is the problem:
"dd-MMM-YYYY"
YYYY is the week-year, not the calendar year. You want yyyy instead.
The last two days of calendar year 2012 were in the first week of week-year 2013. You should normally only use the week year in conjunction with the "week of year" specifier (w).
I am assuming you are using java 1.7.
The code snippet above will not work with java 1.6 as SimpleDateFormat("dd-MMM-YYYY") will raise an java.lang.IllegalArgumentException (YYYY is not available in java 1.6)
You need to use yyyy instead of YYYY.
Y -> week-year
y -> year
here
EDIT
Works great with yyyy:
$ java DateT
03-Jan-2013
02-Jan-2013
01-Jan-2013
31-Dec-2012
30-Dec-2012
29-Dec-2012
28-Dec-2012
27-Dec-2012
26-Dec-2012
25-Dec-2012
The problem lies in your date format string - year should be yyyy not YYYY.
If you print the value of workingDate.getTime() in each iteration of the loop, you'll see it has the expected values:
Thu Jan 03 11:19:33 EST 2013
Wed Jan 02 11:19:33 EST 2013
Tue Jan 01 11:19:33 EST 2013
Mon Dec 31 11:19:33 EST 2012
Sun Dec 30 11:19:33 EST 2012
Sat Dec 29 11:19:33 EST 2012
Fri Dec 28 11:19:33 EST 2012
Thu Dec 27 11:19:33 EST 2012
Wed Dec 26 11:19:33 EST 2012
Tue Dec 25 11:19:33 EST 2012
Therefore the problem lies in the SimpleDateFormat usage.
For the sake of completeness, here’s the modern answer using LocalDate (as recommended by Basil Bourque in a comment).
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class DateT {
private static DateTimeFormatter dateFormatter
= DateTimeFormatter.ofPattern("dd-MMM-uuuu", Locale.US);
private static String getFormattedBackscanStartTime(int days) {
return LocalDate.now(ZoneId.systemDefault()).minusDays(days).format(dateFormatter);
}
public static void main(String args[]) {
for(int i = 155; i < 165; i++) {
System.out.println(getFormattedBackscanStartTime(i));
}
}
}
Running this today I got
04-Jan-2017
03-Jan-2017
02-Jan-2017
01-Jan-2017
31-Dec-2016
30-Dec-2016
29-Dec-2016
28-Dec-2016
27-Dec-2016
26-Dec-2016
A few things to note:
Give an explicit locale to your formatter to control the langauge of your output. Even if you just pass Locale.getDefault() you are telling the reader that you have thought about locale and made a decision.
Similarly give an explicit time zone to LocalDate.now() to tell the reader you’ve made a decision (for example ZoneId.of("America/New_York") for a specific time zone; ZoneId.systemDefault() for the JVM’s current time zone setting).
I find the code simpler and more straightforward than the code using the oldfashioned Calendar class. This is typical for the newer classes.
I have used uuuu for year. yyyy (lowercase) works too, there will only be a difference for years before the common era (AKA BC).
You need to use lower case y for the year. Try this:
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");
I have an array of Strings with the dates e.g.:
Tue, 09 Feb 2016 14:07:00 GMT;
Tue, 09 Feb 2016 19:55:00 GMT.
Now I want to find the most recent date on this list. In order to do that, I try to deserialize these strings to java.util.Date objects and after that compare them.
The code sample of java.util.Date object generation:
strDate = "Tue, 09 Feb 2016 14:07:00 GMT";
DateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
Date date;
try {
date = format.parse(strDate);
//Result: Tue Feb 09 16:07:00 IST 2016
System.out.println("Result: " + date.toString());
} catch(ParseException e) {
e.printStackTrace();
}
My questions:
Why is the result in IST 2016 time zone and not in GMT? What does the IST 2016 stand for? Is it India Standard Time or Irish Standard Time or Israel Standard Time?
The initial string is in EEE, dd MMM format, the SimpleDateFormat pattern is also in this format, thus, why the result is in EEE, MMM dd format?
How can get a java.util.Date object in the same timezone as the initial string, in my case — GMT?
Is the approach I'm using to find the most recent date in the list is OK or there is more convenient/modern way to do that in Java 8, e.g., with the usage of LocalDateTime?
You are relying to Date.toString() to print your date when you should format it to a String with a formatter. What you are seeing is just the default pattern of Date.toString(). What you must keep in mind is that a Date does not have a timezone. You are seeing the output with the IST timezone, this must be because the current locale for the JVM is set to some specific locale for which the timezone name is "IST".
With regard to your point 4, yes, you can do it much cleaner with Java Time API introduced in Java 8. You can create a List of your strings to parse, create a DateTimeFormatter to parse it, and keep the maximum date value.
public static void main(String[] args) {
List<String> dates = Arrays.asList("Tue, 09 Feb 2016 14:07:00 GMT", "Tue, 09 Feb 2016 19:55:00 GMT");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
ZonedDateTime maxDate = dates.stream()
.map(s -> ZonedDateTime.parse(s, formatter))
.max(ZonedDateTime::compareTo)
.get(); // or .orElse(null)
System.out.println(maxDate);
}
This code is using a ZonedDateTime to keep the time-zone of the incoming strings.
Your computer seems to be set to IST. To force GMT output, import java.util.TimeZone and do this in your try block:
format.setTimeZone(TimeZone.getTimeZone("GMT"));
date = format.parse(strDate);
System.out.println("Result: " + format.format(date));
I have 2 dates in String with format (MMM dd, yyyy hh:mm:ss a).
How to convert two Strings into date and find the difference in minutes ?
DateFormat df = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a", Locale.ENGLISH);
Date start = df.parse(startstring);
Date end = df.parse(endstring);
After I want to take the difference in minutes and I am using this code:
long result = ((end.getTime()/60000) - (start.getTime()/60000));
But the result is 0. How can I solve this problem ?
My Strings are :
start: Fri Mar 07 23:45:43 GMT+04:00 2014
end: Fri Mar 07 23:46:01 GMT+04:00 2014
You could use this approach (first calculate the minutes since epoch, then subtract them) -
private static long getTimeInMinutesFromEpoch(Date d) {
if (d == null) {
return 0;
}
return d.getTime() / (60 * 1000);
}
public static long getMinuteDifference(Date a, Date b) {
return Math.abs(getTimeInMinutesFromEpoch(b)
- getTimeInMinutesFromEpoch(a));
}
public static void main(String[] args) throws ParseException {
String startstring = "Mar 07, 2014 23:45:43 PM";
String endstring = "Mar 07, 2014 23:46:01 PM";
DateFormat df = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a",
Locale.ENGLISH);
Date start = df.parse(endstring);
Date end = df.parse(startstring);
System.out.println(getMinuteDifference(start, end));
}
Output is
1
From the looks of it you're creating the start date immediately before the end date (unless there is non-included relevant information).
Date start = df.parse(startstring);
Date end = df.parse(endstring);
These are going to be created in exactly the same minute and therefore give you 0 when you try to find the difference in minutes.
EDIT
Your times:
start: Fri Mar 07 23:45:43 GMT+04:00 2014
end: Fri Mar 07 23:46:01 GMT+04:00 2014
are 18 seconds apart. You're going to get 0 for the difference in minutes.
You can make Calendar object instead of Date and then you can get the minutes using Calendar.get(Calendar.MINUTE). Note that by using this logic, the difference between 22:45:43 GMT+04:00 2014 and 23:45:43 GMT+04:00 2014 will be zero minutes.
this is what i do to get date in java :
Date date = (new GregorianCalendar(year,month - 1, i)).getTime(); // year,month,day
SimpleDateFormat f = new SimpleDateFormat("EEEE");
nameofday = f.format(date);
when i print the date Object it gives me the answer like follows :
Sun Apr 01 00:00:00 IST 2012
Mon Apr 02 00:00:00 IST 2012
Tue Apr 03 00:00:00 IST 2012
Wed Apr 04 00:00:00 IST 2012
Thu Apr 05 00:00:00 IST 2012
Fri Apr 06 00:00:00 IST 2012
Sat Apr 07 00:00:00 IST 2012
from this i want to get only the day ex: 01,02,03,04,05,etc.
How to do this in java?
Regards
Tony
If you want the day as a number, use:
int dayOfMonth = gregorianCalendarInstance.get(Calendar.DATE);
If you want a string like "05", change your date format to dd, that is:
SimpleDateFormat f = new SimpleDateFormat("dd");
Your output is not the nameofday. If you printed nameofday, it would print "saturday" or "friday". If you want the day in the month on two characters, as indicated in the javadoc of SimpleDateFormat, you must use "dd" for the pattern:
Date date = ...
DateFormat df = new SimpleDateFormat("dd");
System.out.println(df.format(date));
You should really learn to read documentation.