35

why when i add 30 days to today's day i got today's day - 30, and when i add 20 it adds??

here is a sample

import java.text.DateFormat;
import java.util.Date;

public class DatePlus
{

    public static void main(String[] args)
    {
        Date now = new Date();
        Date now1 = new Date();
        Date now2 = new Date();
        DateFormat currentDate = DateFormat.getDateInstance();

        Date addedDate1 = addDays(now2, 20);
        Date addedDate2 = addDays(now1, 30);
        System.out.println(currentDate.format(now));
        System.out.println(currentDate.format(addedDate1));
        System.out.println(currentDate.format(addedDate2));
    }

    public static Date addDays(Date d, int days)
    {
        d.setTime(d.getTime() + days * 1000 * 60 * 60 * 24);
        return d;
    }
}

"this is the console"

Jul 30, 2012
Aug 19, 2012
Jul 10, 2012
Elye M.
  • 2,667
  • 4
  • 30
  • 43
  • Related: You can add 10 days to a date using Calendar.Add http://docs.oracle.com/javase/6/docs/api/java/util/Calendar.html#add(int, int) – Nivas Jul 30 '12 at 19:08
  • 10
    I sense a possible integer overflow here (days * 1000 * 60 * 60 * 24) remember, a long isn't a long until you say it is. so try this days * 1000L * 60L * 60L * 24L just to be safe... – Shark Jul 30 '12 at 19:10
  • FYI, the troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/9/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/9/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/9/docs/api/java/time/package-summary.html) classes built into Java 8 & Java 9. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Mar 25 '18 at 04:30

7 Answers7

63

Use a Calendar. http://docs.oracle.com/javase/6/docs/api/java/util/GregorianCalendar.html

Pseudo code:

Calendar c= Calendar.getInstance();
c.add(Calendar.DATE, 30);
Date d=c.getTime();
Pervez
  • 525
  • 6
  • 16
Olivier Refalo
  • 50,287
  • 22
  • 91
  • 122
  • 1
    This. Calendar is a good way of managing dates, and you can get Date objects out of it. – Shark Jul 30 '12 at 19:09
  • 3
    That's a lot of upvotes for code that doesn't work! DAY_OF_WEEK_IN_MONTH is very much the wrong field to be adding to. DATE – Affe Jul 30 '12 at 19:16
  • 1
    that's right ;-) .. that's why I mentioned "pseudo", let me fix it – Olivier Refalo Jul 30 '12 at 19:19
  • FYI from JavaDoc re `DATE` -- _"This is a synonym for DAY_OF_MONTH"_ – Madbreaks Apr 22 '20 at 16:34
  • FYI, the terribly flawed date-time classes such as [`java.util.Date`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/tutorial/datetime/TOC.html) classes built into Java 8 and later. – Basil Bourque Jun 14 '20 at 21:38
  • I wonder how to add 8 years to the initial post date? ;-) – Olivier Refalo Jun 17 '20 at 06:46
35

This is because 30 * 1000 * 60 * 60 * 24 overflows Integer.MAX_VALUE, while 20 * 1000 * 60 * 60 * 24 does not.

Stefan Mücke
  • 823
  • 7
  • 12
21
  1. Date is not tied to any calendar system used by human beings. It just represents point in time. Adding 30 days to Date makes no sense, it is like adding 20 to red color.

  2. Common approach of adding 1000 * 60 * 60 * 24 is wrong. You are adding 86400 seconds, but one day is not necessarily 86400 seconds long. It can be one hour longer or shorter due to . It can be one second longer or shorter due to leap seconds.

  3. What you should do is converting Date into a Calendar (which actually represents some calendar system, like GregorianCalendar. Then simply add days:

    Calendar calendar = new GregorianCalendar(/* remember about timezone! */);
    calendar.setTime(date);
    calendar.add(Calendar.DATE, 30);
    date = calendar.getTime();
    
  4. Use DateUtils.addDays() from Apache Commons Lang:

    DateUtils.add(date, 30);
    

    This does not violate what was written above, it converts to Calendar underneath.

  5. Or avoid this hell altogether and go for Joda Time.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 1
    or go straight to Joda Time - method for adding days: http://joda-time.sourceforge.net/apidocs/org/joda/time/Days.html#plus(org.joda.time.Days) – dantuch Jul 30 '12 at 19:12
  • 1
    @SJuan76: whatever, the whole idea of converting days to milliseconds is broken. And there is no overflow in my post ;-). But +1. – Tomasz Nurkiewicz Jul 30 '12 at 19:18
