Setting future date in Java 8

2019-02-28 09:24发布

问题:

In Joda we have setCurrentMillisFixed method which can be used to set future date In one my test cases , i pass a DateTime (joda) parameter to current system time: DateTime created = new DateTime() DateTimeUtils.setCurrentMillisSystem(created.toInstant().toEpochMilli()););

In Java 8 i am trying : I pass ZonedDateTime instead of DateTime,

I tried setting the date as below:

ZonedDateTime.now(Clock.fixed(Instant.now().plusMillis(created.toInstant().toEpochMilli()),ZoneId.systemDefault()));

But lot of test cases are failing, i am guessing this has something to do with how the date is being set.

Any advice ? Could not find any useful piece of code ,

回答1:

Year 2066?

I suspect you are setting an unintended moment, around 50 years in the future.

Let’s unpack your code.

ZonedDateTime.now(                    // Represents a moment adjusted into a time zone. 
    Clock.fixed(
        Instant.now()                 // Get current moment. Actually a count of nanoseconds since 1970-01-01T00:000Z. That is 48 years, about 5 decades worth of nanoseconds.
               .plusMillis(           // Adding some more milliseconds, for a moment in the future, later than now.   
            created.toInstant()       // Accessing some stored moment. Presumably a contemporary date-time.
                   .toEpochMilli()) , // Getting the count of milliseconds since 1970-01-01T00:000Z, another 5 decades or so of time.
            ZoneId.systemDefault()    // Getting the JVM’s current default time zone. To be assigned to our resulting `ZonedDateTime` object.
        )
)

So you start with about 5 decades of nanoseconds. Then, presumably, you add approximately another 5 decades of milliseconds. So you end up with a date fifty years in the future, around the year 2066. Is that the value you intended to test?

2066 ≈ 2018 + ( time elapsed from 1970 to some moment around 2018 )

We can try that code, so see the result.

ZonedDateTime created = ZonedDateTime.of( 2018 , 1 , 23 , 12 , 34 , 56, 0 , ZoneId.systemDefault()); // 2018-01-23T12:34:56 in America/Los_Angeles.

ZonedDateTime zdt =     ZonedDateTime.now(                    // Represents a moment adjusted into a time zone.
    Clock.fixed(
        Instant.now()                 // Get current moment. Actually a count of nanoseconds since 1970-01-01T00:000Z. That is 48 years, about 5 decades worth of nanoseconds.
            .plusMillis(           // Adding some more milliseconds, for a moment in the future, later than now.
                created.toInstant()       // Accessing some stored moment. Presumably a contemporary date-time.
                    .toEpochMilli()) , // Getting the count of milliseconds since 1970-01-01T00:000Z, another 5 decades or so of time.
        ZoneId.systemDefault()    // Getting the JVM’s current default time zone. To be assigned to our resulting `ZonedDateTime` object.
    )
);

System.out.println("created.toString: " + created );
System.out.println("zdt.toString(): " + zdt );

Sure enough, year 2066, if created is a moment earlier this year.

created.toString: 2018-01-23T12:34:56-08:00[America/Los_Angeles]

zdt.toString(): 2066-08-09T08:51:38.488980-07:00[America/Los_Angeles]

Millis vs nanos

Be aware that the java.time classes such as Instant and ZonedDateTime have a resolution of nanoseconds. That is much finer than the old legacy classes java.util.Date & Calendar which use milliseconds.

Write simple code

Tip: Don't write so much into one line of code. Hard for humans to decipher and to debug/trace. And may be harder for the JVM to optimize. Writing dead-simple lines of code is easier on both humans and machines, generally.

So, let’s break up that code into simple lines.

Instant now = Instant.now();  // Current moment in UTC, with a resolution of nanoseconds.
Duration fiveMinutes = Duration.ofMinutes( 5L );  // `Duration` represents a span-of-time unattached to the timeline.
Instant fiveMinutesFromNow = now.plus( fiveMinutes );  // Returns a `Instant`, using our original `Instant` object plus adding some span-of-time.
Clock clock = Clock.fixed( fiveMinutesFromNow , ZoneId.systemDefault() );  // Instantiate a clock frozen at a specific moment about five minutes in the future. This clock does not “tick”, remaining fixed at this one specified moment.

Now pass that Clock object named clock to your code being tested.

…
ZonedDateTime zdt = ZonedDateTime.now( clock ) ;  // Tells a lie. Reports a moment about five minutes in the future as “now”. Good for testing.
…

Try it.

now.toString(): 2018-07-17T19:22:48.670320Z

fiveMinutesFromNow.toString(): 2018-07-17T19:27:48.670320Z

zdt.toString(): 2018-07-17T12:27:48.670320-07:00[America/Los_Angeles]

Yes, it worked. We see the minute-of-hour as 27 rather than 22, five minutes in the future.

Pass a Clock in production.

To run that same code in production, outside your test harness, pass the default Clock implementation.

Clock clock =  Clock.systemDefaultZone() ;  // Default `Clock` used implicitly in Java if you do not say otherwise.

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?

  • Java SE 8, Java SE 9, Java SE 10, and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

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.