Getting wrong data when using SimpleDateFormat.parse() - java

I am getting the strangest error, when trying to parse a string as a calendar.
It seems that it messes up the Date object which I use to set the result calendar's time. The error is pretty inconsistent (or I see no logic in it). Can anyone point out what I might be doing wrong ?
public class caltest{
public static final SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss.SSS");
public static void main(String[] args) {
String date1 = "1992-03-11 12:00:12.123";
String date2 = "1993-03-11 12:00:12.123";
String date3 = "1994-03-11 12:00:12.123";
String date4 = "1995-03-11 12:00:12.123";
parseStringAsCalendar(date1);
parseStringAsCalendar(date2);
parseStringAsCalendar(date3);
parseStringAsCalendar(date4);
}
public static String calendarToString(Calendar cal) {
return sdf.format(cal.getTime());
}
public static Calendar parseStringAsCalendar(String s) {
Date time = null;
try {
time = sdf.parse(s);
} catch (ParseException e) {
System.out.println("Exception");
e.printStackTrace();
}
System.out.println(time.toString());
GregorianCalendar ret = new GregorianCalendar();
ret.setTime(time);
return ret;
}
}
The output is :
Sun Dec 29 12:00:12 CET 1991
Sun Dec 27 12:00:12 CET 1992
Sun Dec 26 12:00:12 CET 1993
Sun Jan 01 12:00:12 CET 1995

You're using YYYY in your format specifier, which is week year (as of Java 7, I believe). You want yyyy, which is just "year". (See the SimpleDateFormat documentation.)
I suspect the rest of the date was out because you tried to also specify the month and day, which aren't really "features" in the week year... if you'd specified the "week of week year" and day of week, it might have given some more sensible results, but only if you really meant to use week years, which I doubt :)

Use this:
public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
It's lower case y for year, not upper case Y. With that, the result is:
Wed Mar 11 12:00:12 EST 1992
Thu Mar 11 12:00:12 EST 1993
Fri Mar 11 12:00:12 EST 1994
Sat Mar 11 12:00:12 EST 1995
See here:
http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

java.time
The question and existing answers use SimpleDateFormat which was the correct thing to do in 2012. In Mar 2014, the java.util date-time API and their formatting API, SimpleDateFormat were supplanted by the modern Date-Time API. Since then, it is strongly recommended to stop using the legacy date-time API.
Solution using the modern date-time API: As you can learn from the documentation, the symbol Y is used for week-based-year whereas we need y (year-of-era) in this case. However, I prefer u to y.
Note that your date-time string has just date and time units (no time-zone or time-zone offset etc.). The java.time API provides you with LocalDateTime to represent such an object.
In case, you need to obtain a date-time object with time-zone or one representing just a moment in time, java.time provides you with specific types. You can check overview of java.time types here.
With Java 8, java.util date-time API was also upgraded to make it easy to switch to java.time API e.g. if you need java.util.Date instance from a an Instant, you can use Date#from.
Demo:
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Date;
import java.util.Locale;
class Main {
public static void main(String[] args) {
String strDateTime = "1992-03-11 12:00:12.123";
DateTimeFormatter parser = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS", Locale.ENGLISH);
// An alternative parser
DateTimeFormatter ldtParser = new DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ').append(DateTimeFormatter.ISO_LOCAL_TIME).toFormatter(Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse(strDateTime, parser);
System.out.println(ldt);
// Parsing using the alternative parser
ldt = LocalDateTime.parse(strDateTime, ldtParser);
System.out.println(ldt);
// Converting LocalDateTime to a ZonedDateTime
// Replace ZoneId.systemDefault() with applicable ZoneId e.g.
// ZoneId.of("America/New_York")
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zdt = ldt.atZone(zoneId);
System.out.println(zdt);
// Alternatively,
zdt = ZonedDateTime.of(ldt, zoneId);
System.out.println(zdt);
// Obtaining an Instant
Instant instant = zdt.toInstant();
System.out.println(instant);
// In case you need an instance of java.util.Date
Date date = Date.from(instant);
}
}
Output in my time-zone, Europe/London:
1992-03-11T12:00:12.123
1992-03-11T12:00:12.123
1992-03-11T12:00:12.123Z[Europe/London]
1992-03-11T12:00:12.123Z[Europe/London]
1992-03-11T12:00:12.123Z
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
Note: Check this answer and this answer to learn how to use java.time API with JDBC.