6

tl;dr

LocalDate.now().plusDays( 30 )

Details

As others pointed out, the specific problem is an overflow of a 32-bit integer.

That code also ignores crucial issue of time zone.

The bigger problem is trying to roll-your-own date-time calculations rather than using a decent library.

java.time

The java.time classes built into Java 8 and later supplant the troublesome old date-time classes.

An Instant is a moment on the timeline in UTC with a resolution of nanoseconds.

Instant now = Instant.now();  

Apply a time zone to get a ZonedDateTime.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Now add your 30 days. Let java.time handle anomalies such as Daylight Saving Time (DST).

ZonedDateTime zdtLater = zdt.plusDays( 30 );

Or perhaps you meant a month. Let java.time handle the details about months having different lengths.

ZonedDateTime zdtMonthLater = zdt.plusMonths( 1 );

See this Question for info about converting between new and old types. See new methods added to the old class such as:

java.util.Date utilDate = java.util.Date.from( zdt.toInstant() );
java.util.Calendar utilCalendar = java.util.GregorianCalendar.from( ZonedDateTime );

If you want a date-only value, use LocalDate. Same idea as above, add days or months.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
5

days is an integer.

30 * 1000 * 60 * 60 * 24 is 2,592,000,000, greater than 2,147,483,647 (the greatest int in Java). You have a buffer overflow and your result is a considerable smaller, negative number (check it in binary, convert back to int as 2 complement)

The easy fix is to cast one of the values as long so the result of the expression is stored as a long, which can hold that value without overflow.

 (long) days * 1000L * 60 * 60 * 24

Always recomended is to use Calendar or maybe other api (I have heard of JodaTime, but not used it) to manipulate dates.

SJuan76
  • 24,532
  • 6
  • 47
  • 87
4

You're encountering an integer overflow in your calculations. The value of days * 1000 * 60 * 60 * 24 is larger than the maximum value that is allowed for an signed int when days = 30, but not when days = 20. When you increment a signed integer that is at its maximum possible value, instead of increasing in value, the sign flips and it becomes negative! This is why your date goes backwards - you're adding a negative number to it.

To resolve this, you can use the long datatype, which has a much larger maximum value than an integer, like so:

long secondsToAdd = days;
secondsToAdd *= (1000*60*60*24);
d.setTime(d.getTime() + secondsToAdd);
Lanaru
  • 9,421
  • 7
  • 38
  • 64
0

Use this function :

public static String addToCurrentDate(String format,int day,int month,int year){

        DateFormat dateFormat = new SimpleDateFormat(format);

        Calendar calendarInstance = Calendar.getInstance();
        calendarInstance.add(Calendar.YEAR, year);
        calendarInstance.add(Calendar.MONTH, month);
        calendarInstance.add(Calendar.DATE, day);
        return dateFormat.format(calendarInstance.getTime());
    }
Abhishek Sengupta
  • 2,938
  • 1
  • 28
  • 35
  • Please don’t teach the young ones to use the long outdated and notoriously troublesome `SimpleDateFormat` class. At least not as the first option. And not without any reservation. Today we have so much better in [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter`. – Ole V.V. Mar 12 '19 at 00:37
  • thnks for pointing out,Actually this code was used for building an android app. In android most of the new java API requires latest Android OS API levels.So its better to use older java classes to support all older devices. – Abhishek Sengupta Mar 13 '19 at 03:53