[英]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));
}
}
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类。
时区对于确定日期至关重要。 在任何给定时刻,日期都会在全球范围内变化。 例如, 法国巴黎午夜过后几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天”。
以continent/region
的格式指定正确的时区名称 ,例如America/Montreal
, Africa/Casablanca
或Pacific/Auckland
。 切勿使用EST
或IST
等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 ) ;
我们的最终目标是捕获从现在到明天目标之间经过的时间。 要表示未附加到时间轴的时间跨度,请使用Duration
或Period
类。
Duration d = Duration.between( now , target ) ;
调用toString
以生成根据ISO 8601标准PnYnMnDTnHnMnS
格式化的String。 P
表示开始, T
则将年-月-日-天部分与小时-分-秒部分分开。
PT8H32M57.264S
java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧的旧式日期时间类,例如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
,和更多 。
更新: Joda-Time项目现在处于维护模式 ,团队建议迁移到java.time类。 请参见Oracle教程 。 本节保留完整的历史记录。
下面是一些使用Joda-Time 2.3库的示例代码。
DateTime
类知道其自己的时区。
withTime
方法withTime
创建一个从现有实例复制的新(不可变)实例,但是具有指定的时间。
Joda-Time有一些类别可以表示一个时间范围: Period ,Duration,Interval。 默认情况下,Period类根据PnYnMnDTnHnMnS
的ISO 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.