Related

Convert from Date to LocalDateTime with format "yyyy-MM-dd HH:mm:ssXXX"

I currently have a Date e.g. "2015-10-10T14:34:22Z". I need the year from the Date object for my new LocalDateTime object as this object will be set to that Date object year and have a specific month, day and time set (yyyy-06-15T17:00:00Z).
Taking the getYear() from Date has the 1900 issue.
I get the date via LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
Create another object to set the desired month and day
Create LocalDateTime object to set the time
I feel I am doing it a very long convuluted way and would like to ask if there are any other shorter and better alternatives.
EDIT:
Are there are any other shorter and better alternatives?
Since your date-time string has timezone offset information. So, you can parse it to an OffsetDateTime object and then get the year from it.
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
public class Main {
public static void main(String[] args) {
String strDateTime = "2015-10-10T14:34:22Z";
OffsetDateTime odt = OffsetDateTime.parse(strDateTime);
System.out.println(odt);
System.out.println(odt.getYear());
// If you want to get LocalDateTime from OffsetDateTime
LocalDateTime ldt = odt.toLocalDateTime();
System.out.println(ldt);
}
}
Output:
2015-10-10T14:34:22Z
2015
2015-10-10T14:34:22
Note that Z in the date-time string stands for Zulu date-time and specifies a timezone offset of +00:00 hours or date-time at UTC.
Taking the getYear() from Date has the 1900 issue.
The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. I suggest you should stop using them completely and switch to the modern date-time API. Learn more about the modern date-time API at Trail: Date Time.
If you are working for an Android project and 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.
Converting from legacy API to the modern API:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Date;
public class Main {
public static void main(String[] args) throws ParseException {
String strDateTime = "2015-10-10T14:34:22Z";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date date = sdf.parse(strDateTime);
Instant instant = date.toInstant();
OffsetDateTime odt = instant.atOffset(ZoneOffset.UTC);
System.out.println(odt);
System.out.println(odt.getYear());
// If you want to get LocalDateTime from OffsetDateTime
LocalDateTime ldt = odt.toLocalDateTime();
System.out.println(ldt);
}
}
Output:
2015-10-10T14:34:22Z
2015
2015-10-10T14:34:22
Note: If you want to convert the Instant into ZonedDateTime at UTC, you can do it as follows:
ZonedDateTime zdt = instant.atZone(ZoneOffset.UTC);
or the following:
ZonedDateTime zdt = instant.atZone(ZoneId.of("Etc/UTC"));
Note that the three-letter name for a ZoneId is error-prone i.e. avoid using something like ZoneId.of("UTC").
What is wrong with your code:
You are using .atZone(ZoneId.systemDefault()) which is converting the object of Instant to an object of ZonedDateTime with your JVM's timezone. You have to use .atOffset(ZoneOffset.UTC) as shown above to keep the date-time with the same timezone offset (i.e. +00:00 hours or date-time at UTC) which is there in the date-time string.
try this :
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX");
try {
String s = "2015-10-10T14:34:22+02";
s = s.replaceAll("T", " ");
Date d = df.parse(s);
Calendar cl = Calendar.getInstance();
cl.setTime(d);
System.out.println(cl.getTime());
System.out.println("year : " + cl.get(Calendar.YEAR));
} catch (ParseException e) {
System.err.println(e);
}
output
Sat Oct 10 13:34:22 GMT+01:00 2015
year : 2015
Maybe this approach could help:
import java.text.ParseException;
import java.time.*;
import java.util.Date;
public class ConvertDate {
public static void main(String[] args) throws ParseException {
Date date = new Date();
LocalDateTime localDateTime = date.toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime();
System.out.println(localDateTime);
System.out.println(localDateTime.getYear());
}
}
Time zone is crucial
You need to decide in which time zone you want the year. New Year doesn’t happen at one point in time across the globe, but over a span of about 26 hours. So if your date string is within a day or so of New Year, your result could be off by a year if you don’t pick the correct time zone. For example:
ZoneId zone = ZoneId.of("America/Louisville");
// The year in the following object does not matter
ZonedDateTime fixedTimeOfYear = ZonedDateTime.of(2020, 6, 15, 17, 0, 0, 0, zone);
String inputString = "2015-01-01T01:02:03Z";
OffsetDateTime input = OffsetDateTime.parse(inputString);
int year = input.atZoneSameInstant(zone).getYear();
System.out.format("Year in %s is %d%n", zone, year);
ZonedDateTime desiredTime = fixedTimeOfYear.withYear(year);
System.out.println("Result: " + desiredTime);
Output from this snippet is:
Year in America/Louisville is 2014
Result: 2014-06-15T17:00-04:00[America/Louisville]
You notice that even though the year in the string is 2015, it is still only 2014 in the time zone that I chose for the demonstration, so the resulting date and time are in 2014. The example was picked to demonstrate my point.
Don’t use LocalDateTime
The frequent use of LocalDateTime that you mention in a comment is a misunderstanding. For a date and time in a known time zone in 2015, for example, LocalDateTime is the wrong class to use. Use ZonedDateTime or at least OffsetDateTime so we know what we are talking about. These classes have the advantages that they keep track of time zone or offset themselves, and that they define an unambiguous point in time. LocalDateTime does nothing of this.

