卢声远*_* Lu 48
解决方案无循环:
static long days(Date start, Date end){
//Ignore argument check
Calendar c1 = Calendar.getInstance();
c1.setTime(start);
int w1 = c1.get(Calendar.DAY_OF_WEEK);
c1.add(Calendar.DAY_OF_WEEK, -w1);
Calendar c2 = Calendar.getInstance();
c2.setTime(end);
int w2 = c2.get(Calendar.DAY_OF_WEEK);
c2.add(Calendar.DAY_OF_WEEK, -w2);
//end Saturday to start Saturday
long days = (c2.getTimeInMillis()-c1.getTimeInMillis())/(1000*60*60*24);
long daysWithoutWeekendDays = days-(days*2/7);
// Adjust days to add on (w2) and days to subtract (w1) so that Saturday
// and Sunday are not included
if (w1 == Calendar.SUNDAY && w2 != Calendar.SATURDAY) {
w1 = Calendar.MONDAY;
} else if (w1 == Calendar.SATURDAY && w2 != Calendar.SUNDAY) {
w1 = Calendar.FRIDAY;
}
if (w2 == Calendar.SUNDAY) {
w2 = Calendar.MONDAY;
} else if (w2 == Calendar.SATURDAY) {
w2 = Calendar.FRIDAY;
}
return daysWithoutWeekendDays-w1+w2;
}
Run Code Online (Sandbox Code Playgroud)
Piy*_*too 43
public static int getWorkingDaysBetweenTwoDates(Date startDate, Date endDate) {
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
int workDays = 0;
//Return 0 if start and end are the same
if (startCal.getTimeInMillis() == endCal.getTimeInMillis()) {
return 0;
}
if (startCal.getTimeInMillis() > endCal.getTimeInMillis()) {
startCal.setTime(endDate);
endCal.setTime(startDate);
}
do {
//excluding start date
startCal.add(Calendar.DAY_OF_MONTH, 1);
if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
++workDays;
}
} while (startCal.getTimeInMillis() < endCal.getTimeInMillis()); //excluding end date
return workDays;
}
Run Code Online (Sandbox Code Playgroud)
开始日期和结束日期是独家的,只计算给定日期之间的天数.开始日期和结束日期不包括在内.
Rol*_*and 14
两天之间的定义方式与周一至周五之间ChronoUnit.DAYS.between(start, end)
的4
天数相同.由于我们只对工作日感兴趣,所以我们必须减去周末,因此从周五到周二将有2
工作日(只计算endDay - startDay
和减去2
周末).1
如果您想要包含结果,则添加到结果中,即不是两天之间.
我提出两个解决方案.
第一种解决方案(5线,短线和神秘):
import java.time.*;
import java.time.temporal.*;
public static long calcWeekDays1(final LocalDate start, final LocalDate end) {
final DayOfWeek startW = start.getDayOfWeek();
final DayOfWeek endW = end.getDayOfWeek();
final long days = ChronoUnit.DAYS.between(start, end);
final long daysWithoutWeekends = days - 2 * ((days + startW.getValue())/7);
//adjust for starting and ending on a Sunday:
return daysWithoutWeekends + (startW == DayOfWeek.SUNDAY ? 1 : 0) + (endW == DayOfWeek.SUNDAY ? 1 : 0);
}
Run Code Online (Sandbox Code Playgroud)
二解决方案:
public static long calcWeekDays2(final LocalDate start, final LocalDate end) {
final int startW = start.getDayOfWeek().getValue();
final int endW = end.getDayOfWeek().getValue();
final long days = ChronoUnit.DAYS.between(start, end);
long result = days - 2*(days/7); //remove weekends
if (days % 7 != 0) { //deal with the rest days
if (startW == 7) {
result -= 1;
} else if (endW == 7) { //they can't both be Sunday, otherwise rest would be zero
result -= 1;
} else if (endW < startW) { //another weekend is included
result -= 2;
}
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
Lev*_*evi 11
我使用了盛源路的解决方案,但我需要修正一个方法被调用的情况,其中一个日期是星期六而另一个是星期日 - 否则答案是一天关闭:
static long days(Date start, Date end){
//Ignore argument check
Calendar c1 = GregorianCalendar.getInstance();
c1.setTime(start);
int w1 = c1.get(Calendar.DAY_OF_WEEK);
c1.add(Calendar.DAY_OF_WEEK, -w1 + 1);
Calendar c2 = GregorianCalendar.getInstance();
c2.setTime(end);
int w2 = c2.get(Calendar.DAY_OF_WEEK);
c2.add(Calendar.DAY_OF_WEEK, -w2 + 1);
//end Saturday to start Saturday
long days = (c2.getTimeInMillis()-c1.getTimeInMillis())/(1000*60*60*24);
long daysWithoutSunday = days-(days*2/7);
if (w1 == Calendar.SUNDAY) {
w1 = Calendar.MONDAY;
}
if (w2 == Calendar.SUNDAY) {
w2 = Calendar.MONDAY;
}
return daysWithoutSunday-w1+w2;
}
Run Code Online (Sandbox Code Playgroud)
现代方法是使用java.time类.
LocalDate
该LocalDate
级表示没有时间一天和不同时区的日期,唯一的价值.
LocalDate start = LocalDate.of( 2016 , 1 , 23 );
LocalDate stop = start.plusMonths( 1 );
Run Code Online (Sandbox Code Playgroud)
DayOfWeek
枚举该枚举提供了一个单为每个星期的服务器天实例.DayOfWeek
DayOfWeek dow = start.getDayOfWeek();
if( dow.equals( DayOfWeek.SATURDAY ) || dow.equals( DayOfWeek.SUNDAY ) ) …
Run Code Online (Sandbox Code Playgroud)
我们可以收集所需的日期是一个List
.
int initialCapacity = Duration.between( start , stop ).toDays() ;
List<LocalDate> dates = new ArrayList<>( initialCapacity );
…
if( dow.equals( DayOfWeek.SATURDAY ) || dow.equals( DayOfWeek.SUNDAY ) ) {
dates.add( date );
…
Run Code Online (Sandbox Code Playgroud)
An EnumSet
是一种非常高效,快速且低内存的实现方式Set
.我们可以用一个EnumSet
而不是if
上面的陈述.
Set<DayOfWeek> weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;
…
if( weekend.contains( dayOfWeek ) ) …
Run Code Online (Sandbox Code Playgroud)
把它们放在一起.
LocalDate date = start ;
while( date.isBefore( stop ) ) {
if( ! weekend.contains( date.getDayOfWeek() ) ) { // If not weekend, collect this LocalDate.
dates.add( date ) ;
}
// Prepare for next loop.
date = date.plusDays( 1 ); // Increment to next day.
}
Run Code Online (Sandbox Code Playgroud)
nextWorkingDay
TemporalAdjuster另一种方法是使用ThreeTen-Extra项目添加适用于java.time的类.
的Temporals
类添加的另外的实施方式TemporalAdjuster
用于处理日期时间值.我们希望nextWorkingDay
调整器在周六和周日跳过时增加日期.
LocalDate start = LocalDate.of( 2016 , 1 , 23 );
LocalDate stop = start.plusMonths( 1 );
int initialCapacity = Duration.between( start , stop ).toDays() ;
List<LocalDate> dates = new ArrayList<>( initialCapacity );
LocalDate date = start.minusDays( 1 ); // Start a day ahead.
while( date.isBefore( stop ) ) {
date = date.with( org.threeten.extra.Temporals.nextWorkingDay() );
// Double-check ending date as the `nextWorkingDay` adjuster could move us past the stop date.
if( date.isBefore( stop ) ) {
dates.add( date ) ;
}
}
Run Code Online (Sandbox Code Playgroud)
我对本页各种答案中各种方法的表现感到好奇.我只考虑现代java.time代码,而不是使用麻烦的遗留Date
/ Calendar
类的代码.
以下是四种方法,每种方法都返回经过的天数.
private long countWeekDaysMath ( LocalDate start , LocalDate stop ) {
// Code taken from Answer by Roland.
// https://stackoverflow.com/a/44942039/642706
long count = 0;
final DayOfWeek startW = start.getDayOfWeek();
final DayOfWeek stopW = stop.getDayOfWeek();
final long days = ChronoUnit.DAYS.between( start , stop );
final long daysWithoutWeekends = days - 2 * ( ( days + startW.getValue() ) / 7 );
//adjust for starting and ending on a Sunday:
count = daysWithoutWeekends + ( startW == DayOfWeek.SUNDAY ? 1 : 0 ) + ( stopW == DayOfWeek.SUNDAY ? 1 : 0 );
return count;
}
Run Code Online (Sandbox Code Playgroud)
我在本答案中看到的两种使用方法:(a)访问每个日期,在传统循环中逐个递增.
private long countWeekDaysVisit ( LocalDate start , LocalDate stop ) {
// Code taken from Answer by Basil Bourque.
// https://stackoverflow.com/a/40369140/642706
long count = 0;
Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
LocalDate ld = start;
while ( ld.isBefore( stop ) ) {
if ( ! weekend.contains( ld.getDayOfWeek() ) ) { // If not weekend, collect this LocalDate.
count++;
}
// Prepare for next loop.
ld = ld.plusDays( 1 ); // Increment to next day.
}
return count;
}
Run Code Online (Sandbox Code Playgroud)
......和,(b)使用TemporalAdjuster
实施org.threeten.extra.Temporals.nextWorkingDay()
.
private long countWeekDaysAdjuster ( LocalDate start , LocalDate stop ) {
// Code taken from Answer by Basil Bourque.
// https://stackoverflow.com/a/40369140/642706
long count = 0;
Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
TemporalAdjuster nextWorkingDayTA = org.threeten.extra.Temporals.nextWorkingDay();
LocalDate ld = start;
if ( weekend.contains( ld.getDayOfWeek() ) ) {
ld = ld.with( nextWorkingDayTA );
}
while ( ld.isBefore( stop ) ) {
count++;
// Prepare for next loop.
ld = ld.with( nextWorkingDayTA ); // Increment to next working day (non-weekend day).
}
return count;
}
Run Code Online (Sandbox Code Playgroud)
最后一个使用Java Streams方法在Ravindra Ranwala的答案中看到.
private long countWeekDaysStream ( LocalDate start , LocalDate stop ) {
// Code taken from the Answer by Ravindra Ranwala.
// https://stackoverflow.com/a/51010738/642706
long count = 0;
Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
final long weekDaysBetween = start.datesUntil( stop )
.filter( d -> ! weekend.contains( d.getDayOfWeek() ) )
.count();
return count;
}
Run Code Online (Sandbox Code Playgroud)
和测试工具.
注意事项:
TemporalAdjuster
可以在我们的方法之外缓存.测试线束.
LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 1 );
LocalDate stop = start.plusYears( 1 );
int runs = 100_000;
long go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
long count = this.countWeekDaysMath( start , stop );
}
long elapsedMath = ( System.nanoTime() - go );
go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
long count = this.countWeekDaysVisit( start , stop );
}
long elapsedVisit = ( System.nanoTime() - go );
go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
long count = this.countWeekDaysStream( start , stop );
}
long elapsedAdjuster = ( System.nanoTime() - go );
go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
long count = this.countWeekDaysStream( start , stop );
}
long elapsedStream = ( System.nanoTime() - go );
System.out.println( "math: " + elapsedMath + " each: " + ( elapsedMath / runs ) );
System.out.println( "visit: " + elapsedVisit + " each: " + ( elapsedVisit / runs ) );
System.out.println( "adjuster: " + elapsedAdjuster + " each: " + ( elapsedAdjuster / runs ) );
System.out.println( "stream: " + elapsedStream + " each: " + ( elapsedStream / runs ) );
Run Code Online (Sandbox Code Playgroud)
当使用Oracle JDK 10.0.1和ThreeTen-Extra版本1.3.2 在我的MacBook Pro(Sierra)上运行时,我得到的结果始终接近以下内容.正如我们所期望的那样,数学解决方案只有几百纳米,而不是几千纳米的数学解决方案.在其他三个中,它TemporalAdjuster
是最长的,每个超过10,000纳米.访问量和流量均低于10,000纳米,访问量明显快于流量.正如围绕互联网的其他示例所示,Java Streams通常会生成漂亮的短代码,同时运行时间要长得多,在这种情况下长约20%.
数学:18313309每人:183
访问量:708420626每个:7084
理算员:1002157240每人:10021
流:924724750每个:9247
该java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date
,Calendar
,和SimpleDateFormat
.
现在处于维护模式的Joda-Time项目建议迁移到java.time.
要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.
从哪里获取java.time类?
该ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval
,YearWeek
,YearQuarter
,和更多.
几乎所有的解决方案都已经过时和叙述。然而,这里有一个非常简洁易读的解决方案。
此方法使用Java 9 及更高版本中内置的方法提供的Java StreamLocalDate::datesUntil
。
LocalDate startDate = LocalDate.of(2018, 5, 2);
LocalDate endDate = LocalDate.now();
Set<DayOfWeek> weekend = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
final long weekDaysBetween = startDate.datesUntil(endDate)
.filter(d -> !weekend.contains(d.getDayOfWeek()))
.count();
Run Code Online (Sandbox Code Playgroud)
.datesUntil
返回一个顺序有序的日期流。返回的流从该日期(含)开始,以 1 天的增量步长进入 endExclusive(独占)。
然后过滤掉所有周六和周日。最后一步是获取剩余工作日的计数。
Java-9 已于一年前发布,因为现在使用它对我来说似乎是合理的。
归档时间: |
|
查看次数: |
64762 次 |
最近记录: |