我必须计算到第二天凌晨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()
的不同值。
答案 0 :(得分:4)
您必须设置HOUR_OF_DAY
字段而不是HOUR
。前者并不介意AM / PM的区别,总是设定当天的绝对时间。
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
,然后编写TestCase
,JUnit
注入不同的日期。像
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)
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类。
时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜后的几分钟是新的一天,而Montréal Québec中仍然是“昨天”。
以continent/region
的格式指定proper time zone name,例如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点和下午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 ) ;
我们的最终目标是捕捉从现在到明天目标之间的经过时间。要表示未附加到时间轴的时间跨度,请使用Duration
或Period
类。
Duration d = Duration.between( now , target ) ;
调用toString
生成根据ISO 8601标准PnYnMnDTnHnMnS
格式化的字符串。 P
标记开头,T
将年 - 月 - 日部分与小时 - 分 - 秒部分分开。
PT8H32M57.264S
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和&amp; SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
更新 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