I tried to get XMLGregorianCalendar with date format ddMMyyy. When I tried to convert it from Date, it kept throwing me:
java.lang.IllegalArgumentException: Leading zeros are not allowed.
Is there anyway to fix it?
Thanks!
DateFormat format = new SimpleDateFormat("ddMMyyyy");
XMLGregorianCalendar gTest = DatatypeFactory.newInstance().newXMLGregorianCalendar(format.format(new Date(1, 9, 2018)));
First, you shouldn’t use the Date class if you can avoid it. You may also want to avoid XMLGregorianCalendar. Date and SimpleDateFormat are long outdated, and the latter in particular renowned for being troublesome. Today we have so much better in java.time, the modern Java date and time API. It may also sometimes replace XMLGregorianCalendar.
But taking your word for it, you’ve got an old-fashioned Date object — maybe from a legacy API that you cannot change or don’t want to change just now. You need to convert it to an XMLGregorianCalendar. I still prefer to use java.time for the conversion.
Date oldfashionedDate = // some Date equal to 1 August 2018 at 00:00 in my time zone;
LocalDate modernDate = oldfashionedDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
XMLGregorianCalendar gTest = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(modernDate.toString());
System.out.println(gTest);
This prints:
2018-08-01
You asked for format ddMMyyyy. You cannot have that. Or more precisely, the only way you can have that is in a string, not in an XMLGregorianCalendar. XMLGregorianCalendar.toString() produces XML format, and this is defined in the XML Schema (see the link at the bottom). It goes like for example 2018-08-01, 2018-08-01T00:00:00 or 2018-08-01T00:00:00+02:00.
What went wrong in your code?
I could not reproduce your IllegalArgumentException. On my Java 10 the leading 0 of 01082018 was accepted (on Java 8 too). However, the data type factory parsed the string into a year of 1082018 (more than a million years from now), and since the string ended there, it didn’t parse any month or day of month. So the resulting XMLGregorianCalendar had only a year in it, no other fields were defined.
newXMLGregorianCalendar(String) accepts only XML format. Apparently a year alone is accepted. The format still follows the specification linked to at the bottom of this answer.
Accepting the leading 0 is probably a bug, though, since the leading zero is not printed back from the toXMLFormat method, which was supposed to give the same string back as was parsed, according to the documentation of newXMLGregorianCalendar(String).
Links
Oracle tutorial: Date Time explaining how to use java.time.
XML Schema Part 2: Datatypes Second Edition: Lexical representation
Documentation of DatatypeFactory.newXMLGregorianCalendar(String)
Related
I should preface this with I use Apache Spark, which uses java.sql.Date, in case anyone suggests I should use dates from java.time. The example below is in Scala.
The API that I use (which is deprecated) to get the month for a date is as follows:
val date: java.sql.Date = ???
val month = date.getMonth()
However if I look at how it appears I should do this based on the deprecation, the above code would be re-written as follows:
val date: java.sql.Date = ???
val cal = Calendar.getInstance()
cal.setTime(date)
cal.get(Calendar.MONTH)
The simplicity and readability of the code is significantly different, and the date being a side effect on the calendar is not terribly nice from a functional programming point of view. Can someone explain why they think this change was made?
Prior to JDK 1.1, the class Date had two additional functions. It
allowed the interpretation of dates as year, month, day, hour, minute,
and second values. It also allowed the formatting and parsing of date
strings. Unfortunately, the API for these functions was not amenable
to internationalization. As of JDK 1.1, the Calendar class should be
used to convert between dates and time fields and the DateFormat class
should be used to format and parse date strings. The corresponding
methods in Date are deprecated.
The JavaDoc explains. Internationalization.
"in case anyone suggests I should use dates from java.time"
There is nothing to stop you from converting to java.time classes as soon as possible, performing whatever calculations/modifications you need and, if you need to re-insert, converting back to java.sql.Date again.
val date: java.sql.Date = ???
val month = date.toLocalDate().getMonthValue()
You said it yourself, and I still think: You should use java.time, the modern Java date and time API. When you get an old-fashioned java.sql.Date from a legacy API not yet upgraded to java.time, convert it to a modern LocalDate and enjoy the natural code writing with java.time.
Why were getMonth() and the other getXxx methods deprecated?
While Michael has already answered the question with respect to java.util.Date, I have something to add when it comes to java.sql.Date. For this class the situation is quite a bit worse than what Michael reported.
What is left undeprecated (apprecated?) of java.util.Date after the deprecations is that a Date is a point in time. java.sql.Date on the other hand was never meant to be a point in time. One way to illustrate this fact is that its toInstant method — which should convert it to an Instant, a point in time — unconditionally throws an UnsupportedOperationException. A java.sql.Date was meant to be a calendar date to be used with an SQL database and its date datatype, which in most cases is also a date, defined by year, month and day of month. Since a Date is no longer year, month and day of month, they have virtually deprecated everything that a java.sql.Date was supposed to be. And they didn’t give us a replacement until with JDBC 4.2 we can exchange LocalDate objects with SQL databases.
The observations that lead to deprecation have got very practical consequences. Let’s try this (in Java because it is what I can write):
void foo(java.sql.Date sqlDate) {
System.out.println(sqlDate);
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Pacific/Samoa")));
System.out.println(sqlDate.getMonth());
}
In one call the method printed:
2020-11-02
9
So we had the 2nd day of the 11th month, and month prints as 9? There are two things going on:
Confusingly the month number that getMonth() returns is 0-based, so 9 means October.
The Date is internally represented as a count of milliseconds since the epoch to the start of the day in the default time zone of the JVM. 2020-11-02 00:00:00 in my original time zone (set to Pacific/Kiritimati for this demonstration) is the same point in time as 2020-10-31 23:00:00 in Samoa. Therefore we get October.
You don’t have to change the time zone yourself for this to happen. Situations where it can happen include:
The default time zone of the JVM can be changed from any part of your program and from any other program running in the same JVM.
The date may be serialized in a program running in one JVM and deserialized in a different JVM with a different time zone setting.
BTW the first snippet I presented at the top often won’t help against unexpected results in these situations. If things go off track before you convert from java.sql.Date to LocalDate, the conversion too will give you the wrong date. If you can make it, convert to LocalDate before anyone messes with the JVM time zone setting and be on the safe side.
I'm developing an android application and I use GSON to get the data from the server, I'm working on both APIs of Facebook and Imgur and the same issue happens. How can I convert a date from milliseconds format to a human-readable format like for example 1584780523 and I want to convert it to any format, for example, 25, Mar 2020.
What I tried to do!
• Get Data
#SerializedName("datetime")
#Expose
private long datetime;
// setters and getters
• In my adapter after getting DATETIME I parse it to a human-readable format
// Get Datetime.
long getDate = data.getDatetime();
// Parse it in this format for example.
DateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss:SSS Z");
Date result = new Date(getDate);
// Set Result (Human-Readable date)
date.setText(dateFormat.format(result));
• But here is the problem! It gives me date like this
19 Jan 1970 09:54:00:533 +0200
Why the output at the 1970s. I saw something like that is the default output of Datetime? But in the same case, it gives me on the other items on my RecyclerView the same output but the last three digits changes!
Here what I'm asking!
1- Why we use (a lot of people use long instead of int?
2- Why the output gives me that and how can I fix this?
3- In other APIs like Youtube they use the regular DateTime why Facebook and Imgur changes them?
Note: I searched about my question for 3 days but I didn't get any answers or relative questions on StackOverflow so, I asked here. All of them or most are for PHP and JavaScript I need an example in Java Android Studio
Thanks.
Your 1584780523 value is in seconds, not milliseconds.
long secondsSinceEpoch = 1584780523;
long millisSinceEpoch = secondsSinceEpoch * 1000L;
Date date = new Date(millisSinceEpoch);
System.out.println(date);
SimpleDateFormat fmt = new SimpleDateFormat("d, MMM yyyy", Locale.US);
System.out.println(fmt.format(date));
Output1
Sat Mar 21 04:48:43 EDT 2020
21, Mar 2020
1) I'm in America/New_York time zone
1- Why we use (a lot of people use long instead of int?
This is to avoid the year 2038 problem. While for example 1584780523 does fit into an int (a 32 bits signed integer), only dates and times up to January 19 2038 03:14:07 UTC can be represented. When writing a program today, we cannot be very sure that no one will ever use our code for dates and times after that point. So we use long instead. After the year 2000 problem (using two digit years leading to problems for dates in year 2000 and later) I guess the IT world has made a sort of a commitment not again to use date and time representations that have an end date within our lifetime.
2- Why the output gives me that and how can I fix this?
Andreas has already explained this part: Because you were treating your seconds as milliseconds. It’s a common mistake.
BTW I recommend using a proven library for the conversion. The comments have already mentioned:
Instant.ofEpochSecond(timestamp)
While multiplying by 1000 works (on a long, not on an int because of overflow), doing your date and time conversions by hand is a bad habit to get into because they very often get more complicated than you think, and the risk of errors is great. Also using a library method with a nice name much better conveys why you are multiplying by 1000.
3- In other APIs like Youtube they use the regular DateTime why
Facebook and Imgur changes them?
I guess it’s because they didn’t know better back when they designed the API. There’s an international standard for transmitting dates and times, ISO 8601, and using it efficiently prevents mistakes like yours, so this is what they should have used. Even if they have understood that later, now a lot of programs rely on the old way, so changing it now would also be risky.
Links
Wikipedia article: Year 2038 problem
A few related Stack Overflow questions out of many:
Java: Date from unix timestamp
Converting Long to Date in Java returns 1970
android timestamp parsing gone wrong(always in 1970)
Wikipedia article: ISO 8601
Java application gets a stream of JSON messages,
within those, the String fields, that represent some date are formatted like \"2019-01-01+01:00\".
It looks like a normal date, followed by the timezone-offset, this guess of mine has been confirmed on the producer side.
Problem:
Even though I still do not really get the logic behind it, I have to parse it in my Java App.
The simplest approach would be to split that string on the '+' character and just parse the date part with some pattern in LocalDate.
But out of curiosity, maybe there is some Date format in Java I do not know of, which could allow for this strange string format?
Yes there is. DateTimeFormatter.ISO_OFFSET_DATE. I agree that it’s weird, and I don’t know what use you should have of the offset. But you can parse into a LocalDate using the formatter:
String stringFromJson = "2019-01-01+01:00";
LocalDate date = LocalDate.parse(stringFromJson, DateTimeFormatter.ISO_OFFSET_DATE);
System.out.println(date);
Output:
2019-01-01
Splitting at the + (plus sign) will not always work. A date with an offset may also come with a negative offset, for example -04:00, and with offset Z (for zero, that is, UTC). Of course you could write code to handle each of those situations manually, but it’s better to use the built-in formatter.
Don’t use Date and SimpleDateFormat. Those classes are poorly designed and long outdated, the latter in particular notoriously troublesome. Use LocalDate and DateTimeFormatter, both from java.time, the modern Java date and time API.
So I have an object ('Task') that has an attribute 'Start Date' which is basically a Timestamp object. So this date is in this format 'YYYY/MM/dd hh:mm:ss:ms'. But for a test case I am authoring, I need this date to be in this format 'YYYY/MM/dd hh:mm'. Also it needs to be a timestamp object as I have to set this value back to the 'Task' object.
I have tried several approaches including the snippet shown below:
SimpleDateFormat formatter = new SimpleDateFormat("YYYY-MM-dd hh:mm");
if (task.getStartDate() != null) {
String newDate = formatter.format(task.getStartDate());
Date date = formatter.parse(newDate);
task.setStartDate(new Timestamp(date.getTime()));
}
I expected the value of the timestamp to be in the format '2018-12-30 09:54' but it resulted in '2018-12-30 09:54:00.0'. So the questions that I have in mind is:
Is there a way to not consider the seconds and millis in the Timestamp object?
If no, then, is the snippet provided an efficient way to update the Timestamp object?
TL;DR
Avoid the Timestamp class if you can. It’s poorly designed and long outdated.
To answer your questions, no, a Timestamp hasn’t got, as in cannot have a format (the same holds true for its modern replacement, Instant (or LocalDateTime)).
Under all circumstances avoid SimpleDateFormat and Date. The former in particular is notoriously troublesome, and both are long outdated too.
Don’t put a format into your model class
You should not want an Instant nor a Timestamp with a specific format. Good practice in all but the simplest throw-away programs is to keep your user interface apart from your model and your business logic. The value of the Instant object belongs in your model, so keep your Instant or Timestamp there and never let the user see it directly. I hope that it’s clear to you that 2018-12-30 09:54 and 2018-12-30 09:54:00.0 represent the same value, the same Timestamp. Just like 17, 0017 and 0x11 represent the same integer value. When you adhere to what I said, it will never matter which format the Instant has got.
Whenever the user should see the date and time, this happens in the UI, not in the model. Format it into a String and show the string to the user. Similarly if you need a specific format for persistence or exchange with another system, format the Instant into a string for that purpose.
java.time and JDBC 4.2
Also for exchange with your database over JDBC, provided that you’ve got a JDBC 4.2 compliant driver, prefer to use a type from java.time over Timestamp. If the datatype on the database side is timestamp with time zone, very clearly recommended for a timestamp, pass an OffsetDateTime like
OffsetDateTime dateTime = yourInstant.atOffset(ZoneOffset.UTC);
yourPreparedStatement.setObject(4, dateTime);
Use setObject, not setTimestamp. Some drivers accept the Instant directly, without conversion to OffsetDateTime. If on the database side you need a mere timestamp (without time zone), use LocalDateTime in Java instead and pass one to setObject in the same way as above.
PS There are errors in your format pattern string
In a format pattern string, uppercase YYYY is for week based year and only useful with a week number. For year use either uuuu or lowercase yyyy. Similarly lowercase hh is for hour within AM or PM from 01 through 12 and only useful with an AM or PM marker. For hour of day from 00 through 23 you need uppercase HH. These errors will give you incorrect dates and times in most cases. Using the wrong case of format pattern letters is a very common mistake. SimpleDateFormat generally doesn’t mind, it just gives incorrect results. The modern DateTimeFormatter does a somewhat better job of notifying you of such errors.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related questions
Formatting timestamp in Java about getting rid of the .0 decimal on the second of a Timestamp.
timestamp formatting in scala [duplicate] about getting a Timestamp with only date and hour (no minute, second or fraction of second).
java parsing string to date about uppercase Y for year in a format pattern string.
Comparing two times in android about lowercase h for hour of day in a format pattern string.
This question already has answers here:
How to compare dates in Java? [duplicate]
(11 answers)
Closed 6 years ago.
So I am using dateString1.compareTo(dateString2) which does a lexicographic comparison with strings, based on the Unicode value of each character, and returns an int. Here is a code sample.
String dateString1 = "05-12-2012";
String dateString2 = "05-13-2012";
if (dateString1.compareTo(dateString2) <=0){
System.out.println("dateString1 is an earlier date than dateString2");
}
Is this a wrong approach to compare dates in Java?
In my tests, I have not run into a situation where I have gotten unexpected result. I really do not want to create a Date object out of the string, if I don't have to, because I am doing this inside a long running loop.
Ninja Edit
Gleaning from the answers below there is nothing wrong with comparing dates as a string if it is in yyyyMMdd format but if it is in any other format it will obviously result in error.
I actually have my date string as yyyyMMdd format in my actual code. (I typed the format wrong in the example I gave above.) So for now, I will just leave the code as it is, and add few lines of comments to justify my decision.
But I now see that comparing strings like this is very limiting and I will run into bugs if dba decides to change the date format down the road, which I don't see happening.
Use strings to handle dates in Java is not always the best option. For example, when it is a leap year, February has an extra day. Because strings can be seemingly correct, it is more appropriate to perform a conversion. Java validates that the date is correct.
You can convert strings to dates using the SimpleDateFormat class.
public static void main(String[] args) throws ParseException {
String dateString1 = "05-12-2012";
String dateString2 = "05-13-2012";
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
Date date1 = format.parse(dateString1);
Date date2 = format.parse(dateString2);
if (date1.compareTo(date2) <= 0) {
System.out.println("dateString1 is an earlier date than dateString2");
}
}
To find out which parameters are allowed to check Customizing Formats (The Java™ Tutorials > Internationalization > Formatting)
I suggest you do the Right Thing (as described here) and convert to proper Date objects to compare. Worry about the performance impact if and when it actually impacts your application (which it probably won't).
It is pretty bad as now you cannot handle a year change.
If you want to do it like that you might wanna format the date as YYYY-MM-DD so the new year doesn't ruin it.
It is bad to use the rules for alphabetization to handle date ordering, mostly because you run into issues where things are ordered differently according to the alphabet and the number system
For the alphabet
01-02-2011 comes before
01-1-2011 (because 0 in the date field is before 1 in the other date field)
For the number system
01, 02, 2011 comes after
01, 1, 2011 because all fields are being compared like numbers
Date objects extend numeric comparison to know which fields take precedence in the comparison, so you don't get a earlier month putting a date "before" another that actually occurs at a latter month but an earlier year.
If you have strict control over the date format, you can align the dates such that they also follow alphabetical rules; however, doing so runs a risk of having your entire program fail in odd ways if you accidentally inject a misformatted date.
The typical way to do this is (not recommended, please use non-String Date comparisons)
YYYYMMDD
(year)(month)(day) all zero-padded.
The last technique is included mainly as you will eventually see it in the wild, and should recognize it for what it is: an attempt to handle dates without a proper date library (aka a clever hack).
As discussed, generally better to work with date-time objects rather than strings.
java.time
The other Answers use old outmoded date-time classes that have proven to be poorly designed, confusing, and troublesome. They lack a class to truly represent a date-only value without time-of-day and without time zone.
Instead use the java.time framework built into Java 8 and later. See Oracle Tutorial. Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
String input = "05-12-2012";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "MM-dd-yyyy" );
LocalDate ld = LocalDate.parse( input , formatter );
The LocalDate implements compareTo. Also, you can call methods equals, isBefore, isAfter.
Boolean isEarlier = ld.isBefore( someOtherLocalDate );
if you are doing only one read of each date then YYYYMMDD (not MMDDYYYY as you did it) might be the most optimal solution. however when you intend to process each date more than once (e.g. you are sorting them) then for sure it's better to change them to an object that can be compared quicker than string (e.g. date)