Apache date utils parseDateStrictly method dosen't work properly

DateUtils.parseDateStrictly("28 Sep 2018" , "dd MMMM yyyy")
The format of the above date should be dd MMM yyyy (MMM denotes shorter month) , but MMMM also parses shorter month which causes invalid parsing. I am already using parseDateStrictly method. Any other suggestions ?
java.time
I recommend that you use java.time for your date and time work. Apache DateUtils was useful once we only had the poorly designed Date and SimpleDateFormat classes to work with. We don’t need it anymore. For a long time now we haven’t needed it.
java.time behaves the way you expect out of the box.
DateTimeFormatter dateFormatter
= DateTimeFormatter.ofPattern("dd MMMM uuuu", Locale.ENGLISH);
String dateString = "28 Sep 2018";
LocalDate.parse(dateString, dateFormatter);
Result:
Exception in thread "main" java.time.format.DateTimeParseException: Text '28 Sep 2018' could not be parsed at index 3
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
at java.base/java.time.LocalDate.parse(LocalDate.java:428)
(etc.)
Link
Oracle tutorial: Date Time explaining how to use java.time.
The column format is dd MMMM yyyy and it should parse dates like 28
September 2018 and should throw error on values such as 28 Sep 2018
DateUtils uses the date-time API of java.util and their formatting API, SimpleDateFormat which are outdated and error-prone. I suggest you should stop using them completely and switch to the modern date-time API.
Using the modern date-time API:
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// Test strings
String[] arr = { "28 September 2018", "28 Sep 2018", "28 09 2018" };
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM uuuu", Locale.ENGLISH);
for (String s : arr) {
try {
LocalDate date = LocalDate.parse(s, formatter);
// ...Process date e.g.
System.out.println(DateTimeFormatter.ofPattern("MMMM dd, uuuu", Locale.ENGLISH).format(date));
} catch (DateTimeException e) {
System.out.println(s + " is not a valid string.");
}
}
}
}
Output:
September 28, 2018
28 Sep 2018 is not a valid string.
28 09 2018 is not a valid string.
Learn more about the modern date-time API at Trail: Date Time.
If you are doing it for your Android project and 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.

Extract date from String java

I have a String with several dates, for example:
[20-Jul-2012 5:11:36,670 UTC PM, 20-Jul-2012 5:11:36,683 UTC PM]
How do I read this string and extract each date? I'm using the SimpleDateFormat class to create a regex.
SimpleDateFormat format2 = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss,SSS z a");
I've tried :
I've just did, to get the first one and it changes the format and the timezone:
ParsePosition parsePos = new ParsePosition(1);
SimpleDateFormat format2 = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss,SSS z a");
System.out.println(format2.parse(entry.getValue().toString(), parsePos)) ;
Output : Fri Jul 20 06:11:36 BST 2012
You can try:
parsePos = new ParsePosition(1);
while((date = format2.parse(yourString, parsePos)!=null){
//use date
}
java.time
The question uses SimpleDateFormat which was the correct thing to do in 2012. In Mar 2014, the java.util date-time API and their formatting API, SimpleDateFormat were supplanted by the modern Date-Time API. Since then, it is strongly recommended to stop using the legacy date-time API.
Solution using the modern date-time API:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;
class Main {
public static void main(String[] args) {
DateTimeFormatter parser = DateTimeFormatter.ofPattern("d-MMM-uuuu h:m:s,SSS VV a", Locale.ENGLISH);
Stream.of(
"20-Jul-2012 5:11:36,670 UTC PM",
"20-Jul-2012 5:11:36,683 UTC PM"
)
.map(s -> ZonedDateTime.parse(s, parser))
.forEach(System.out::println);
}
}
Output:
2012-07-20T17:11:36.670Z[UTC]
2012-07-20T17:11:36.683Z[UTC]
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.

Unparseable date exception in java

These lines of codes
SimpleDateFormat formatter = new SimpleDateFormat("dd/mm/yy");
ContentValues values = new ContentValues();
values.put(COL_EVENT_ID, appointment.mEventId);
try {
values.put(COL_START_DATE, String.valueOf(formatter.parse(appointment.mStartDate.toString())));
values.put(COL_END_DATE, String.valueOf(formatter.parse(appointment.mEndDate.toString())));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
causees this exception
10-15 11:44:38.150: WARN/System.err(3861): java.text.ParseException: Unparseable date: "Mon Jan 10 00:10:00 GMT+02:00 2011"
what is the possible solution ?
Your format is completely wrong. Not only are you using mm (which means minutes) when you probably meant MM, but this:
Mon Jan 10 00:10:00 GMT+02:00 2011
is clearly not in the format
dd/MM/yy
You probably want something like
EEE MMM dd HH:mm:ss z yyyy
EDIT: That works for me in desktop Java:
import java.text.*;
public class Test {
public static void main(String[] args) throws ParseException {
String value = "Mon Jan 10 00:10:00 GMT+02:00 2011";
String pattern = "EEE MMM dd HH:mm:ss z yyyy";
DateFormat format = new SimpleDateFormat(pattern);
System.out.println(format.parse(value));
}
}
You may want to set the culture of the SimpleDateFormat of course.
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Solution using java.time, the modern Date-Time API:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String strDateTime = "Mon Jan 10 00:10:00 GMT+02:00 2011";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM d H:m:s OOOO uuuu", Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, dtf);
System.out.println(zdt);
}
}
Output:
ONLINE DEMO
Notes:
m specifies minute-of-hour whereas M specifies month-of-year. You have wrongly used the former.
The pattern used with the parser (DateTimeFormatter or SimpleDateFormat) should match the input date-time string. Your pattern, dd/mm/yy is off by far.
Never use SimpleDateFormat or DateTimeFormatter without a Locale.
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and 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.

Problem while fetching milliseconds from Date

