繁体   English   中英

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

[英]How to test method with Calendar.getInstance()

我必须计算直到第二天凌晨2:00的时间

以下代码似乎正常工作

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()不同值。

您必须设置HOUR_OF_DAY字段而不是HOUR 前者不介意AM / PM的区别,而是始终设置一天中的绝对小时。

引用文件

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

HOUR_OF_DAY用于24小时制。

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

//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();
}

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

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

例如:

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
}

如果您正在寻找一种测试方法,建议将方法签名更改为now接受,然后编写一个带有JUnit注入不同日期的TestCase 喜欢

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));
    }
}

tl; dr

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类。

时区对于确定日期至关重要。 在任何给定时刻,日期都会在全球范围内变化。 例如, 法国巴黎午夜过后几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天”。

continent/region的格式指定正确的时区名称 ,例如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 AM vs 2 PM问题。 2 PM值将输入14小时,因此没有歧义。

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

警告:如果您碰巧选择了无效的特定日期和时间,例如夏令时(DST)转换,则ZonedDateTime类会进行调整以适合您的情况。 阅读课程文档,以了解其调整算法是否符合您的需求/期望。 例如,在夏令时的“春季向前”日期,您将以凌晨3点而不是凌晨2点结束,因为该日期不存在02:00小时,仅存在01:00和03:00小时。 不会影响您的持续时间计算。 时空连续体并没有跳跃,只是我们在墙上的时钟表示跳跃了。

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

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

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

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

调用toString以生成根据ISO 8601标准PnYnMnDTnHnMnS格式化的String。 P表示开始, T则将年-月-日-天部分与小时-分-秒部分分开。

PT8H32M57.264S


关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧的旧式日期时间类,例如java.util.DateCalendarSimpleDateFormat

现在处于维护模式Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参见Oracle教程 并在Stack Overflow中搜索许多示例和说明。 规格为JSR 310

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目为将来可能在java.time中添加内容提供了一个试验场。 您可以在这里找到一些有用的类,比如IntervalYearWeekYearQuarter ,和更多


乔达时代

更新: Joda-Time项目现在处于维护模式 ,团队建议迁移到java.time类。 请参见Oracle教程 本节保留完整的历史记录。

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

DateTime类知道其自己的时区。

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

Joda-Time有一些类别可以表示一个时间范围: Period ,Duration,Interval。 默认情况下,Period类根据PnYnMnDTnHnMnSISO 8601 持续时间格式呈现字符串。 您要求毫秒,因此我在示例中进行了计算。 有关更多详细信息请参见此问题, 乔达时间,毫秒数。

范例程式码

// © 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM