I tried the code below.
Timestamp timestampDate = scheduled.getInterviewDateAndTime(); //from DB
Map<String, Object> map = new HashMap();
map.put("eventTitle", "interview with");
map.put("startDateTime", timestampDate);
System.out.println("startDateTime : " + timestampDate);
long addTime = 1*60*60*1000;
timestampDate.setTime(timestampDate.getTime() + TimeUnit.HOURS.toMillis(addTime));
map.put("endDateTime", timestampDate);
System.out.println("endDateTime : " + timestampDate);
Is this the correct way or are there any good alternative approaches?
output is:
startDateTime : 2017-11-07 09:08:00.0
endDateTime : 2428-07-15 09:08:00.0
how to get correct output?
There are several problems here:
java.sql.Timestamp (I assume that's what it is) is a mutable class, so setting a time on it changes the timestamp state. Even if it is not apparent with your println statements, debugging the map afterwards will reveal it in a heartbeat.
You logic for computing hours is wrong (you're multiplying two times there)
First time when making addTime variable.
Second time when using TimeUnit.toMillis()
There are (of course) several ways of fixing this:
The way I like more (it, however, requires Java 8 or ThreeTen library):
Timestamp start = obtain();
Timestamp end = Timestamp.from(start.toInstant().plus(1, ChronoUnit.HOURS));
It utilizes ability to convert a Timestamp to a java.time.Instant object (or equivalent version from ThreeTen), and then a factory constructor that will take an Instant and make a Timestamp out of it (this is Java 8 version, ThreeTen will have similar factory in other class not on Timestamp). It also utilizes a much cleaner time computation logic added in java.time compared to old datetime/calendar classes from JDK.
The second variant, which is about the same, doesn't use all the fancy stuff, but as a result is also much less readable (to me):
Timestamp start = obtain();
Timestamp end = new Timestamp(start.getTime() + TimeUnit.HOURS.toMillis(1));
As you can see, second variant is almost what you have, but without unnecessary computation.
And please don't compute these values manually; we all know that hour is supposed to be 60 minutes long and a minute is 60 second, etc., but reading this all over the place blurs eyes very quickly. You yourself has seen it by computing it manually first and then still using the TimeUnit one. And it's even worse when you need to add a day, because millis will not let you to add exactly one day without pulling a lot of information about day length at some specific point in time, considering daylight savings times and historical timezone changes. Not every minute is 60 seconds as well, there are compensative measures for those too, you know.
My recommendation for you is that you stop using the long outdated Timestamp class. Either completely if you can, or at least you minimize your use of it. I will show you code for both options. The modern Java date and time API known as java.time or JSR-310 is so much nicer to work with. And even more so when it comes to time arithmetic like adding an hour to a date-time.
java.time
Change getInterviewDateAndTime() to return an Instant. Instant is the class from java.time that naturally replaces the old Timestamp class. Also change the receiver of your Map to accept a map with Instant objects in it. Modern versions of JDBC, JPA, etc., happily retrieve Instant objects from your database and store Instants back into it.
Instant instantStart = scheduled.getInterviewDateAndTime(); //from DB
Map<String, Object> map = new HashMap<>();
map.put("eventTitle", "interview with");
map.put("startDateTime", instantStart);
System.out.println("startDateTime : " + instantStart);
Instant instantEnd = instantStart.plus(1, ChronoUnit.HOURS);
map.put("endDateTime", instantEnd);
System.out.println("endDateTime : " + instantEnd);
Things to note: The code much more naturally and straightforward expresses the fact that one hour is added. No need for multiplications, no need for the reader to check that you multiplied the right constants, or that your multiplication didn’t overflow; it’s all taken care of. The Instant class is immutable, so there’s no risk of accidentally changing the Instant object that you have alrady added to the map.
In my example the above code printed:
startDateTime : 2017-11-29T09:15:00Z
endDateTime : 2017-11-29T10:15:00Z
Times are in UTC.
EDIT: As Basil Bourque helpfully pointed out in a comment, adding an hour can also be done this way:
Instant instantEnd = instantStart.plus(Duration.ofHours(1));
The result is the same.
Use with legacy APIs
Assume you cannot change the return type of getInterviewDateAndTime() and/or the receiver of your map absolutely needs Timestamp objects in it. The standard way to go about such a restriction is you still use the modern API in your own code. As soon as you receive a Timestamp, you convert it to Instant. And when you need to pass a Timestamp to your legacy code, you convert your Instant only in the last moment.
Timestamp timestampStart = scheduled.getInterviewDateAndTime(); //from DB
Instant instantStart = timestampStart.toInstant();
Map<String, Object> map = new HashMap<>();
map.put("eventTitle", "interview with");
map.put("startDateTime", timestampStart);
System.out.println("startDateTime : " + timestampStart);
Instant instantEnd = instantStart.plus(1, ChronoUnit.HOURS);
Timestamp timestampEnd = Timestamp.from(instantEnd);
map.put("endDateTime", timestampEnd);
System.out.println("endDateTime : " + timestampEnd);
You still have most of the advantages of the modern API mentioned above, but a couple of extra lines for the conversions. This code printed
startDateTime : 2017-11-29 10:15:00.0
endDateTime : 2017-11-29 11:15:00.0
The times are the same as above, but in my local time zone since Timestamp.toString() renders them this way (which confuses many).
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 (ThreeTen for JSR 310). Conversion between Timestamp and Instant is a little different and goes through a class named DateTimeUtils, but it’s not more complicated. The rest is the same.
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.
Related
private GregorianCalendar formatDate(Date dateStatus, Time timeStatus) {
GregorianCalendar calendar = (GregorianCalendar) GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.setTime(new Date(dateStatus.getTime() + timeStatus.getTime()));
return calendar;
}
The above code returns the Calendar value in milliseconds. But i am getting different value in local and while running the test in jenkins causing the testcase to fail.
Local and Jenkins Server running in different timezones.
Jenkins Error:
Expected: 1554866100000
got: 1554903900000
How can i handle this?
java.sql.Date is an extremely unfortunate API design messup. That class extends java.util.Date, and that class is a lie. It does not represent a date at all (check the source code if you are understandably skeptical). It represent a moment in time, devoid of any timezone information, based on milliseconds since UTC new year's 1970. Anything you care to coerce from a Date object that isn't a very large number or a direct swap to a more appropriate type that doesn't lie (such as java.time.Instant) is therefore suspect: It is picking a timezone implicitly and mixing that in, in order to provide you your answer. This is why most of Date's methods, such as .getYear(), are marked deprecated: In the java core libs, a deprecation marker usually doesn't actually mean "This will be removed soon", it actually means: "This is fundamentally broken and you should never call this method". (See: Thread.stop).
Nevertheless JDBC API (what you use to talk to DBs) was built on top of this; java.sql.Date as well as java.sql.Timestamp extend java.util.Date and thereby inherit the messup. This means date handling in this fashion will convert timestamps in the DB that have full timezone info into timezoneless constructs, and then java-side you can add new timezones, which is a bad way of doing things.
Unfortunately date/time is extremely complicated (see below) and databases have wildly different ways of storing it; usually multiple slightly different date/time types, such as 'TIMESTAMP', 'TIME WITH TIME ZONE', etcetera.
Because of this there is no unique advice that works regardless of context: The right answer depends on your JDBC driver version, DB engine, DB engine version, and needs. This means the best approach is generally to first understand the fundamentals so that you can adapt and figure out what would work best for your specific situation.
java.util.Calendar is even worse. Again a lie (it represents time. Not a calendar!), the API is extremely badly designed (it is very non-java-like). There is a reason this second try at date/time API resulted in yet another date/time library (java.time): It's bad. Don't use it.
So, let me try to explain the basics:
You like to wake up at 8 in the morning. It's noon and you check your phone. It says 'the next alarm will ring in 20 hours'. You now hop onto a concorde at Schiphol Airport in Amsterdam, and fly west, to New York. The flight takes 3 hours. When you land, you check your phone. Should it say 'the next alarm will ring in 17 hours' (3 hours of flight time have passed), or should it say: 'the next alarm will ring in 23 hours' (you actually land at 9 in the morning in New York time because its 6 hours earlier there relative to Amsterdam, so it's 23 hours until 8 o' clock in the morning local time). The right answer is presumably: 23 hours. This requires the concept of 'local time': A point in time stated in terms of years, months, days, hours, minutes, and seconds - but no timezone, and not even 'please assume a timezone for me', but the concept '... where-ever you are now'.
Before you jumped in the plane, you made an appointment at a barber's in Amsterdam for when you return. You made it for March 8th, 2022, at 12:00. When you check your phone it reads: "365 days, 0 hours, 0 minutes, and 0 seconds" as you made the appointment. If you fly to new york, that should now read "364 days, 21 hours, 0 minutes, and 0 seconds": The fact that you're currently in new york has no bearing on the situation whatsoever. You'd think that time-as-millis-since-UTC should thus suffice, but no it does not: Imagine that the netherlands abolishes daylight savings time (crazy idea? No, quite likely actually). The very instant that the gavel hits the desk and the law is adopted that The Netherlands would switch to summer time in 2021, and then stay there forever more, that very moment? Your phone's indicator for 'time until barber appointment' should instantly increment by 1 hour (or it is decrement?). Because that barber appointment isn't going to reschedule itself to be at 11:00 or 13:00.
During your flight, you snapped a pic of the tulip fields before the plane crossed the atlantic. The timestamp of this picture is yet another concept of time: If, somehow, the netherlands decides to apply the timezone change retroactively, then the timestamp in terms of 'this picture was taken on 2021, march 8th, 12:05, Amsterdam time' should instantly pop and now read 13:05 or 11:05 instead. In this way it is unlike the barber appointment.
Before this question can be answered, you need to figure out which of the 3 different concepts of time your situation boils down to.
Only the java.time package fully covers this all. Respectively:
Alarm clock times are represented by LocalDate, LocalTime, and LocalDateTime.
Appointment times are represented by ZonedDateTime.
When did I make the picture times are represented by Instant.
The java.sql.Date type is most equivalent to Instant, which is bizarre, because you'd think that this is more java.sql.Timestamp's bailiwick.
Pragmatics, how to get the job done
Your first stop is to fully understand your DB engine's data types. For example, in postgres:
concept
java.time type
postgres type
alarm clocks
java.time.LocalTime
time without time zone
-
java.time.LocalDate
date
-
java.time.LocalDateTime
timestamp without time zone
appointments
java.time.ZonedDateTime
timestamp with time zone
when i took the picture
java.time.Instant
no appropriate type available
Kind of silly that the implementation of java.sql.Date and java.sql.Timestamp best matches the very concept postgres cannot express, huh.
Once you ensured that your DB is using the right type for your concept, the next thing to check is if your JDBC driver and other infrastructure is up to date. You can use the right types java-side (from the java.time packages): Use these types (LocalDate, Instant, ZonedDateTime, etc) in your infrastructre and if making raw JDBC calls, .setObject(x, instanceOfZDT) instead of .setDate when setting, and .getObject(col, LocalDateTime.class) to fetch, which works on many JDBC drivers.
If it doesn't work, you need to work around the issues, because the process is now that the DB is storing e.g. the year, month, day, hour, minute, second, and complete timezone description (not 'EST' there are way too many zones to cover with 3 letters, but something like 'Europe/Amsterdam'), will then convert this into millis-since-epoch to transfer it to the JDBC server, and then your java code will inject a timezone again, and thus you're going to run into issues. The best bet is to IMMEDIATELY convert ASAP, so that you have an old unwieldy type (java.sql.Date or java.sql.Timestamp) as short as possible and can test right at the source that you're 'undoing the damage' done when your ZDT/LDT type arrives as the Instant-esque java.sql type.
I'm wondering how I would compare times in a Java program. For example: If I choose a time block from 09:00-12:00 and set that. If i choose a second time and choose 11:00-13:00 time period, I would want to be able to have an error pop up, but I'm not exactly sure how to set that up.
For example if you are reserving a time at a massage therapy place from 9-12, you can't reserve another one for yourself at 11-1, it wouldn’t make sense. I'm trying to figure out how to put it into code. My noob level is having me think to have a string as a selection method, then parse the string to integer and compare the integer for overlapping? I want to use the Date class though because it seems very useful and I'd like to learn how to use it.
LocalTime from java.time
LocalTime blockStart = LocalTime.of(9, 0);
LocalTime blockEnd = LocalTime.of(12, 0);
assert blockEnd.isAfter(blockStart);
LocalTime newBlockStart = LocalTime.of(11, 0);
LocalTime newBlockEnd = LocalTime.of(13, 0);
assert newBlockEnd.isAfter(newBlockStart);
if (newBlockStart.isBefore(blockEnd) && newBlockEnd.isAfter(blockStart)) {
System.out.println("" + newBlockStart + '–' + newBlockEnd
+ " overlaps with " + blockStart + '–' + blockEnd);
}
Output from the snippet is:
11:00–13:00 overlaps with 09:00–12:00
A LocalTime is a time of day without a date. A potential liability is that it cannot take into account if the clock is turned forward or backward, for example when summer time (DST) begins or ends. But if you want nothing that fancy, it’s the correct class to use.
The formula for detecting an overlap is standard. I include a link to a question about it below.
I want to use the Date class though…
Don’t, don’t, please don’t. The Date is poorly designed and long outdated. You are seriously better off not learning how to use that class. Learn to use java.time, the modern Java date and time API. A possible place to start is the first link below.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Algorithm to detect overlapping periods [duplicate] (in C#, but the formula used is the same)
Determine Whether Two Date Ranges Overlap
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
I want to see a month which contains whole days.
private void createRandomData(InMemoryCursor cursor) {
List<Object[]> data = new ArrayList<>();
Calendar today = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault());
today.set(Calendar.HOUR_OF_DAY,0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
today.set(Calendar.MILLISECOND, 0);
mStart = (Calendar) today.clone();
mStart.add(Calendar.SEPTEMBER, -5);
while (mStart.compareTo(today) <= 0) {
data.add(createItem(mStart.getTimeInMillis()));
mStart.add(Calendar.SEPTEMBER, 1);
}
cursor.addAll(data);
}
When I write Calendar.SEPTEMBER(or other months), I see red line on Calendar.SEPTEMBER which contains:
Must be one of: Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH, Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND, Calendar.ZONE_OFFSET, Calendar.DST_OFFSET less... (Ctrl+F1)
This inspection looks at Android API calls that have been annotated with various support annotations (such as RequiresPermission or UiThread) and flags any calls that are not using the API correctly as specified by the annotations. Examples of errors flagged by this inspection:
Passing the wrong type of resource integer (such as R.string) to an API that expects a different type (such as R.dimen).
Forgetting to invoke the overridden method (via super) in methods that require it
Calling a method that requires a permission without having declared that permission in the manifest
Passing a resource color reference to a method which expects an RGB integer value.
...and many more. For more information, see the documentation at developer.android.com/tools/debugging/annotations.html
When I run it despite the red line, It shows complicated dates like:
see
I use this library from GitHub:https://github.com/jruesga/timeline-chart-view
Is problem related to library? or It is about Java calendar?
As explained in #Michael's answer, you can't use Calendar.SEPTEMBER in the add method.
If you want to add or subtract a specified number of months, just use Calendar.MONTH. If you want to add/subtract days, you use Calendar.DAY_OF_MONTH and so on.
The Calendar API might be confusing sometimes (most times, IMO), and has lots of problems and design issues.
In Android, there's a better alternative: you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).
As you're getting a Calendar in the default timezone, a good candidate for replacement is a org.threeten.bp.ZonedDateTime (it represents a date and time in a specific timezone). First I use the now() method (that takes the current date/time at the JVM default timezone). Then I use a org.threeten.bp.LocalTime to set the time to midnight.
I also use the minusMonths method to get a date 5 months before the current date, and inside the loop I use the toInstant() method, to get the millis value, and the plusMonths method to get the next month:
// get today at default timezone, at midnight
ZonedDateTime today = ZonedDateTime.now().with(LocalTime.MIDNIGHT);
// 5 months ago
ZonedDateTime start = today.minusMonths(5);
while (start.compareTo(today) <= 0) {
data.add(createItem(start.toInstant().toEpochMilli()));
start = start.plusMonths(1);
}
If you want to add/subtract minutes instead of months, for example, you can use the methods minusMinutes and plusMinutes. There are other methods for another units as well (such as hours, days, and so on), check the javadoc to see all the options.
The problem of using the default timezone is that it can be changed without notice, even at runtime, so it's better to always make it explicit which one you're using.
With Calendar, you can use TimeZone.getTimeZone(zoneName):
Calendar todayCal = Calendar.getInstance(TimeZone.getTimeZone("Europe/London"), Locale.getDefault());
And with ThreeTen Backport, you can use ZoneId.of(zoneName):
ZonedDateTime today = ZonedDateTime.now(ZoneId.of("Europe/London")).with(LocalTime.MIDNIGHT);
In the example above, I used Europe/London, but you can change it to any timezone you want. The API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds() or TimeZone.getAvailableIDs().
You are using an incompatible option. The first parameter of Calendar.add() is a Unit of Time (Day, Week, Hour etc) as defined by the possible options outlined in the error. Calendar.SEPTEMBER is not a unit of time, it is a convenience constant representing the MONTH of September that is typically used in the set() method instead.
Assuming you're iterating through months, you'll need Calendar.MONTH instead.
I was trying to fetch the current time in UTC in Java and came across this post: Getting "unixtime" in Java
I checked out that all solutions
new Date().getTime()
System.currentTimeMillis()
Instant.now().toEpochMillis()
return the same value. I wished to know if there is any difference between the three of them. If so then what?
There is no difference. This is just the result of the evolution of the date API over the years. There is now more than one way to do this.
As far as just getting epoch milliseconds, all three are fine. Things get more complicated as soon as formatting, calendars, timezones, durations and the like become involved.
I'm looking to create a datetime stamp, then add 10 hours to it, then have a thread check to see if the time has elapsed.
I read through, Time comparison but it seems a bit complicated/convoluted for something so simple. Especially if your time comparison goes across midnight.
My understanding is that java's underlying datetime, is suppose to be a long, if this is true, is there a simple way to add another long to it, such as the number equivalent of 10 hours? Or some other means such as adding two dates?
Note: The solution needs to be part of core java, can't be part of a 3rd party lib.
You can use a Calendar to perform that math,
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR, 10); // Add 10 hours.
Date date2 = cal.getTime(); // Now plus 10 hours.
Date date = new Date(); // Now.
You can use the Date.getTime() method to obtain the underlying timestamp, the timestamp is basically the number of milliseconds elapsed since a defined base instant (1970-01-01 00:00:00 IIRC).
System.currentTimeMillis() allows you the get the "now" instant directly, without any detours using Date, Calendar and the like.
The timestamp can then be manipulated basic math:
timestamp += TimeUnit.MILLISECONDS.convert(10, TimeUnit.HOURS);
Example of adding 10 hours:
long nowInMilliSince1970 = System.currentTimeMillis();
long tenHoursAsMilli = TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES);
long tenHoursLater = nowInMilliSince1970 + tenHoursAsMilli;
System.out.println("now in milliseconds: \t\t" + nowInMilliSince1970);
System.out.println("10 hours in milliseconds: \t" + tenHoursAsMilli);
System.out.println("10 hours from now: \t\t" + tenHoursLater);
Checking if the timestamp is in the past is as easy as:
if (timestamp < System.currentTimeMillis()) {
System.out.println("timestamp is in the past");
}
Do note that direct timestamp math has no concept of daylight saving and time zones. If you want that, use a Calendar for math - Calendar implements the dirty exceptional rules for that.
Another way of achieving it using just JDK built in stuff is:
long tenHoursFromNow = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(10);
and then in your Thread you would check:
if(System.currentTimeMillis() > tenHoursFromNow)
{
//Do something as the time has elapsed
}
Although I would argue that the use of Calendar and Date is clearer as to what the intention of your code is trying to achieve.
The bundled java.util.Date and .Calendar are notoriously troublesome. They really should be avoided.
You stated a requirement of no added libraries. So see the java.time part of my answer, using the new package newly added to Java 8. But I urge you to reconsider your reluctance to add a library, especially if you cannot move to Java 8 yet; j.u.Date/Calendar really are that bad.
Both libraries handle anomalies such as Daylight Saving Time.
Consider specifying a time zone rather than rely on the JVM's default. Generally best to work in UTC, and then translate to a local time zone for presentation to the user.
java.time
The java.time package is newly added to Java 8. Inspired by Joda-Time but re-architected. Defined by JSR 310. Extended by the threeten-extra project.
ZonedDateTime tenHoursLater = ZonedDateTime.now().plusHours( 10 );
Joda-Time
Using the Joda-Time 2.3 library.
DateTime tenHoursLater = DateTime.now().plusHours( 10 );
For more info on this kind of use of Joda-Time, see my answer to a similar question.