Add 1 Week to a Date, which way is preferred?
Asked Answered
P

11

25

I am reviewing some code at work and came across an inconsistency in how the code handles adding 1 week to the current time and was wondering if there was any reason why one should really be preferred over the other:

The first was a utility method:

public static Date addDaysToDate(final Date date, int noOfDays) {
    Date newDate = new Date(date.getTime());

    GregorianCalendar calendar = new GregorianCalendar();
    calendar.setTime(newDate);
    calendar.add(Calendar.DATE, noOfDays);
    newDate.setTime(calendar.getTime().getTime());

    return newDate;
}

And the second used simple millisecond arithmetic:

long theFuture = System.currentTimeMillis() + (86400 * 7 * 1000);
Date nextWeek = new Date(theFuture);

The second method obviously uses 'magic numbers' to define a week, but this could be moved to a constant MILLISECONDS_IN_ONE_WEEK = 86400 * 7 * 1000 So other than that, is there any reasons why one of these methods should be preferred over the other?

Basically I want to change the code to be consistent throughout, but I'm not entirely sure which one to remove. So any arguments one way or the other would be useful.

Passover answered 21/7, 2010 at 14:29 Comment(3)
cough Use Joda-Time. DateTime newDate = date.plusWeeks(1);Velutinous
The code is run on a Windows Mobile device using a JVM based largely on Java 1.2. Joda is only tested against 1.4 and later, so I don't want to add a third party library that was not really designed to run on the platform I am using. But thanks for the suggestion.Passover
Update: GregorianCalendar, and both Date classes bundled with Java were years ago supplanted by the modern java.time classes defined in JSR 310, and built into Java 8+.Anastasia
C
23

The two methods will behave differently on daylight savings boundaries. The first method will continue returning the same time of the day, regardless of daylight savings status. The second method will return times which vary an hour in each direction as daylight savings time starts and stops.

Cal answered 21/7, 2010 at 14:46 Comment(0)
C
7

The below can be done in java 8, Java 8 rocks !!

public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        System.out.println("Current date: " + today);

        //add 1 week to the current date
        LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
        System.out.println("Next week: " + nextWeek);
    }
Convocation answered 15/8, 2016 at 17:0 Comment(2)
Shorter simpler code omitting the ChronoUnit with same effect: today.plusWeeks( 1 )Anastasia
I suggest always passing a time zone (ZoneId) to the now method. If omitted your JVM’s current default time zone is implicitly applied. That default can vary, even during the execution of your app! The time zone is crucial as for any given moment the date varies around the globe. LocalDate.now( ZoneId.of( "America/Montreal" ) )Anastasia
O
3

Be very careful about using method two which caught me out the other day. Consider

private static long ONE_YEAR_AS_MILLISECONDS = 365*24*60*60*1000;

This looks innocent enough, but in fact will not produce what is expected as the multiplication uses integers which, when multiplied by each the other integers, causes a numeric overflow and will yield an unexpected result. This is because the max int value in Java is 2,147,483,647 and yet there are 31,536,000,000 ms in a year. On my machine the above code produces 1,471,228,928 which is obviously not correct.

Instead you need to do this:

private static long ONE_YEAR_AS_MILLISECONDS = 365L*24L*60L*60L*1000L;
Obsolesce answered 21/7, 2010 at 19:54 Comment(0)
W
1

First and foremost, I would argue that you replace it with JodaTime. http://joda-time.sourceforge.net/ It is a very nice time library. You'll want to look at this page to see how easy it is to add days or weeks to a particular point in time: http://joda-time.sourceforge.net/key_period.html Can't do this, mobile device with incompatible JVM. Bummer.

Your first example is easier to read and will be easier to use by your developers. It also uses the Calendar classes which is the generally accepted way to manipulate dates in Java. What makes it better is that it has a clear method name that sets the expectation for what it does.

So if you refactor your system to consistently use com.DaveJ.util.date.DateUtils.addDaysToDate(final Date date, int noOfDays) you can then do whatever you want inside that method, be it Calendar or millis or Joda, and be consistent within your application. Don't forget to write some unit tests for it!

Wadding answered 21/7, 2010 at 14:38 Comment(1)
Another answer mentioned speed. If you have this logic encapsulated in one method, in one place, it will be VERY easy to benchmark it and tune it for speed as necessary.Wadding
A
1

tl;dr

For a date only, use LocalDate:

java.time.LocalDate.now().plusWeeks( 1 ) 

For a date with time-of-day, use ZonedDateTime:

java.time.ZonedDateTime.now().plusWeeks( 1 ) ;

Details

For a date-only value, see the Answer by javaHelper. The LocalDate class purposely has no time-of-day and no time zone.

