下面我有3种方法。第一个很简单。它只是计算总天数。但是,第二个不仅会计算天数,还会忽略传递给该方法的星期几。
我的问题是第三种方法并不总是正确的。它应该匹配第二种方法。我猜它与闰年有关,因为当它不正确时,差异通常是 +=3|4 。
附加信息
我试图以某种方式模拟 Excel 的 weekday(serial_number,[return_type])
公式。
serial_number = startDate:Date - daysOfWeekToInclude:Array<Integer>
例子
| A | B | C
+---------+----------------------------------------------------+-----------
1 | Start | =DATE(2014,9,7) | 9/7/2014
2 | End | =DATE(2025,6,13) | 6/13/2025
3 | Include | ={1,2,4,6} (Mon, Tue, Thu, & Sat) | <Disp Only>
4 | Days | =SUM(INT((WEEKDAY($B$1-{1,2,4,6},1)+$B$2-$B$1)/7)) | 2248
这里有关于此功能的更多信息:How to count / calculate the number of days between two dates in Excel?
原始图像
方法
简单地计算两个日期之间的天数。
public static int simpleDaysBetween(final LocalDate start, final LocalDate end) { return (int) ChronoUnit.DAYS.between(start, end); }
计算天数,忽略一周中的某些天,使用循环。
public static int betterDaysBetween(final LocalDate start, final LocalDate end, final List<DayOfWeek> ignore) { int count = 0; LocalDate curr = start.plusDays(0); while (curr.isBefore(end)) { if (!ignore.contains(curr.getDayOfWeek())) { count++; } curr = curr.plusDays(1); // Increment by a day. } return count; }
计算天数。再次但没有循环。
public static int bestDaysBetween(final LocalDate start, final LocalDate end, final List<DayOfWeek> ignore) { int days = simpleDaysBetween(start, end); if (days == 0) { return 0; } if (!ignore.isEmpty()) { int weeks = days / 7; int startDay = start.getDayOfWeek().getValue(); int endDay = end.getDayOfWeek().getValue(); int diff = weeks * ignore.size(); for (DayOfWeek day : ignore) { int currDay = day.getValue(); if (startDay <= currDay) { diff++; } if (endDay > currDay) { diff++; } } if (endDay > startDay) { diff -= endDay - startDay; } return days - diff; } return days; }
完整代码
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;
public class DayCounter {
public static void main(String[] args) {
final LocalDate start = LocalDate.of(2014, 9, 7);
final LocalDate end = LocalDate.of(2025, 6, 13);
List<DayOfWeek> ignore = Arrays.asList(DayOfWeek.SUNDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY);
print(start);
print(end);
System.out.println(simpleDaysBetween(start, end));
System.out.println(betterDaysBetween(start, end, ignore));
System.out.println(bestDaysBetween(start, end, ignore));
}
public static void print(LocalDate date) {
System.out.printf("%s -> %s%n", date, date.getDayOfWeek());
}
public static int simpleDaysBetween(final LocalDate start,
final LocalDate end) {
return (int) ChronoUnit.DAYS.between(start, end);
}
public static int betterDaysBetween(final LocalDate start,
final LocalDate end, final List<DayOfWeek> ignore) {
int count = 0;
LocalDate curr = start.plusDays(0);
while (curr.isBefore(end)) {
if (!ignore.contains(curr.getDayOfWeek())) {
count++;
}
curr = curr.plusDays(1); // Increment by a day.
}
return count;
}
public static int bestDaysBetween(final LocalDate start,
final LocalDate end, final List<DayOfWeek> ignore) {
int days = simpleDaysBetween(start, end);
if (days == 0) {
return 0;
}
if (!ignore.isEmpty()) {
int weeks = days / 7;
int startDay = start.getDayOfWeek().getValue();
int endDay = end.getDayOfWeek().getValue();
int diff = weeks * ignore.size();
for (DayOfWeek day : ignore) {
int currDay = day.getValue();
if (startDay <= currDay) {
diff++;
}
if (endDay > currDay) {
diff++;
}
}
if (endDay > startDay) {
diff -= endDay - startDay;
}
return days - diff;
}
return days;
}
}
最佳答案
如果我们谈论 Java 8 API,为什么不相应地使用 Java 8 功能......
static long daysBetween(LocalDate start, LocalDate end, List<DayOfWeek> ignore) {
return Stream.iterate(start, d->d.plusDays(1))
.limit(start.until(end, ChronoUnit.DAYS))
.filter(d->!ignore.contains(d.getDayOfWeek()))
.count();
}
从 Java 9 开始,我们可以使用更简单的方法
static long daysBetween(LocalDate start, LocalDate end, List<DayOfWeek> ignore) {
return start.datesUntil(end)
.filter(d->!ignore.contains(d.getDayOfWeek()))
.count();
}
不过,可能值得使用 Set
使用优于线性查找而不是 List
:
static long daysBetween(LocalDate start, LocalDate end, List<DayOfWeek> ignore) {
if(ignore.isEmpty()) return start.until(end, ChronoUnit.DAYS);
EnumSet<DayOfWeek> set = EnumSet.copyOf(ignore);
return start.datesUntil(end)
.filter(d->!ignore.contains(d.getDayOfWeek()))
.count();
}
您可以考虑将参数更改为 Set<DayOfWeek>
,因为它不仅效率更高,而且更适合实际用例。而不是 Arrays.asList(DayOfWeek.SUNDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY)
, 你可以通过 EnumSet.of(DayOfWeek.SUNDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY)
, 但您也可以使用像 EnumSet.range(DayOfWeek.MONDAY, DayOfWeek.FRIDAY)
这样的结构, 表示典型的工作日。
您可以避免整天迭代,但需要特别注意极端情况,因此需要进行彻底的测试。并且只会在非常大的范围内得到返回。为了完整起见,这是优化后的变体:
static long daysBetween(LocalDate start, LocalDate end, Set<DayOfWeek> ignore) {
long d1 = start.toEpochDay(), d2 = end.toEpochDay();
if(d1 > d2) throw new IllegalArgumentException();
if(ignore.isEmpty()) return d2 - d1;
int incompleteWeek = 0;
DayOfWeek startDoW = start.getDayOfWeek(), endDoW = end.getDayOfWeek();
if(startDoW != endDoW) {
for(int v1 = startDoW.getValue(), v2 = endDoW.getValue();
v1 != v2 && d1 < d2; v1 = v1%7+1, d1++) {
if(!ignore.contains(DayOfWeek.of(v1))) incompleteWeek++;
}
}
return incompleteWeek + (d2 - d1) * (7 - ignore.size()) / 7;
}
在这里,ignore
的性能set 的查找并不重要,因为我们最多只查找六个值,但是,强制执行 Set
,即没有重复项,允许我们使用集合的大小来计算范围内完整周中包含的天数。完整周的开始日期和(唯一的)结束日期在一周中的同一天。所以代码只需要迭代天数,直到一周的开始和结束日期匹配。
关于java - 使用 Java 8 计算两个日期之间的天数,同时忽略一周中的某些天数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25798876/