Java中不包括周末的日期差异计算



我是Java平台的新手。我有以下3个简单的Java日期差计算函数。我想在所有3种方法中排除周末的以下计算。有人能帮助如何排除周末进行以下日期差异计算吗?

public static String getDatesDiff(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
timeDiff = ""+diff;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}
public static String getDatesDiffAsDays(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
String days = ""+(diff / (24 * 60 * 60 * 1000));

timeDiff = days;
timeDiff = timeDiff.replaceAll("-", "");
timeDiff = timeDiff+" days";

} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}
public static String getDatesDiffAsDate(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
String days = (diff / (24 * 60 * 60 * 1000))+" days";
String hours = (diff / (60 * 60 * 1000) % 24)+"h";
String minutes = (diff / 1000 % 60)+"mts";
String seconds = (diff / (60 * 1000) % 60)+"sec";

timeDiff = days;
timeDiff = timeDiff.replaceAll("-", "");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}

此代码已从根本上被破坏。java.util.Date不代表日期,它代表时间戳。但是,如果你工作的时间很短,你就会遇到一个问题:并非所有的日子都是24小时。例如,夏令时是存在的,有些日子是25或23小时。在地球上特定地点的特定时刻,整整一天都被跳过,比如一个地方切换到国际日期线的哪一边,或者俄罗斯是最后一个从朱利安切换到格里高利的国家(著名的十月革命?是的,那实际上发生在11月!)

使用LocalDate,它表示实际日期,而不是时间戳。不要使用DateSimpleDateFormat——这些都是过时的,而且大多是对日期和时间的破坏。java.time软件包经过了适当的考虑。

"周末"是什么时候?在一些地方,周五和周六被视为周末,而不是周六和周日。

如果你把周末排除在外,大概你也想把法定假日排除在外。许多国家表示,1月1日,无论是哪一天,都算作周日,例如,出于政府建筑和服务开放与否的目的。

你需要从中吸取的教训:

  • 约会非常复杂,因此,对于教授基本原则来说,这是一个可怕的想法
  • 永远不要使用java.util.DateCalendarGregorianCalendarSimpleDateFormat。使用java.time中的内容
  • 如果你这样写数学,你可能做错了——例如ChronoUnit.DAYS.between(date1, date2)为你做了所有的数学
  • 您可能应该从开始日期开始,然后开始循环:检查该日期是否算作工作日(如果是,则增加计数器),然后转到第二天。继续,直到当天等于结束日期,然后返回计数器。是的,这是"慢"的,但一台电脑会在你的心跳中愉快地度过200万天(相当于5000多年的时间)。优点是,你可以计算一天是否算作"工作日"(这可能会变得非常复杂。例如,大多数欧洲大陆国家和我认为美国也规定复活节是公共假日。去查一下,如何知道复活节是什么时候。不过,先煮点咖啡)
  • 如果你真的坚持公式化,把周末定义为周六和周日,最好分别计算两个日期之间的完整周数,然后乘以5,然后分别加上"范围前面"的半周和后面的半周。即使你假设的范围是一百万年,这也会很快
  • 这不是处理异常的方式。如果你现在不想处理它,请添加throws X,或者,将throw new RuntimeException("unhandled", e);放在你的捕获块中。不是这个,这太可怕了。它记录了一半的错误,并盲目地继续进行,状态无效
  • 几乎所有有趣的问题,比如"这次约会是度假吗?"在不知道自己所在的文化/地区的情况下是不可回答的。这包括看似显而易见的常量,例如"周六是周末吗?">

rzwitserloot已经提出了许多关于代码中问题的有效观点。

这是一个如何计算工作日的例子:

LocalDate startDate = ...;
LocalDate endDateExclusive = ...;
long days = startDate.datesUntil(endDateExclusive)
.filter(date -> isWorkingDay(date))
.count();

当然,您还需要实现isWorkingDay方法。一个例子是:

public static boolean isWorkingDay(LocalDate date) {
DayOfWeek dow = date.getDayOfWeek();
return (dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY);
}
  • 我使用LocalDate来举例说明。如果你正在处理周末和节假日之类的概念,LocalDate非常适合。然而,如果你想同时包含时间成分,那么你也应该考虑像DST这样的时钟调整;否则为";差异";没有道理。

  • 我假设用户输入一个表示某个日期时间值的对象,而不是String。字符串的解析不属于此方法,但应该在其他地方处理。

  • 已经说过了,但我重复一遍:不要使用DateCalendarSimpleDateFormat。它们很麻烦。以下是一些原因。


如果你想考虑时间,它会变得有点复杂。例如,ChronoUnit.DAYS.between(date1, date2)仅支持单个连续的时间跨度。时间跨度上的差距,比如排除某些时间段,并不是。然后,你必须遍历每个日期,并获得该日期部分的相关持续时间。

  • 首先,我们可以创建一个LocalTimeRange类,它表示某一天的时间跨度。

    public record LocalTimeRange(LocalTime start, LocalTime endExclusive) {
    public static final LocalTimeRange EMPTY = new LocalTimeRange(null, null);
    public Duration toDuration(LocalDate date, ZoneId zone) {
    if (this.equals(EMPTY)) {
    return Duration.ZERO;
    }
    var s = ZonedDateTime.of(date, Objects.requireNonNullElse(start, LocalTime.MIN), zone);
    var e = (endExclusive != null ? ZonedDateTime.of(date, endExclusive, zone) : ZonedDateTime.of(date.plusDays(1), LocalTime.MIN, zone));
    return Duration.between(s, e);
    }
    }
    

    计算不会立即完成,因为两个挂钟时间之间的持续时间取决于日期和时区。toDuration方法对此进行了计算。

  • 然后,我们将创建一个方法,定义每天的哪些时间算作非周末。在本例中,我将周末定义为从星期五12:00(中午)到星期日23:59(午夜)。

    private static Duration nonWeekendHours(LocalDate date, ZoneId zone) {
    var result = switch (date.getDayOfWeek()) {
    case MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY   -> new LocalTimeRange(LocalTime.MIDNIGHT, null);
    case FRIDAY     -> new LocalTimeRange(LocalTime.MIDNIGHT, LocalTime.NOON);
    case SATURDAY,
    SUNDAY     -> new LocalTimeRange(null, null);
    };
    return result.toDuration(date, zone);
    }
    

    使用传递的LocalDateZoneId参数调用LocalTimeRange::toDuration方法。

    请注意,将null作为LocalTimeRange的第二个参数传递意味着"直到一天结束"。

  • 最后,我们可以对某一时期的所有日期进行流式处理,计算出每天的非周末时间有多少,然后将其减少以获得总时间:

    LocalDate startDate = ...;
    LocalDate endDate = ...;
    ZoneId zone = ...;
    Duration result = startDate.datesUntil(endDate)
    .map(date -> nonWeekendHours(date, zone))
    .reduce(Duration.ZERO, Duration::plus);
    

使用检索到的Duration实例,您可以使用get<unit>Part()方法轻松获取时间部分,

在线演示

最新更新