ZonedDateTime

For a date-time value, use ZonedDateTime to represent a date and a time-of-day along with a time zone to account for anomalies such as Daylight Saving Time (DST).

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now( zoneId );
ZonedDateTime weekLater = now.plusWeeks( 1 );

Or add some arbitrary number of days.

ZonedDateTime later = now.plusDays( someNumberOfDays );

About java.time

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

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

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

Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time.

Anastasia answered 15/8, 2016 at 20:28 Comment(0)
C
0

The more general, the more useful it will be. If you always add a week, I'd chose the second method. If you have different adds, I'd chose the first one, perhaps replacing days for seconds or even millis if needed.

Contravene answered 21/7, 2010 at 14:32 Comment(0)
H
0

The first will be slower, so if performance is an issue, the second one is better.

Harlem answered 21/7, 2010 at 14:37 Comment(4)
It is run on a Windows Mobile device, so performance is definitely a concern. How much of an impact do you think it would have (roughly)?Passover
Depends how often it's called, that kind of thing. To have an idea you'd need to profile it. As an example though, in some time critical code I had to look over, we were constantly calling "new Date().getTime()" - replacing that with "System.currentTimeMillis()" made a significant difference because we weren't repeatedly instantiating Date objects.Harlem
"will be slower" is pretty subjective. Calendar and date field manipulation is mostly about changing the underlying long representation of the date anyway, which means that "slower" comes down to how fast it is to create objects - which is much much faster than most people think.Thorne
I agree, I clarified in my reply above.Harlem
H
0

It depends! Because of leap seconds, DST and other calendar oddities, the two are not always equivalent.

For business and every day use, always use the first method and the performance are not bad at all. It will handle those things for you.

For scientific needs, often you have to use a monotone clock (here the second one).

Haft answered 21/7, 2010 at 14:41 Comment(0)
S
0

The two methods might give different results when a change in daylight saving time is involved. Imagine the current time is 23:50 and at 02:00 the clock jumps to 03:00. When you just add 7 days in milliseconds the time would be 00:50 on the following day. Addings 7 days, the resulting time would still be 23:50.

To make the confusion complete, you could also try add(Calendar.WEEK_OF_YEAR, 1), not sure how that would differ though.

Smalley answered 21/7, 2010 at 14:46 Comment(0)
K
0

Since you're using it on the mobile device, the first method is preferable. The reason is that your code should be independent of specific calendar, DST and other problems, such as seconds overrun(leap seconds).

You have to remove dependence on GregorianCalendar and create it using Calendar.getInstance().

Karie answered 21/7, 2010 at 14:47 Comment(1)
You really shouldn't use getInstance(), in this case. I'm guessing the code is trying to calculate 1 week in the future according to the Gregorian Calendar, not one week in the future according to the user's local calendar. Although why the user would be using the Decimal Calendar from the French Revolution, I have no idea.Christianity
G
0

Since Java8, with Date only, ignoring any summer time/winter time shift between 'date' and the future date:

Date newDate = Date.from(date.toInstant().plus(noOfDays, ChronoUnit.DAYS));
Geller answered 14/4, 2023 at 11:17 Comment(7)
This will not work correctly on summer time transitions (spring forward, fall back).Curnin
Both Date classes bundled with Java are terribly flawed with poor design decisions. They were years ago supplanted by the modern java.time classes defined in JSR 310.Anastasia
I totally agree that Date is terrible. But if it is used already in the code, sometimes you just need to use what you have. @OleV.V. that's a very interesting comment, could you elaborate? I view both Date and Instant classes as instants in time. Am I wrong?Geller
You are correct. Assuming Europe/London time zone, when date is Fri Mar 24 23:00:00 GMT 2023, then Date.from(date.toInstant().plus(7, ChronoUnit.DAYS)) yields Sat Apr 01 00:00:00 BST 2023. The Instant class assumes that every day is 24 hours, but in the UK Sunday 26 March this year was only 23 hours because summer time began and the clocks were turned forward.Curnin
Thanks @OleV.V. I've missed this. It's a good reminder about the arithmetic with 'Instant' as well.Geller
@Geller "But if it is used already in the code, sometimes you just need to use what you have" … (A) I agree, use what we have. We have had java.time since Java 8, all the way through to today's Java 20. (B) The old legacy classes were given now conversion methods to easily convert back and forth between legacy and modern. So there is no reason to write new code with the old classes. Use java.time only.Anastasia
@BasilBourque I had to use Date to test old classes. If old classes operate with Dates (i.e. conceptually, Instants), I could only play with Dates in the tests,Geller

© 2022 - 2024 — McMap. All rights reserved.