I have a date in string format as 25 November 2010 and I am trying to fetch the milliseconds of that date, for that I have written the below code:
strDateSelcted = "25 November 2010" // Actually I am receiving date in this format
SimpleDateFormat curFormater = new SimpleDateFormat("dd MMM yyyy");
try {
Date dateObj = curFormater.parse(strDateSelcted);
insertEventtoCalendar(dateObj.getTime()); // Actually insert an event onto the native calendar
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
But, when I try to run the application,it creates an event a day before the date inserted, i.e. For 25 November 2010 it inserts event on 24 November 2010 and For 27 November 2010 it inserts event on 26 November 2010, and same.
Where am I making a mistake?
To set the timezone used by the date formatter: call the setTimeZone method
e.g.
curFormater.setTimeZone(TimeZone.getTimeZone("UTC"));
For one, your date pattern is wrong, use dd MMMM yyyy. MMM parses & returns Nov (not November).
Also, since there is no millisecond provided in your date string, the millisecond will be set to 0.
Seeing that Jon Skeet updated me with the question, it might be timezone issue (as he mentioned). Sorry for the misunderstanding.
Test code:
/**
*
*/
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* #author The Elite Gentleman
*
*/
public class Test {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
String date = "25 November 2010";
SimpleDateFormat sdf = new SimpleDateFormat("dd MMMM yyyy");
Date d = sdf.parse(date);
System.out.println(d.getTime());
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Value: 1290636000000
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Note that a Date-Time without a timezone represents a local Date-Time which may be different for a different timezone e.g. today, it is 31st May 2021 in my timezone, Europe/London whereas 1st June 2021 in Australia/Sydney. So, in order to represent a moment (i.e. an instantaneous point on the timeline in UTC), Java provides a class called Instant which you can convert to other Date-Time types e.g.
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
Instant now = Instant.now();
LocalDate todayUK = now.atZone(ZoneId.of("Europe/London")).toLocalDate();
LocalDate todaySydney = now.atZone(ZoneId.of("Australia/Sydney")).toLocalDate();
System.out.println(todayUK);
System.out.println(todaySydney);
LocalDateTime nowUK = now.atZone(ZoneId.of("Europe/London")).toLocalDateTime();
LocalDateTime nowSydney = now.atZone(ZoneId.of("Australia/Sydney")).toLocalDateTime();
System.out.println(nowUK);
System.out.println(nowSydney);
}
}
Output:
2021-05-31
2021-06-01
2021-05-31T16:22:40.418214
2021-06-01T01:22:40.418214
So, you need to choose a timezone. Most digital operations are based on the UTC (timezone offset, +00:00 hours).
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("d MMMM u", Locale.ENGLISH);
LocalDate date = LocalDate.parse("25 November 2010", dtf);
ZonedDateTime zdt = date.atStartOfDay(ZoneOffset.UTC);
Instant instant = zdt.toInstant();
System.out.println(instant);
long millis = instant.toEpochMilli();
System.out.println("Milliseconds since January 1, 1970, 00:00:00 GMT: " + millis);
}
}
Output:
2010-11-25T00:00:00Z
Milliseconds since January 1, 1970, 00:00:00 GMT: 1290643200000
The Z in the sample output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
Now, pass millis to your insertEventtoCalendar:
insertEventtoCalendar(millis);
Note: For any reason you need an object of java.util.Date, you can get it from this Instant as
Date date = Date.from(instant);
Learn more about java.time, the modern Date-Time API* from Trail: Date Time.
What went wrong with your code?
You tried parsing the date string without setting a timezone and therefore, SimpleDateFormat used your JVM's timezone to parse the date string, giving you a moment/instant corresponding to the start-of-the-day on 25 November 2010 in your timezone. To fix the problem, set the desired Timezone (e.g. UTC) to the SimpleDateFormat instance before parsing i.e.
SimpleDateFormat curFormater = new SimpleDateFormat("dd MMMM yyyy", Locale.ENGLISH);
curFormater.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
Two more important notes:
Use MMMM instead of MMM for the full monthname.
Never use SimpleDateFormat or DateTimeFormatter without a Locale.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and 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.
It's almost certainly a time zone issue. Check which time zone your SimpleDateFormat is using, and the one your phone is using to display the event.
For diagnostic purposes I suggest you log the result of dateObj.getTime() and you can check exactly what that means on a desktop machine.
Use Locale.getDefault() when Your using SimpleDateFormat.I hope it may solve your problem.

Categories

Resources