Parker提供的答案似乎是正确且良好的。
使用java.time
该问题使用了过时的麻烦日期时间类,这些类现在已被java.time类取代。下面是相同类型的代码以及Parker的解决方案,使用java.time进行重写。
Instant
首先,如果必须使用java.util.Date
对象,请将其转换为/from Instant
。Instant
类表示UTC上的时间轴上的一个时刻,分辨率为纳秒(最多九(9)位小数)。要进行转换,请查看添加到旧类中的新方法。
Instant instant = myJavaUtilDate.toInstant();
java.util.Date myJavaUtilDate = java.util.Date.from( instant ) ;
让我们重写方法签名,但传递和返回
Instant
对象。
public Instant getRandomDate( Instant early , Instant late) {
验证
early
参数确实早于
later
参数。或者断言下面看到的
Duration
不是负数(
!duration.isNegative()
)。
if( early.isAfter( late) ) { … } // Assert `early` is not after `late`.
半开
计算最早和最晚时刻之间的差值。这是使用半开方法定义时间跨度时经常使用的方式,其中开始时间是包括在内的,结束时间是不包括在内的。
持续时间
持续时间
类表示这样一段时间,以秒为单位的总数加上纳秒为单位的分数。
Duration duration = Duration.between( early , late ) ;
进行随机数计算时,我们需要一个整数。为了处理纳秒级的分辨率,我们需要一个64位的
long
而不是32位的
int
。
ThreadLocalRandom
提示:如果在多个线程中生成这些值,请使用
ThreadLocalRandom
类。引用文档:
在并发程序中,如果适用,使用ThreadLocalRandom而不是共享的Random对象通常会遇到更少的开销和争用。
我们可以通过调用
ThreadLocalRandom::nextLong(origin, bound)
以半开放风格指定范围,其中起点是
包含的,边界是
排除的。
long bound = duration.toNanos() ;
long nanos1 = ThreadLocalRandom.current().nextLong( 0 , bound );
long nanos2 = ThreadLocalRandom.current().nextLong( 0 , bound );
long nanos = Math.min( nanos1 , nanos2 ); // Select the lesser number.
Instant instant = early.plusNanos( nanos );
return instant ;
}
实例
请查看以下代码在IdeOne.com上的实时运行。
我们提取生成的每个日期(仅限LocalDate
)的日期时间值数量,以便以一种随意的方式调查结果,以验证我们所需的结果是否偏向较早的日期。
测试工具展示了如何将时区(ZoneId
)分配给Instant
以获得一个ZonedDateTime
对象,并从中提取LocalDate
。如果您希望通过某个特定地区的挂钟时间而不是UTC来查看Instant
对象,请使用该指南。
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.concurrent.ThreadLocalRandom ;
import java.util.TreeMap ;
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Ideone app = new Ideone();
app.doIt();
}
public void doIt() {
ZoneId z = ZoneId.of( "America/Montreal" ) ;
int count = 10 ;
LocalDate today = LocalDate.now( z );
LocalDate laterDate = today.plusDays( count );
Instant start = today.atStartOfDay( z ).toInstant();
Instant stop = laterDate.atStartOfDay( z ).toInstant();
List<LocalDate> dates = new ArrayList<>( count );
Map<LocalDate , Integer > map = new TreeMap<LocalDate , Integer >();
for( int i = 0 ; i <= count ; i ++ ) {
LocalDate localDate = today.plusDays( i ) ;
dates.add( localDate );
map.put( localDate , new Integer( 0 ) );
}
for( int i = 1 ; i <= 10_000 ; i ++ ) {
Instant instant = this.getRandomInstantBetween( start , stop );
LocalDate localDate = instant.atZone( z ).toLocalDate();
Integer integer = map.get( localDate );
map.put( localDate , integer + 1);
}
System.out.println( map );
}
public Instant getRandomInstantBetween( Instant early , Instant late) {
Duration duration = Duration.between( early , late ) ;
long bound = duration.toNanos() ;
ThreadLocalRandom random = ThreadLocalRandom.current() ;
long nanos1 = random.nextLong( 0 , bound );
long nanos2 = random.nextLong( 0 , bound );
long nanos = Math.min( nanos1 , nanos2 );
Instant instant = early.plusNanos( nanos );
return instant;
}
}
这里是一些样本结果。我认为它们看起来不错,但我不是统计学家。使用需自担风险。
{2017-02-24=1853,2017-02-25=1697,2017-02-26=1548,2017-02-27=1255,2017-02-28=1130,2017-03-01=926,2017-03-02=706,2017-03-03=485,2017-03-04=299,2017-03-05=101,2017-03-06=0}
{2017-02-25=930,2017-02-26=799,2017-02-27=760,2017-02-28=657,2017-03-01=589,2017-03-02=470,2017-03-03=342,2017-03-04=241,2017-03-05=163,2017-03-06=49,2017-03-07=0}
{2017-02-25=878,2017-02-26=875,2017-02-27=786,2017-02-28=676,2017-03-01=558,2017-03-02=440,2017-03-03=370,2017-03-04=236,2017-03-05=140,2017-03-06=41,2017-03-07=0}
关于java.time
Java 8及其后续版本内置了java.time框架。这些类取代了旧的、麻烦的遗留日期时间类,如java.util.Date
、Calendar
和SimpleDateFormat
。
Joda-Time项目现在处于维护模式,建议迁移到java.time类。
要了解更多,请查看
Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是
JSR 310。
如何获取java.time类?
该
ThreeTen-Extra项目扩展了java.time的附加类。该项目是java.time可能未来添加的一个试验场。您可能会在这里找到一些有用的类,例如
Interval
,
YearWeek
,
YearQuarter
以及
更多。
long
构造Date
对象(https://docs.oracle.com/javase/7/docs/api/java/sql/Date.html#Date(long))。不需要使用Calendar类。例如:`return new Date(early.getTime()+randVal);` - rustyx