JodaTime:两个日期之间的工作日数

问题描述 投票:7回答:3

我在寻找一种计算Joda Time工作日数的方法。我得到的总天数:

DateMidnight start = new DateMidnight(DateMidnight.now());
DateMidnight end =  new DateMidnight(2014,10,10);

int days = Days.daysBetween(start,end).getDays();

然后我会减去周末/假日的数量。但是我如何计算它们呢?

很乐意帮忙。

java jodatime
3个回答
6
投票

今天必须弄清楚这一点,并不喜欢我在网上看到的任何答案。所以这是我遇到这个问题的其他人的解决方案。请注意,我假设如果开始或结束是在周末,我会在下一个工作日(周六或周日成为下周一)。之后就像获取之间的天数一样简单,然后计算出之间的周末数,最后每个周末减去2天就有:

public class DateUtil {

  public static final int DAYS_PER_WEEKEND = 2;
  public static final int WEEK_START = DateTimeConstants.MONDAY;
  public static final int WEEK_END = DateTimeConstants.FRIDAY;

  public static int workdayDiff(Date d1, Date d2) {
    LocalDate start = LocalDate.fromDateFields(d1);
    LocalDate end = LocalDate.fromDateFields(d2);

    start = toWorkday(start);
    end = toWorkday(end);

    int daysBetween = Days.daysBetween(start, end).getDays();
    int weekendsBetween = Weeks.weeksBetween(start.withDayOfWeek(WEEK_START), end.withDayOfWeek(WEEK_START)).getWeeks();

    return daysBetween - (weekendsBetween * DAYS_PER_WEEKEND);
  } 

  public static LocalDate toWorkday(LocalDate d) {
    if (d.getDayOfWeek() > WEEK_END) {
      return d.plusDays(DateTimeConstants.DAYS_PER_WEEK - d.getDayOfWeek() + 1);
    }
    return d;
  }
}

我相信有人会过来评论并不是每个人都有同样的周末等等等等。我将区域设置差异作为练习对象:)


1
投票

tl;dr

Joda-Time库现在处于维护模式,并建议迁移到java.time类,如这个不切实际的例子所示:

EnumSet                                       // An implementation of `Set` highly optimized in both performance and memory-usage for a collection of enum objects.
.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY )  // Using enum `DayOfWeek` defining 7 objects, one for each day-of-week.
.contains(                                    // Search `Set` for a match.
    LocalDate                                 // The `java.time.LocalDate` class represents a date-only without time-of-day and without time zone.
    .of( 2018 , Month.MARCH , 23 )            // Specify month by `Month` enum or by number 1-12 for Jan-Dec.
    .getDayOfWeek()                           // Return a `DayOfWeek` object for this date’s day-of-week.
)                                             // Returns a `boolean`, true if value found in set.

java.time

现代方法使用了取代优秀Joda-Time库的java.time类。

LocalDate

LocalDate类表示没有时间且没有时区的仅日期值。

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜过后几分钟是新的一天,而在Montréal Québec仍然是“昨天”。

如果未指定时区,则JVM会隐式应用其当前的默认时区。在运行时(!)期间,该默认值可能是change at any moment,因此您的结果可能会有所不同。最好明确指定[期望/预期时区] [2]作为参数。

proper time zone name的格式指定continent/region,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;

如果要使用JVM的当前默认时区,请求它并作为参数传递。如果省略,则隐式应用JVM的当前默认值。最好是显式的,因为默认情况下可以在运行期间随时由JVM中任何应用程序的任何线程中的任何代码更改。

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

或指定日期。您可以将月份设置为一个数字,1月至12月的数字为1-12。

LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.

或者,更好的是,使用预定义的Month枚举对象,一年中的每个月一个。提示:在整个代码库中使用这些Month对象而不仅仅是整数,以使您的代码更加自我记录,确保有效值,并提供type-safety

LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;

Days elapsed

您想要计算在周末(周六/周日)和假期过去的天数。让我们从周末开始吧。

定义一个星期值的Set来定义周末。使用预定义七个对象的DayOfWeek枚举,一周中的每一天。

Set< DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;

初始化命中日期的集合。将初始容量设置为稍微优化ArrayList处理的方法。

int initialCapacity = ChronoUnit.DAYS.between( start , stop ) ;  // Bigger than we need, but better than too small.
ArrayList< LocalDate > dates = new ArrayList<>( initialCapacity ) ; 

循环日期,逐日递增。

LocalDate ld = start ;
while ( ld.isBefore( stop ) ) {
    boolean hit = ( ! weekend.contains( ld.getDayOfWeek() ) ) ;
    if( hit ) {
        dates.add( ld ) ;
    }
}

通过修剪我们的List空插槽释放一点内存。

dates.trimToSize() ;

最后,查询List的大小以获取我们的天数。

int weekdaysElapsed = dates.size() ;

Holidays

至于假期,你是自己的。假期的定义变化太大,无法实现自动化。

有一些数据文件和服务按国家/地区提供假期列表。但是您有责任为您所在的国家/地区,州/省,城镇,行业和公司寻找合适的产品。您必须定期检查它是否正确并更新。

即使使用数据源,您也可能需要自定义提供的列表以省略或添加各种假期。例如,在美国,并非所有企业都尊重所有联邦政府指定的假期,同样,许多企业也定义了州或联邦政府未承认的额外假期。

最后,我建议您最好创建一个自己的数据库或其他数据源,并定期使用您感兴趣的特定假期进行手动更新。添加上面的代码,为boolean hit添加另一个标准,根据您自己的假期列表测试每个日期,例如List::contains


About java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,如java.util.DateCalendarSimpleDateFormat

现在在Joda-Timemaintenance mode项目建议迁移到java.time班。

要了解更多信息,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规格是JSR 310

您可以直接与数据库交换java.time对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目是未来可能添加到java.time的试验场。你可能会在这里找到一些有用的类,如IntervalYearWeekYearQuartermore


0
投票

DeezCashews的答案对我来说效果很好,有以下可选修改......

如果像我一样,您正在寻找日期之间的包容性差异,而不是日期之间的独家差异(例如,11月21日至11月25日星期五的员工假期请求应该在5天而不是4天出现),那么正确的方法是在调用“toWorkday()”方法之前将一天添加到“结束”日期。由于算法的实现方式,在此之后的任何时刻对差异进行调整都不起作用

© www.soinside.com 2019 - 2024. All rights reserved.