如何使用Calendar.getInstance()测试方法

时间:2014-01-10 11:03:04

标签: java testing

我必须计算到第二天凌晨2点的时间

以下代码似乎工作正常

public static long millisTillNextDay() {
    Date now = new Date();
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + 1);
    cal.set(Calendar.HOUR, 3);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    return cal.getTimeInMillis() - now.getTime();
}

然而,几天前碰巧这个方法计算的时间直到02:00 PM ,而我不知道为什么。因此,我想创建一些测试场景来模拟Calendar.getInstance()的不同值。

5 个答案:

答案 0 :(得分:4)

您必须设置HOUR_OF_DAY字段而不是HOUR。前者并不介意AM / PM的区别,总是设定当天的绝对时间。

来自documentation

的引言
  

HOUR用于12小时制(0 - 11)。

     

HOUR_OF_DAY用于24小时制。

答案 1 :(得分:3)

出于测试目的,您可以将方法重构为:

//the original method
public static long millisTillNextDay() {
    return millisTillNextDay(new Date());
}

//a new method that is testable
public static long millisTillNextDay(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + 1);
    cal.set(Calendar.HOUR, 3);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    return cal.getTimeInMillis() - date.getTime();
}

现在可以轻松地测试第二种方法并传入您喜欢的任何日期。

答案 2 :(得分:1)

只需使用Calendar中的一种方法即可指定时间。

例如:

long l = System.currentTimeMillis();
long limit = l + 24*60*60*1000;

for (long l = System.currentTimeMillis();l<=limit;l+=60*1000) {
    Calendar cal = new Calendar();
    cal.setTimeInMillis(l);
    // Do test
}

答案 3 :(得分:1)

如果您正在寻找测试方法,我建议您更改方法签名以接受now,然后编写TestCaseJUnit注入不同的日期。像

public void MillisTillNextDayTest extends TestCase {
    public void testNewYearEveMidnight() {
        Date midnight = new Date(2014, 0, 1, 0, 0, 0);
        assertEquals(2*3600*1000, MyUtils.millisTillNextDay(midnight));
    }

    public void test3am() {
        Date now = new Date(2014, 0, 1, 3, 0, 0);
        assertEquals(23*3600*1000, MyUtils.millisTillNextDay(now));
    }

    public void test1pm() {
        Date now = new Date(2014, 0, 1, 13, 0, 0);
        assertEquals(13*3600*1000, MyUtils.millisTillNextDay(now));
    }

    public void testLeapYear() {
        Date now = new Date(2012, 1, 28, 13, 0, 0);
        assertEquals(13*3600*1000, MyUtils.millisTillNextDay(now));
    }
}

答案 4 :(得分:1)

TL;博士

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;
Duration d = Duration.between( 
    now , 
    ZonedDateTime.of( 
        now.toLocalDate().plusDays(1) , 
        LocalTime.of( 2 , 0 )
        z 
    ) ;
).toString()
  

PT8H32M57.264S

时区

其他答案是正确的,除了一个大缺陷:时区。显示的所有代码都默认为JVM的当前默认时区。将代码部署到另一台计算机时,可能会出现意外行为。夏令时(DST)或与时区相关的其他异常可能会改变计算。

java.time

现代方法使用java.time类。

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜后的几分钟是新的一天,而Montréal Québec中仍然是“昨天”。

continent/region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now( z );

我们需要在明天凌晨2点建立目标日期时间。明天的日期为LocalDate(仅限日期的值)加上目标时间LocalTime(仅限时间的值)。默认情况下,java.time类使用24小时制,因此我们不必担心您的上午2点和下午2点的问题。下午2点的值将输入14小时,因此没有歧义。

LocalDate tomorrow = now.toLocalDate().plusDays( 1 ) ;
LocalTime targetTime = LocalTime.of( 2 , 0 ) ;  // 2 hours, zero minutes.

警告:如果您碰巧选择了无效的特定日期和时间,例如夏令时(DST)切换,则ZonedDateTime类会调整为适合。阅读课程文档,了解其调整算法是否符合您的需求/期望。例如,在DST&#34; Spring-forward&#34;日期,您将在凌晨3点而不是凌晨2点结束,因为该日期不存在02:00小时,仅为01:00&amp; 03:00时存在。这将影响您的持续时间计算。 space-time continuum没有跳跃,只是我们的墙上时钟代表跳了起来。

将它们与时区放在一起,明天凌晨2点。

ZonedDateTime target = ZonedDateTime.of( tomorrow , targetTime , z ) ;

我们的最终目标是捕捉从现在到明天目标之间的经过时间。要表示未附加到时间轴的时间跨度,请使用DurationPeriod类。

Duration d = Duration.between( now , target ) ;

调用toString生成根据ISO 8601标准PnYnMnDTnHnMnS格式化的字符串。 P标记开头,T将年 - 月 - 日部分与小时 - 分 - 秒部分分开。

  

PT8H32M57.264S


关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore


约达时间

更新 Joda-Time项目现在位于maintenance mode,团队建议迁移到java.time课程。见Tutorial by Oracle。这部分保留完整的历史记录。

以下是使用Joda-Time 2.3库的一些示例代码。

DateTime类知道自己的时区。

方法withTime创建一个从现有实例复制的新(不可变)实例,但具有指定的时间。

Joda-Time有几个类来表示一段时间:Period,持续时间,间隔。默认情况下,Period类根据PnYnMnDTnHnMnS的{​​{3}} ISO 8601呈现字符串。你问了几毫秒,所以我在这个例子中计算了。有关更多详细信息,请参阅此问题Duration format

示例代码

// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;

// Better to specify a time zone explicitly rather than rely on default.
// Time Zone list… http://joda-time.sourceforge.net/timezones.html  (not quite up-to-date, read page for details)
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );

DateTime now = new DateTime( timeZone );
DateTime tomorrowTwoInMorning = now.plusDays( 1 ).withTime( 2, 0, 0, 0 );

// Calculate the span of time between them.
Period period = new Period( now, tomorrowTwoInMorning );
long milliseconds = period.toStandardDuration().getMillis();

转储到控制台...

System.out.println( "now: " + now );
System.out.println( "tomorrowTwoInMorning: " + tomorrowTwoInMorning );
System.out.println( "period: " + period );
System.out.println( "milliseconds: " + milliseconds );

跑步时......

now: 2014-01-11T00:51:38.059+01:00
tomorrowTwoInMorning: 2014-01-12T02:00:00.000+01:00
period: P1DT1H8M21.941S
milliseconds: 90501941