I have the two different timestamp values as String. I need to find which one is latest. Its in the format of [YYYYMMDDHHMMSS]
Timestamps are :
20150804030251
20150804040544
Is there any easy way to get the latest using Java 8?
You don't even have to parse those strings. Just use compareTo():
"20150804030251".compareTo("20150804040544");
More info
you can create local date object In java 8, like below
LocalDateTime dt = LocalDateTime.parse("20150804030251",
DateTimeFormatter.ofPattern("YYYYMMDDHHMMSS")
LocalDateTime dt2 = LocalDateTime.parse("20150804030251",
DateTimeFormatter.ofPattern("YYYYMMDDHHMMSS")
then compare using dt.isBefore(dt2)
Yes, those timestamps are formated in a way which is easy to compare.
if ( Long.parseLong(timesteamp1) < Long.parseLong(timestamp2) ) {
//timestamp2 is later than timestamp1
}
This is possible because the most significative part, the year, is in the most significative part of an integer, the leftmost; the rest of the parts go in decreasing order of significance from left to right; and a fixed number of digits is used for each part, like month 02 instead of month 2. Otherwise this simple way would not be possible.
You can also compare them lexicografically. The previous code, in the particular case of this format, is equivalent to :
if ( timestamp1.compareTo(timestamp2) < 0 ) {
// timestamp2 is later than timestamp 1
}
Unfortunately, none of the existing answers is proper. Let's first discuss the problem with them:
The accepted answer works as a result of mere coincidence. It happened to work just because the values are arranged in the decreasing order of time units (year, month, day, hour, minute, second). It will fail if their positions are changed e.g. MMyyyy.... Another serious problem with this approach is that it will fail to validate a wrong value e.g. 15 digits instead of 14 digits.
This has similar problems as with the accepted answer. Instead of numeric value, it compares ASCII values and thus may fail for the same reasons as mentioned for the accepted answer.
This is the idiomatic way of doing it, but it is wrong because it uses Y (which specifies week-based-year) instead of y (which specifies year-of-era) and D (which specifies day-of-year) instead of d (which specifies day-of-month). In fact, it is recommended to use u instead of y. Check the documentation page
to learn more about these symbols.
This is blatantly wrong. Date(long date) should initialize the object with the no. of milliseconds since 1970-01-01T00:00:00Z.
By now, you must have already figured out the solution which I have posted below just for the sake of completeness:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
String strDateTime1 = "20150804030251";
String strDateTime2 = "20150804040544";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuuMMddHHmmss");
LocalDateTime ldt1 = LocalDateTime.parse(strDateTime1, dtf);
LocalDateTime ldt2 = LocalDateTime.parse(strDateTime2, dtf);
LocalDateTime latest = ldt1.isAfter(ldt2) ? ldt1 : ldt2;
System.out.println(latest);
}
}
Output:
2015-08-04T04:05:44
Learn more about the 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.
You can still try this. In here not use any special feature in Java 8
String timeStamp1 = "20150804030251";
String timeStamp2 = "20150804040544";
DateFormat dateFormat = new SimpleDateFormat("yyyyMMhhHHmmss");
Date date1 = dateFormat.parse(timeStamp1);
Date date2 = dateFormat.parse(timeStamp2);
if(date1.after(date2)){
System.out.println("latest "+date1);
}else {
System.out.println("latest "+date2);
}
Related
I have a timestamp and offset in string format as shown below in two different variables:
01/14/2016 07:37:36PM
-08:00
I want to convert above timestamp into ISO 8601 compliant String, with milliseconds and timezone so it should look like this after conversion:
2016-01-14T19:37:36-08:00
How can I do that? I am using jodatime library.
The newer java.time classes work so well with ISO 8601 strings.
String dateTimeString = "01/14/2016 07:37:36PM";
String offsetString = "-08:00";
LocalDateTime dateTime = LocalDateTime.parse(dateTimeString,
DateTimeFormatter.ofPattern("MM/dd/uuuu hh:mm:ssa"));
ZoneOffset offset = ZoneOffset.of(offsetString);
String formattedTimestamp = dateTime.atOffset(offset).toString();
System.out.println(formattedTimestamp);
This prints
2016-01-14T19:37:36-08:00
Stay away from outdated classes like SimpleDateFormat.
What is offsetString is not present? I understand that in this case you want an offset of Z for UTC. For example like this:
ZoneOffset offset;
if (offsetString == null) {
offset = ZoneOffset.UTC;
} else {
offset = ZoneOffset.of(offsetString);
}
String formattedTimestamp = dateTime.atOffset(offset).toString();
With a null offsetString we now get
2016-01-14T19:37:36Z
The classes in java.time (of which I’m using but a few) are described in JSR-310 and come built-in with Java 8. What if you would like to use them with Java 6 or 7? You get the ThreeTen Backport (link below). It gives you the majority of the classes for Java 6 and 7. I’m not perfectly happy to tell you you need an external library, but in this case it’s only until you move to Java 8. I hope you will soon.
I am sure it can be done with JodaTime too, but I haven’t got experience with it, so cannot give you the details there. What I do know, I have read the the folks behind JodaTime now recommend you move over to java.time instead. So I am asking you to swap one external library for a newer (and supposedly better) one. In itself I’m not unhappy with that. Only if you already have a codebase that uses JodaTime, it’s not really trivial.
Link: ThreeTen Backport
You can find more examples in section Examples at :- http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String string2 = "2001-07-04T12:08:56.235-07:00";
Date result2 = df2.parse(string2);
I have a dates in format such as:
- dd. - dd.MM.yyyy
Based on this information I want to receive from date and to date.
Naive implementation in pseudo code is:
Split the date into first part and second part
Create a SimpleDateFormat for second part
Take a look, whether the number in the first part is higher than the number in a second part.
If it is:
Decrement month for the first date
Create from date which will contain the dd from the first part, decremented month from the second part and year from the second part.
If it isn't:
Create from date which will contain the dd from the first part and month and year from second part.
This solution would probably work most of the time, but it feels rather awkward. Isn't there any better solution?
I'd suggest you to check the package
org.joda.time
In particular the following classes
DateTime
Period
java.time
The java.time framework is built into Java 8 and later. Much of that functionality is back-ported to Java 6 & 7 and to Android.
I provide some scraps of example code here, but untested – never run. Should get you going in the right direction.
Two tricky parts to this problem:
YearThe Question says to assign the year. That is not the case for a stop date in January with a start date that turns out to be in previous month, December. You want the previous year in such a case. Solution is to let java.time subtract a month and handle the Jan-to-Dec math for you.
Month lengthVarious months have different lengths, different number of days, obviously. Keep mind that you cannot try to put day-of-month 31 on to month of April. If your input data is always clean and valid, and our algorithm below is correct, this should be a non-issue. Nevertheless, I would certainly add some exception-catching code to my example code below to trap any attempt to form an invalid date.
I'll skip the string-splitting part, and assume you have a number (the day-of-month) from the first part, and a string of the date from the second part.
long dayOfMonth = Long.longValue( "31" );
That date is not in standard format, so we must specify a formatting pattern. A LocalDate represents a date-only value without time-of-day and without time zone.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "dd.MM.yyyy" );
LocalDate stop = LocalDate.parse( "21.05.2016" , formatter );
We can extract the day-of-month to compare.
LocalDate start = null;
int d = localDate.getDayOfMonth();
Do the comparison.
if( dayOfMonth >=d ) { // If start is in previous month…
start = stop.minusMonths( 1 ).withDayOfMonth( dayOfMonth );
} else if (dayOfMonth < d ) { // If start is in same month…
start = stop.withDayOfMonth( dayOfMonth );
} else {
// FIXME: handle impossible condition as error. The 'if' statements are flawed.
}
By the way, the format of this input data is awkward and, frankly, silly. This kind of precious “cleverness” creates extra work, gives opportunity for confusion and errors, is completely needless without providing any benefits, and drives me nuts. If you have any control of this input data I strongly suggest either of two possible changes.
First, if exchanging data within your app, do not use strings. Use objects. Above you have seen the LocalDate object. You could pass those around. Or even define your own class LocalDateRange to house a pair of LocalDate objects. Or see this Question and especially this Answer that talks about using the Google Guava class Range to hold the pair of LocalDate objects.
Secondly, when you must serialize date-time values to strings, use the standard ISO 8601 formats. Such use is simple as the java.time classes by default use these formats when parsing/generating strings. A date-only value should be in YYYY-MM-DD order. A date range interval is a pair of those strings mated with a slash (SOLIDUS) or alternatively a pair of hyphens when a slash is inappropriate (such as file or folder naming within a Unix-related file system).
My Objective is
to create a java class that can handle the below two requirements
(A) 1. Verify if the format of a timestamp matches with expected format.
CCYY-MM-DD'T'hh:mm:ss'.0000000000+'uh:um"
Ex: the expected format is not static.
It may be either of these
"2013-09-10T18:30:20.123456+10:00" or
"2013-09-10T18:30:20.123+10:00".
I am not bothered about the
precision and value. Only the format matters.
(B) 2. Verify if the timestamp is in a certain range.
Ex: Verify if the timestamp is in
between "2013-09-10 18:27" and "2013-09-10 18:33". (verification is only upto minute level precision) (may be a delta of + or - 2min)
As suggested by one of the member, I have edited the post to target at
One specific question.
The QUESTION :
How to validate the custom timestamp upto microsec precision using JAVA class ?
The two arguments for this class will be
1) Expected FORMAT as a String
2) timestamp value as a String
Based on analysis from various search results, below is my understanding :
Java (by default) does not parse/format Timestamp at microsecond level( I used SimpleDateFormat)
If 6 digits are given in milliseconds place, it will re-calculate the value into seconds and the dateformat will be updated and the new dateformat will have 3 digits in milliseconds precision.
I have also seen a thread which suggests to use java.sql.Timestamp.
Tried this approach but not working.
I was not able to convert my strTimestamp 2013-09-10T18:30:20.123456+10:00 into Timestamp object.
Timestamp ts = Timestamp.valueOf(strTimestamp);
java.lang.IllegalArgumentException:
Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
I was not able convert my input format into Timestamp object.
I have a workaround to validate using regular expression :
2013-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9]).[0-9][0-9][0-9][0-9][0-9][0-9]\+10:00
The problem with this reg ex is that, my expected timestamp format is not static. So i have to use a regex for every pattern.
So I am trying to figure out if there is any robust solution in java, which can be self sufficient even if the expected format changes.
java.time in Java 8
JSR 310 defined a new java.time package in Java 8. Its date-time class resolves to nanoseconds. That gives you 9 digits after the decimal point.
The java.time package is inspired by Joda-Time but entirely re-architected. Concepts are similar.
Like Joda-Time, the java.time package uses ISO 8601 formats as its defaults for parsing and formatting. So you can input or output strings such as 2013-09-10T18:30:20.123456789+10:00.
An early release of Java 8 is available now. Official release should be this month.
A project to backport this package to earlier versions of Java was underway. I do not know of its current status or success. The backport project is independent of Oracle and the OpenJDK project.
Milliseconds
The old bundled classes, java.util.Date & .Calendar, use a precision of milliseconds.
Ditto for the excellent Joda-Time library, milliseconds precision.
So not enough digits in the fractional seconds to meet your needs.
A java.sql.Timestamp is not going to help you, because that is a java.util.Date.
The code is fairly simple, if you use the right format String with SimpleDateFormat, which you let do the heavy lifting. Here's an entire working solution:
public static boolean isNear(String timestamp, int microPlaces, Date near, int minutes) {
if (!timestamp.matches(".*\\.\\d{" + microPlaces + "}\\D.*") {
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ");
try {
Date date = sdf.parse(timestamp.replaceAll(":(?=\\d\\d$)", ""));
return Math.abs(date.getTime() - near.getTime()) <= minutes * 60000;
} catch (ParseException ignore) {
return false; // string was not of correct format
}
}
This may not be exactly what you had in mind - if not, you should be able to use it as a basis for what you want. The key points are:
The S format string means "microseconds", and it doesn't require all the digits - so your timestamp can have any number
Java 6 needs the colon removed from the timezone. Java 7 doesn't need this - use the X format string instead of Z
A failure to parse a date from the input throws a ParseException - do what you want with this event
I chose to make the API give central date for the range and a +/- minute value. You may need to pass two dates - up to you. Use Date.before() and Date.after() to compare if you do that.
Here's some test code testing your examples and a couple of edge cases:
public static void main(String[] args) throws Exception {
Date near = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm").parse("2013-09-10T18:32");
System.out.println(isNear("2013-09-10T18:30:20.123456+10:00", near, 2));
System.out.println(isNear("2013-09-10T18:30:20.123+10:00", near, 2));
System.out.println(isNear("2013-09-10T18:10:20.123+10:00", near, 1));
System.out.println(isNear("XXXX-09-10T18:10:20.123+10:00", near, 1));
}
Output:
true
true
false
false
Really I`m also trying to find answer to this problem. As I have no ability to add comment to the Bohemian answer. I want to mention that 'S' pattern in SimpleDateFormat is used not for microseconds but for milliseconds. It means that for pattern "yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ" provided microsecond digits in string would be parsed as milliseconds.
So the first three digits would be passed as XXX seconds and their value value would be added to date. So we can receive mistake about 16 minutes.
I have date in string format and I want to parse that into util date.
var date ="03/11/2013"
I am parsing this as :
new SimpleDateFormat("MM/dd/yyyy").parse(date)
But the strange thing is that, if I am passing "03-08-201309 hjhkjhk" or "03-88-2013" or 43-88-201378", it does not throw error , it parses it.
For this now, I have to write regex pattern for checking whetehr input of date is correct or not.
but why is it so ??
Code :
scala> val date="03/88/201309 hjhkjhk"
date: java.lang.String = 03/88/201309 hjhkjhk
scala> new SimpleDateFormat("MM/dd/yyyy").parse(date)
res5: java.util.Date = Mon May 27 00:00:00 IST 201309
You should use DateFormat.setLenient(false):
SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
df.setLenient(false);
df.parse("03/88/2013"); // Throws an exception
I'm not sure that will catch everything you want - I seem to remember that even with setLenient(false) it's more lenient than you might expect - but it should catch invalid month numbers for example.
I don't think it will catch trailing text, e.g. "03/01/2013 sjsjsj". You could potentially use the overload of parse which accepts a ParsePosition, then check the current parse index after parsing has completed:
ParsePosition position = new ParsePosition(0);
Date date = dateFormat.parse(text, position);
if (position.getIndex() != text.length()) {
// Throw an exception or whatever else you want to do
}
You should also look at the Joda Time API which may well allow for a stricter interpretation - and is a generally cleaner date/time API anyway.
Jon Skeet’s answer is correct and was a good answer when it was written in 2013.
However, the classes you use in your question, SimpleDateFormat and Date, are now long outdated, so if someone got a similar issue with them today, IMHO the best answer would be to change to using the modern Java date & time API.
I am sorry I cannot write Scala code, so you will have to live with Java. I am using
private static DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy");
The format pattern letters are the same as in your question, though the meaning is slightly different. DateTimeFormatter takes the number of pattern letters literally, as we shall see. Now we try:
System.out.println(LocalDate.parse(date, parseFormatter));
Results:
"03/11/2013" is parsed into 2013-03-11 as expected. I used the modern LocalDate class, a class that represents a date without time-of-day, exactly what we need here.
Passing "03/88/2013 hjhkjhk" gives a DateTimeParseException with the message Text '03/88/2013 hjhkjhk' could not be parsed, unparsed text found at index 10. Pretty precise, isn’t it? The modern API has methods to parse only part of a string if that is what we want, though.
"03/88/201309" gives Text '03/88/201309' could not be parsed at index 6. We asked for a 4 digit year and gave it 6 digits, which leads to the objection. Apparently it detects and reports this error before trying to interpret 88 as a day of month.
It does object to a day of month of 88 too, though: "03/88/2013" gives Text '03/88/2013' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 88. Again, please enjoy how informative the message is.
"03-08-2013" (with hyphens instead of slashes) gives Text '03-08-2013' could not be parsed at index 2, not very surprising. Index 2 is where the first hyphen is.
Jon Skeet explained that the outdated SimpleDateFormat can be lenient or non-lenient. This is true for DateTimeFormatter too, in fact it has 3 instead of 2 resolver styles, called ‘lenient’, ‘smart’ and ‘strict’. Since many programmers are not aware of this, though, I think they made a good choice of not making ‘lenient’ the default (‘smart’ is).
What if we wanted to make our formatter lenient?
private static DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy")
.withResolverStyle(ResolverStyle.LENIENT);
Now it also parses "03/88/2013", into 2013-05-27. I believe this is what the old class would also have done: counting 88 days from the beginning of March gives May 27. The other error messages are still the same. In other words it still objects to unparsed text, to a 6 digit year and to hyphens.
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (that’s ThreeTen for JSR-310, where the modern API was first defined).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and I think that there’s a wonderful explanation in this question: How to use ThreeTenABP in Android Project.
In my web app, date & time of a user's certain activity is stored(in database) as a timestamp Long which on being displayed back to user needs to be converted into normal date/time format.
(Actually my database Cassandra stores the timestamp of when a column was written to it, as a long value( microseconds since 1970 ) which I will use to find out the time of that corresponding user activity)
I am using JSF 2.0(+ primefaces) which I believe has converters that may be helpful for this conversion? Or otherwise how How can I, at best, achieve these conversions?
Let me propose this solution for you. So in your managed bean, do this
public String convertTime(long time){
Date date = new Date(time);
Format format = new SimpleDateFormat("yyyy MM dd HH:mm:ss");
return format.format(date);
}
so in your JSF page, you can do this (assuming foo is the object that contain your time)
<h:dataTable value="#{myBean.convertTime(myBean.foo.time)}" />
If you have multiple pages that want to utilize this method, you can put this in an abstract class and have your managed bean extend this abstract class.
EDIT: Return time with TimeZone
unfortunately, I think SimpleDateFormat will always format the time in local time, so we can't use SimpleDateFormat anymore. So to display time in different TimeZone, we can do this
public String convertTimeWithTimeZome(long time){
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(time);
return (cal.get(Calendar.YEAR) + " " + (cal.get(Calendar.MONTH) + 1) + " "
+ cal.get(Calendar.DAY_OF_MONTH) + " " + cal.get(Calendar.HOUR_OF_DAY) + ":"
+ cal.get(Calendar.MINUTE));
}
A better solution is to utilize JodaTime. In my opinion, this API is much better than Calendar (lighter weight, faster and provide more functionality). Plus Calendar.Month of January is 0, that force developer to add 1 to the result, and you have to format the time yourself. Using JodaTime, you can fix all of that. Correct me if I am wrong, but I think JodaTime is incorporated in JDK7
java.time
ZoneId usersTimeZone = ZoneId.of("Asia/Tashkent");
Locale usersLocale = Locale.forLanguageTag("ga-IE");
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(usersLocale);
long microsSince1970 = 1_512_345_678_901_234L;
long secondsSince1970 = TimeUnit.MICROSECONDS.toSeconds(microsSince1970);
long remainingMicros = microsSince1970 - TimeUnit.SECONDS.toMicros(secondsSince1970);
ZonedDateTime dateTime = Instant.ofEpochSecond(secondsSince1970,
TimeUnit.MICROSECONDS.toNanos(remainingMicros))
.atZone(usersTimeZone);
String dateTimeInUsersFormat = dateTime.format(formatter);
System.out.println(dateTimeInUsersFormat);
The above snippet prints:
4 Noll 2017 05:01:18
“Noll” is Gaelic for December, so this should make your user happy. Except there may be very few Gaelic speaking people living in Tashkent, so please specify the user’s correct time zone and locale yourself.
I am taking seriously that you got microseconds from your database. If second precision is fine, you can do without remainingMicros and just use the one-arg Instant.ofEpochSecond(), which will make the code a couple of lines shorter. Since Instant and ZonedDateTime do support nanosecond precision, I found it most correct to keep the full precision of your timestamp. If your timestamp was in milliseconds rather than microseconds (which they often are), you may just use Instant.ofEpochMilli().
The answers using Date, Calendar and/or SimpleDateFormat were fine when this question was asked 7 years ago. Today those classes are all long outdated, and we have so much better in java.time, the modern Java date and time API.
For most uses I recommend you use the built-in localized formats as I do in the code. You may experiment with passing SHORT, LONG or FULL for format style. Yo may even specify format style for the date and for the time of day separately using an overloaded ofLocalizedDateTime method. If a specific format is required (this was asked in a duplicate question), you can have that:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss, dd/MM/uuuu");
Using this formatter instead we get
05:01:18, 04/12/2017
Link: Oracle tutorial: Date Time explaining how to use java.time.
Not sure if JSF provides a built-in functionality, but you could use java.sql.Date's constructor to convert to a date object: http://download.oracle.com/javase/1.5.0/docs/api/java/sql/Date.html#Date(long)
Then you should be able to use higher level features provided by Java SE, Java EE to display and format the extracted date. You could instantiate a java.util.Calendar and explicitly set the time: http://download.oracle.com/javase/1.5.0/docs/api/java/util/Calendar.html#setTime(java.util.Date)
EDIT: The JSF components should not take care of the conversion. Your data access layer (persistence layer) should take care of this. In other words, your JSF components should not handle the long typed attributes but only a Date or Calendar typed attributes.
To show leading zeros infront of hours, minutes and seconds use below modified code. The trick here is we are converting (or more accurately formatting) integer into string so that it shows leading zero whenever applicable :
public String convertTimeWithTimeZome(long time) {
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(time);
String curTime = String.format("%02d:%02d:%02d", cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND));
return curTime;
}
Result would be like : 00:01:30
I tried this and worked for me.
Date = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1, 0, 0, 0))).TotalSeconds