1

In my application, the date and time need to be changed frequently when the tester tests the application. Currently, we have to use the system command date-s to change the system time,But this will cause other applications on the server to be affected as well。I want to change the Date() only this application, and I don't want to change the application itself, because there are so many places where new Date is used

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Xu Yanhua
  • 31
  • 2
  • Would you be able to give some context? It sounds like you just need to use more than one date object. – Powersjd Apr 28 '20 at 00:20
  • 1
    That class (`java.util.Date`) is old, and should not be used (at all) in new code. That being said, I'm not at all certain what you're asking - the current system date and time control what `new Date()` results in, but I suspect you know that. – Elliott Frisch Apr 28 '20 at 00:23
  • The context is:@Xu Yanhua is asking for the ability to make the VM act as if it's, say, 1999, because in order to run unit tests they need control of the clock. See my answer for how one would go about it, without resorting to drastic measures like messing with your OS's system settings to update the clock at the OS level. – rzwitserloot Apr 28 '20 at 00:35
  • Does this answer your question? [Writing and testing convenience methods using Java 8 Date/Time classes](https://stackoverflow.com/questions/52956373/writing-and-testing-convenience-methods-using-java-8-date-time-classes) – Ole V.V. Apr 28 '20 at 04:17

1 Answers1

2

The java.util.Date API is very very old. It was replaced by the Calendar API which is also old and obsolete and bad, and that in turn was replaced by the java.time API - see how old j.u.Date is?

At any rate, j.u.Date just does not allow what you need here. It has no support for custom clocks.

But java.time DOES.

You can call: Instant.now(), which is the new API equivalent of new Date() (j.u.Date is epically badly named. It does not represent dates at all; it represents instants in time). There is a variant call: Instant.now(clock). This lets you pick a custom clock.

Arrange for the code to obtain the clock instance to use, for example via Dependency Injection, or have a way to set it, with an unset clock meaning you default to the system clock (Clock.systemUTC()), but you can implement your own clock instance, which can indeed do exactly what you need: Have test code 'configure' the clock to return whatever dates/times you need for the test, without requiring messing with your computer's clock at all.

So, your 3-step solution:

  1. Eliminate all usage of the old API: java.text.DateTimeFormat, java.util.Date, java.sql.Timestamp, these all need to go away. Use java.time.Instant, java.time.LocalDateTime, java.time.LocalDate, java.time.ZonedDateTime, etc instead (the new API has way more types, because time is inherently more complicated than j.u.Date thinks it is; it is one of the many flaws of that old API. Also eliminate ALL calls to System.currentTimeMillis() as well. Anything that gets 'the current time' in terms of obsolete API needs to go.

  2. Set up a dependency injection solution of some sort (write it yourself, or use off the shelf solutions like dagger, guice, spring, etc) so you can inject an instance of Clock. Use the Instant.now(clock) form to obtain the 'current' date and time, and never the args-less Instant.now().

  3. Implement a custom impl of clock and use this to set all the various points in your code that query 'current date/time' to inject this 'test clock'.

Then, voila. Test valhalla.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • thank you very much , it seems that it is time for me to change the old code. It ’s just a lot of difficulty because it ’s all over the program. – Xu Yanhua Apr 28 '20 at 01:30
  • I found an interesting thing is that System.currentTimeMillis () itself is old, but it is still used inside Clock, and then said that Clock is new – Xu Yanhua Apr 28 '20 at 02:36
  • In college I used the first public beta of Java. Quite frankly I resent the "very very" in very very old. I cannot refute it. But I resent it. And I feel very very old now. Thanks for that. Also, `java.sql.Timestamp` gets a pass (but only in the DB layer). Once they fix JDBC we can talk about retiring `Timestamp`. – Elliott Frisch Apr 28 '20 at 04:28
  • @Xu 'new API' tends to use 'old API'. System.cTM() is not 'old API' in the sense that it no longer works: It is _THE_ call to make to get the current value of the system's clock. What's 'old' about it is that we now know that time is complicated (for example, you want custom clocks for testing!), and therefore such 'direct access via a static method' is taking a shortcut you shouldn't be taking: You need an object in between (java.time.Instant) so you have a place to put nice methods, and logic such as custom clocks. – rzwitserloot Apr 28 '20 at 11:49