I want to set the minutes and seconds of a date to 0,
but I see that those methods are deprecated
Date yesterday = Date.from(Instant.now().minus(24, ChronoUnit.HOURS));
yesterday.setMinutes(0);
yesterday.setSeconds(0);
TL;DR
ZonedDateTime yesterday = ZonedDateTime.now(ZoneId.of("Europe/Madrid"))
.minusDays(1)
.truncatedTo(ChronoUnit.HOURS);
System.out.println("Yesterday with minutes and seconds set to 0: " + yesterday);
Running just now gave
Yesterday with minutes and seconds set to 0: 2019-03-17T23:00+01:00[Europe/Madrid]
Define yesterday
If you intended “yesterday at the same time”, that’s not always 24 hours ago. Due to summer time (DST) and other anomalies, a day may be for example 23, 23,5 or 25 hours.
java.time
The ZonedDateTime class of java.time, the modern Java date and time API, takes the time anomalies in your time zone into account. So when you subtract a day, you do get the same time yesterday, if that time exists at all.
.truncatedTo(ChronoUnit.HOURS) sets minutes, seconds and fraction of second to 0 in the time zone in question.
You were trying to use the Date class, but in Java 8 you should not do that. It’s poorly designed and long outdated. I find java.time so much nicer to work with.
If you indispensably need an old-fashioned Date
You may need a Date for a legacy API that you cannot change or cannot afford to change just now. In that case convert only in the last moment:
Instant yesterdayInstant = yesterday.toInstant();
Date yesterdayDate = Date.from(yesterdayInstant);
System.out.println("As old-fashoined Date: " + yesterdayDate);
In my time zone (Europe/Copenhagen, currently agrees with Europe/Madrid) I got
As old-fashoined Date: Sun Mar 17 23:00:00 CET 2019
Link
Oracle Tutorial: Date Time explaining how to use java.time.
Related
In my application while converting to date object i am always getting one hour behind time . this issue in happening only with moscow time zone.
below is code :
MutableDateTime mdt = new MutableDateTime(time);
mdt.setSecondOfMinute(0);
mdt.setMinuteOfDay(0);
mdt.toDate()
in above code mdt.todate() returning 5/30/2021 23:00 instead of 5/31/2021 00:00.
jdk version : "1.8.0_191"
Edit: Why does "June 6 00:00" after conversion mdt.toDate() become "May 31 23:00"?
Your surprising observation probably comes from an old Joda-Time version with an old time zone database where Europe/Moscow was at offset +04:00 rather than +03:00. It was between 31 October 2010 and 26 October 2014. If Joda-Time “believes” that this is still the case, it sets your MutableDateTime to something like 2021-06-01T00:00:00.000+04:00 with offset +04:00 instead of +03:00. This corresponds to 2021-05-31T20:00Z UTC where the correct point in time would have been 2021-05-31T21:00Z UTC. In other words, it’s an hour too early. Therefore you get a Date that is an hour too early too. Your Java 1.8 “knows” that Moscow is at offset +03:00 these days and therefore prints the time as Mon May 31 23:00:00 MSK 2021.
Solutions include:
Upgrade to a newer version of Joda-Time that has an up-to-date time zone database.
Build your Joda-Time from sources for the version that you are using only with a newer bundled time zone database. This is explained on the Joda-Time home page, see the second link below.
Original answer
Your surprising observation probably comes from an old Java version with an old time zone database where Europe/Moscow was at offset +04:00 rather than +03:00. It was between 31 October 2010 and 26 October 2014. I have reproduced your result on my Java 1.7.0_67 and verified that my Java installation “believes” that Moscow is at offset +04:00 and does not use summer time (DST), as was the case in the mentioned period.
Your Joda-Time seems to be new enough to know that Europe/Moscow is at +03:00 so correctly converts your MutableDateTime to a Date at 00:00 hours on the date in question. Only when you print this Date, Java uses its default time zone, still Europe/Moscow, but its own time zone data, and therefore incorrectly prints the time as 01:00 hours instead of 00:00.
Possible solutions include:
Upgrade to a newer Java version that has up-to-date time zone data.
Fix your current Java installation by upgrading only its time zone database. See Timezone Updater Tool in the second link below.
Setting the time to the start of the day
Edit: you added:
Here MutableDateTime time =new MutableDateTime(new Date().getTime());
To get a Date representing the start of today’s date using Joda-Time:
Date oldfashionedDateObject = LocalDate.now(DateTimeZone.getDefault()).toDate();
System.out.println(oldfashionedDateObject);
Output just now:
Mon May 31 00:00:00 MSK 2021
Original aside: As an aside, the simpler and safer way to set the time to the start of the day is:
mdt = mdt.toDateTime().withTimeAtStartOfDay().toMutableDateTime();
If you need to keep the same MutableDateTime object, instead do:
mdt.setMillis(mdt.toDateTime().withTimeAtStartOfDay().toInstant());
First of all I would be worried that your code may run in a time zone and on a day that in that time zone has a transition at 00:00 so that the first moment of the day is 01:00 or something else. In this case I b believe that your code would throw a surprising exception. Also I find setting individual fields low-level and prefer to set everything in one method call even if it requires further operations to determine the argument to pass to that method.
Links
Time Zone in Moscow, Russia (Moskva).
Joda-Time Updating the time zone data.
Timezone Updater Tool on Oracle’s web site.
I'm unsure whether this can be made more efficient or not, but I need to get the number of days that have passed since a unix/epoch timestamp, where the time itself is not a factor, only the date compared to now.
Example 1:
Timestamp is : 3rd September 14:35
Compared to now which is: 4th September 00:35
Days difference = 1
Example 2:
Timestamp is: 3rd September 23:55
Compared to now which is: 4th September 00:35
Days difference = 1
Example 3:
Timestamp is: 2nd September 02:23
Compared to now which is: 4th September 00:35
Days difference = 2
To get this, I have the following code:
String epoch = "1599134401" // the unix/epoch timestamp in seconds
Long epochMillis = Long.valueOf(epoch) * 1000;
Date epochDateObj = new Date(epochMillis);
Calendar tsCal = Calendar.getInstance();
tsCal.setTime(epochDateObj);
tsCal.set(Calendar.HOUR_OF_DAY, 0);
tsCal.set(Calendar.MINUTE, 0);
tsCal.set(Calendar.SECOND, 0);
tsCal.set(Calendar.MILLISECOND, 0);
Calendar today = Calendar.getInstance();
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
today.set(Calendar.MILLISECOND, 0);
long diffInMillies = Math.abs(today.getTime().getTime() - tsCal.getTime().getTime());
long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);
if(diff > 1) {
return diff + " days";
} else {
return diff + " day";
}
The above code works, but to me, it seems quite elaborate for such a rather small thing as this.
Any suggestions for optimizing it? Maybe there's some functionality I don't know about. Its an Android app which is using a rather old SDK (back to Android 4.1).
Days are quite fundamentally a human concept. They involve politics, opinion, confusion, timezones, eras, epochs, and other very hairy concepts. java.util.Date has no snowball's chance in hades to do it right. Nor does calendar.
Your only hope is a proper API, such as java.time.
Furthermore you need to clean up your question. What you're asking is impossible; you're comparing guns to grandmas. epoch-time is fundamentally a 'computer' concept - it refers solely to moments in time, it has no idea when, where, who, which political party, etc you are asking. Which is a problem, because without any of that information it is NOT possible to know what day it is. Seconds are more or less universal, but days are not. A day can be 23 hours or 25 hours, or 23 hours, 59 minutes and 59 seconds, or 24 hours and 1 second - sometimes whole days get skipped, etcetera. 'how long is a day' is not answerable without knowing who you ask and what timezone (and sometimes, political entity!) is used as context.
So, let's say you're asking someone in arizona. The answer will then depend rather a lot on where in arizona you ask and who you ask: You would need to (potentially) know whether the person you so happen to ask applies daylight savings time or not back in 1970 as well as in the 'target' time. This depends on whether you're asking when you're on an native american reservation within arizona or not, and/or if the person you're asking is sticking to NAR zones or not. Hence why I mentioned the politics thing, and why what you want is completely impossible.
java.time to the rescue which can actually represent the crazy mess!
Instant represents a moment in time. It's internally stored as epoch-millis and cannot tell you the day, month, year, era, hour, etc of that moment in time by itself. That's because.. well, that's because that's how reality works. If I snap my fingers right now, and I ask someone 'what time is it', it depends on where I am and where the person I'm asking is and what political parties they ascribe to, so it's not possible. But, you combine a Zone and an Instant and now we're getting somewhere.
LocalDateTime represents a time as a human would say it: A year/month/day + hour/minute/second. It is not possible to turn this into epochmillis for the same reason in reverse. And for the same reason, if you combine this with a Zone doors start opening.
ZonedDateTime tries to bridge the gap: It represents a time as a human would say it, but we code in the location (and political affiliations) of the human who said it. You can store this either as a LocalDateTime + TimeZone, or as an Instant+TimeZone (you don't need to know how it is implemented, of course). You can move from a ZDT to either Instant or LocalDateTime, of course, and this one can answer many questions.
Let's try to solve your problem:
String epoch = "1599134401"; // the unix/epoch timestamp in seconds
String where = "Europe/Berlin"; // what you want is impossible without this!!
Instant instant = Instant.ofEpochSecond(Long.valueOf(epoch));
ZonedDateTime target = instant.atZone(ZoneId.of(where));
ZonedDateTime today = ZonedDateTime.now(where);
long days = ChronoUnit.DAYS.between(target, today);
System.out.println(days);
As a general rule, if you start doing serious math on dates you're messing up and it won't work. Not that your tests will ever catch it of course; it'll go ape when the clocks go back or forward or some political party decides 5 days before it happens to end daylight savings time, or the client is in one place and your server is in another, etc - all stuff that tests rarely catch.
Proper use of java.time should usually mean you aren't doing much calculation, and so it is here, fortunately.
There’s already a very great and insightful answer by rzwitserloot, I highly recommend it. Just as a minor supplement I wanted to give you my go at the code. Still using java.time, the modern Java date and time API, of course.
ZoneId zone = ZoneId.of("Europe/Tirane");
DateTimeFormatter epochSecondFormatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.INSTANT_SECONDS)
.toFormatter();
String epoch = "1599134401"; // the unix/epoch timestamp in seconds
Instant then = epochSecondFormatter.parse(epoch, Instant::from);
LocalDate thatDay = then.atZone(zone).toLocalDate();
LocalDate today = LocalDate.now(zone);
long diff = ChronoUnit.DAYS.between(thatDay, today);
diff = Math.abs(diff);
if (diff == 1) {
System.out.println("" + diff + " day");
} else {
System.out.println("" + diff + " days");
}
When I ran the code just now, the output was:
1 day
Since you want to ignore the time of day, LocalDate is the correct class to use for the dates. A LocalDate is a date with time of day and without time zone.
In English (not being a native speaker, though) I prefer saying “0 days”, not “0 day”. So I have changed your condition for choosing between singular and plural.
Did your code work?
Your code gives inaccurate results in corner cases. TimeUnit is generally a fine enum for time unit conversions, but it assumes that a day is always 24 hours, which is not always the case, as rzwitserloot explained. The java.time code of that answer and of this one correctly takes transitions to and from summer time (DST) and other time anomalies into account.
Question: Does java.time work on Android 4.1?
java.time works nicely on both older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On older Android either use desugaring or the Android edition of ThreeTen Backport. It’s called ThreeTenABP. In the latter case make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
Java 8+ APIs available through desugaring
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
If we don't want to add the ThreeTenABP library to our project, we need to normalize to a date-without-time in UTC, in order to prevent things like Daylight Savings Time to skew the results.
For that, a helper method is appropriate:
static long toDateUtcMillis(Date time) {
// Get year/month/day according to default time zone
Calendar cal = Calendar.getInstance();
cal.setTime(time);
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH);
int day = cal.get(Calendar.DAY_OF_MONTH);
// Set year/month/day in UTC
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.clear();
cal.set(year, month, day);
return cal.getTimeInMillis();
}
We can now easily calculate the number of days. In the following we return negative value if the dates are reverse. Add call to Math.abs() if that's not desired.
static int daysBetween(Date date1, Date date2) {
long dateMillis1 = toDateUtcMillis(date1);
long dateMillis2 = toDateUtcMillis(date2);
return (int) TimeUnit.MILLISECONDS.toDays(dateMillis2 - dateMillis1);
}
Test
public static void main(String[] args) throws Exception {
test("3 September 2020 14:35", "4 September 2020 00:35");
test("3 September 2020 23:55", "4 September 2020 00:35");
test("2 September 2020 02:23", "4 September 2020 00:35");
}
static void test(String date1, String date2) throws ParseException {
// Parse the date strings in default time zone
SimpleDateFormat format = new SimpleDateFormat("d MMMM yyyy HH:mm", Locale.US);
int days = daysBetween(format.parse(date1), format.parse(date2));
System.out.println("Timestamp is: " + date1);
System.out.println("Compared to: " + date2);
System.out.println("Days difference = " + days);
System.out.println();
}
Output
Timestamp is: 3 September 2020 14:35
Compared to: 4 September 2020 00:35
Days difference = 1
Timestamp is: 3 September 2020 23:55
Compared to: 4 September 2020 00:35
Days difference = 1
Timestamp is: 2 September 2020 02:23
Compared to: 4 September 2020 00:35
Days difference = 2
Current time is Sat Apr 04 15:02:00 AEST 2020.
In the following snippet, I create a Date object and add 86400000L milliseconds (1 day) to it:
Date date = new Date();
date.setTime(date.getTime() + 86400000L);
System.out.println(date);
The output is Sun Apr 05 14:02:00 AEST 2020. I don't understand why the result adds only 23 hours to my current time, instead of 24 hours.
Any help would be appreciated.
The code works just fine. The AEST on your output means that the date regards Australian Eastern Standard Time. Googling for AEST dst shows that on Sunday, April 5, 3:00 am 2020 the clock will "go back" 1 hour. Thus adding 24 hours just before the DST change, will only move the time 23 hours forward.
If you run that code tomorrow, you'll not have this "problem".
Do use java.time, the modern Java date and time API, for your date and time work.
ZonedDateTime currentTime = ZonedDateTime.now(ZoneId.of("Australia/Sydney"));
System.out.println(currentTime);
ZonedDateTime tomorrowSameTime = currentTime.plusDays(1);
System.out.println(tomorrowSameTime);
Output when running just now:
2020-04-04T16:00:30.579484+11:00[Australia/Sydney]
2020-04-05T16:00:30.579484+10:00[Australia/Sydney]
Please observe: we got the same time of day tomorrow, 16:00. Because summer time (daylight saving time) ends, the UTC offset for tomorrow is different, +10:00 instead of +11:00. And importantly, while I find + 86400000L pretty close to unreadable for adding a day, .plusDays(1) conveys the intention very clearly.
Please insert a different Eastern Australian time zone if required.
What went wrong in your code? cherouvim has explained this very nicely in the other answer, no need for me to repeat. Only allow me to add that the Date class is not only poorly designed — giving rise to your confusion — it is also long outdated. I recommend you don’t use it. And as cherouvim notes in a comment, programming with dates is hard. Don’t trust that you can yourself convert 1 day to 86 400 000 milliseconds. Leave all date and time calculations to proven library methods.
Link: Oracle tutorial: Date Time explaining how to use java.time.
I've been scratching my head trying to understand why the FastDateFormat parser is returning a very incorrect time. The string timestamp I'm trying to convert is in GMT/UTC, and I'm trying to insert it into a Timestamp column in DB2.
Here's the code:
String gmtTimestamp = "2017-03-12 02:38:30.417000000";
FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSSSSSSS", TimeZone.getTimeZone("GMT"));
java.util.Date d = fdf.parse(gmtTimestamp);
Timestamp ts1 = new Timestamp(d.getTime());
System.out.println(ts1);
The time that's printed is: "2017-03-16 17:28:30.0", 4 days and nearly 15 hours off. What's happening here?
TL;DR
String gmtTimestamp = "2017-03-12 02:38:30.417000000";
DateTimeFormatter dtf
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSSSSS");
Instant i1 = LocalDateTime.parse(gmtTimestamp, dtf)
.atOffset(ZoneOffset.UTC)
.toInstant();
System.out.println(i1);
This prints
2017-03-12T02:38:30.417Z
The implicit call to Instant.toString() produces date and time in UTC (for our purpose the same as GMT), so you recognize the date and time from your GMT string.
java.time
I recommend you drop the java.sql.Timestamp class. It is long outdated, and today we have so much better in java.time, the modern Java date and time API. The original purpose of Timestamp was to store and retrieve date-time values to and from SQL databases. With a sufficiently new JDBC driver (JDBC 4.2), you can and will want to take advantage of two classes from java.time for that purpose: Instant for a point on the timeline and LocalDateTime for date and time without time zone.
In case you do need a Timestamp (for instance for a legacy API or an older JDBC driver you don’t want to upgrade just now), convert the Instant from above to Timestamp just before handing it to the legacy API or the database:
Timestamp ts1 = Timestamp.from(i1);
System.out.println(ts1);
Running in America/Chicago time zone this prints:
2017-03-11 20:38:30.417
Timestamp.toString() grabs the JVM’s time zone setting and outputs the date and time in this time zone (which may be confusing).
What was happening in your code snippet?
FastDateFormat uses SimpleDateFormat format patterns. In SimpleDateFormat and FastDateFormat capital S means milliseconds. so 417000000 was taken as milliseconds (where you intended 417 milliseconds), it is rpughly the same as 4 days 20 hours, which were added to the date-time value up to the seconds. I reproduced your result using a SimpleDateFormat and setting my JVM to America/Chicago time zone. Other time zones that are -5 hours from UTC in March will produce the same result. Since the Timestamp was printed at offset -5:00, the apparent difference in the output was a bit less than the real difference of 4 days 20 hours, “only” 4 days 15 hours.
By contrast, though the modern DateTimeFormatter mostly uses the same format pattern letters, to it capital S means fraction of second, which is why we get 30.417 seconds as expected and desired.
Quotes
All patterns are compatible with SimpleDateFormat (except time zones
and some year patterns - see below).
(FastDateFormat documentation)
S Millisecond Number 978
(SimpleDateFormat documentation)
What I am trying to do is store a date, represented by a long of milliseconds, that is the next midnight from the current time. So, posting this at 10:11 PM, I would want a date representing 12:00 AM tomorrow morning. For this task, I wrote this line of code (knowing that there are 86400000 milliseconds in one day):
long time = System.currentTimeMillis() + (86400000 - (System.currentTimeMillis() % 86400000));
The line is designed to calculate the extra milliseconds from the last midnight, substract that from one whole day to find the time until the next midnight, then add that to the current time so that the new value is the value of the next midnight. For whatever reason though, the date object I am using to debug spits out "Wed Apr 20 20:00:00 EDT 2016" when calling the #toString() method. The current time is said to be "Tue Apr 19 22:08:34 EDT 2016" at the same time as the other date is being calculated.
This means that the long of milliseconds is actually representing 8:00 PM the next day, while I want it to represent 12:00 AM. Can anyone help me spot the flaw in my logic?
I most likely am missing something obvious, so bear with me.
NOTE: I also tried calculating the time like this:
long time = System.currentTimeMillis() - (System.currentTimeMillis() % 86400000) + 86400000;
But this resulted in the same Date object.
You are forgetting to adjust for your timezone. Here is a simple way to achieve this.
TimeZone tz = TimeZone.getDefault();
long time = System.currentTimeMillis() + (86400000 - (System.currentTimeMillis() % 86400000));
time -= tz.getOffset(time);
System.out.println(new Date(time));
java.time
You are using flawed troublesome date-time classes that have long been supplanted, first by the Joda-Time library and now it's successor, the java.time framework built into Java 8 and later. Much of java.time has been back-ported to Java 6 & 7 and then adapted to Android by the ThreeTenABP project.
An Instant is a moment on the timeline in UTC, with a resolution in nanoseconds.
Instant instant = Instant.now();
Apply a time zone to get a ZonedDateTime. Time zone is crucial to determining a date. It may already be “tomorrow” to the east of you or “yesterday” to the west.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( zoneId );
To get the first moment of the day, do not assume that time is 00:00:00.0. Anomalies such as Daylight Saving Time may result in a different time. Let java.time determine that time-of-day. To do that we must go through the LocalDate class.
LocalDate today = zdt.toLocalDate();
Days are not always 24 hours long, which is an incorrect assumption in you question and code. Again, let java.time determine the following day with a call to plusDays.
LocalDate tomorrow = LocalDate.plusDays( 1 );
ZonedDateTime zdtTomorrowStart = tomorrow.atStartOfDay( zoneId );
I suggest you avoid tracking date-time values as a count from epoch. That is prone to error, and is terribly difficult to debug. And you will be losing data as you go from nanosecond resolution to milliseconds. But if you insist.
long millis = zdtTomorrowStart.toInstant().toEpochMilli();
Direct time calculation can be tricky, there are quite a few corner cases. I think the simplest solution to avoid it is :
// now, with current timezone and locale
Calendar calendar = new GregorianCalendar();
// tomorrow
calendar.add(Calendar.DAY_OF_MONTH, 1);
// midnight
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
// get the resulting date
Date date = calendar.getTime();