考虑不添加周末,如何将营业时间添加到日期? - Java

时间:2016-01-22 09:45:57

标签: java android

我想添加一定数量的小时,忽略周末

例如,

(星期五18:00)+ 48 =(Tuseday 18:00)(周六和周日被忽略)

由于该公司24小时工作,营业时间为24小时。但我仍然无法在工作日如何增加工作时间

功能可以是:

public Date getTaskEndTime(Calendar startDate, int hours){
   // calculate the end time by adding the hours ignoring the weekends
}

7 个答案:

答案 0 :(得分:3)

逐步增加小时数而不是24小时。如果你在周六或周日结束,请在每一步后检查。在每种情况下再添加24小时。那应该做你想要的。

public Date getTaskEndTime(Calendar startDate, int hours){
    while (hours > 0){
        int step = 0;
        if(hours > 24) step = 24;
        else step = hours;          
        hours -= step;          
        startDate.add(Calendar.HOUR_OF_DAY, step);          
        int dayOfWeek = startDate.get(Calendar.DAY_OF_WEEK);
        if(dayOfWeek == Calendar.SATURDAY) hours += 24;
        if(dayOfWeek == Calendar.SUNDAY) hours += 24;
    }
    return startDate.getTime();
}

答案 1 :(得分:1)

我强烈建议您使用JodaTime(或DateTime Java8),因为旧的日期/日历API非常无用。

public DateTime getEndtime(final DateTime startdate, final int hours) {
    final DateTime endOfWeek = endOfWeek(startdate);

    final Duration restOfWeek = new Duration(startdate, endOfWeek);

    final Duration hoursDuration = toDuration(hours);
    if (restOfWeek.isLongerThan(hoursDuration)) {
        return startdate.plus(hoursDuration);
    } else {
        final Duration durationForNextWeek = hoursDuration.minus(restOfWeek);
        return startOfWeek(startdate).plus(durationForNextWeek);
    }
}

//Converts number of hours as int to Duration
private Duration toDuration(final int hours) {
    return new Duration(hours * 60 * 60 * 1000);
}

//Returns coming friday, 1 millisecond to midnight
private DateTime endOfWeek(final DateTime dateTime) {
    DateTime copy = dateTime;
    while (copy.getDayOfWeek() != 6) {
        copy = copy.plusDays(1);
    }

    return copy.toDateMidnight().toDateTime().minusMillis(1);
}

//Returns the following monday at midnight
//If dateTime is on a monday, the next monday will be chosen
private DateTime startOfWeek(final DateTime dateTime) {
    DateTime copy = dateTime.plusDays(1);
    while (copy.getDayOfWeek() != 1) {
        copy = copy.plusDays(1);
    }

    return copy.toDateMidnight().toDateTime();
}

代码说明:

  • 检查是否可以在不进入周末的情况下添加小时数
  • 如果不是,只需将小时数添加到startdate
  • 即可
  • 如果是,请查找要转移到下周的持续时间,并将其添加到一周的开头

此代码不支持延长数周的任务,但这是一个可以修改以支持此功能的开始..可能是一些处理不好的边缘情况,我会留给您彻底测试它

答案 2 :(得分:1)

避免旧的日期时间类

您正在使用旧的日期时间类,这些类已被证明设计糟糕,令人困惑且麻烦。避免它们。

java.time

使用Java 8及更高版本中内置的java.time框架。见Tutorial。对于Java 6& 7,使用ThreeTen-Backport项目。对于Android,改编后端口ThreeTenABP

以下是一些示例代码,可帮助您实现目标。我只是即兴地鞭打它,所以它可能不健壮。此代码似乎适用于您的一个示例用法:

  

(星期五18:00)+ 48 =(Tuseday 18:00)(周六和周日被忽略)

我稍微概括了这段代码。而不是几个小时,在任何时间跨度内需要Duration(内部表示为总秒数加上几分之一秒,以纳秒为单位)。您会注意到toString方法的输出使用标准ISO 8601表示法,例如PT48H,例如48小时。

假设您想要时间线上的真实时刻,我们需要使用时区来解决夏令时(DST)等异常情况。为此,我们需要从ZonedDateTime开始,它将UTC时间轴(Instant)上的时刻与时区(ZoneId)相结合。

我们还会传递一段时间,例如48小时,以及一组星期几值,例如星期六和星期六。星期日。跳过此方法,以便在下面讨论这些类型。

这种方法的策略是采用我们的持续时间,例如48小时,每次一天消耗掉一个必要的量,以达到第二天的开始。如果第二天恰好是禁止的星期几,我们会在此之后滑到第二天,并继续滑动直到我们达到允许(不禁止)的星期几日期。我们继续蚕食剩余的时间,直到它达到零。阅读代码中的注释以获得更多讨论。

在计算第二天的开始时,不要假设时间为00:00:00.0。由于夏令时(DST)以及某些时区可能存在其他异常,这一天可能会在另一时间开始。我们调用atStartOfDay让java.time确定当天的第一个时刻。

public ZonedDateTime addDurationSkippingDaysOfWeek ( ZonedDateTime zdt , Duration duration , EnumSet<DayOfWeek> daysOfWeek ) {
    // Purpose: Start at zdt, add duration but skip over entire dates where day-of-week is contained in EnumSet of prohibited days-of-week. For example, skip over United States weekend of Saturday-Sunday.
    // Verify inputs.
    if ( ( null == zdt ) || ( null == zdt ) || ( null == zdt ) ) {
        throw new IllegalArgumentException ( "Passed null argument. Message # bf186439-c4b2-423a-b5c9-76edebd87cf0." );
    }
    if ( daysOfWeek.size () == DayOfWeek.values ().length ) { // We must receive 6 or less days. If passed all 7 days of the week, no days left to use for calculation.
        throw new IllegalArgumentException ( "The EnumSet argument specified all days of the week. Count: " + daysOfWeek.size () + ". So, impossible to calculate if we skip over all days. Message # 103a3088-5600-4d4e-a1e0-54410afa14f8." );
    }

    // Move through time, day-to-day, allocating remaining duration.
    ZoneId zoneId = zdt.getZone (); // Passed as argument in code below.
    ZonedDateTime moment = zdt; // This var is reassigned in loop below to fresh value, later and later, as we allocate the Duration to determine our target date-time.
    Duration toAllocate = duration; // Loop below chips away at this Duration until none left.
    while (  ! toAllocate.isZero () ) { // Loop while some duration remains to be allocated.
        if ( toAllocate.isNegative () ) {  // Bad - Going negative should be impossible. Means our logic is flawed.
            throw new RuntimeException ( "The duration to allocate ran into a negative amount. Should not be possible. Message # 15a4267d-c16a-417e-a815-3c8f87af0232." );
        }
        ZonedDateTime nextDayStart = moment.toLocalDate ().plusDays ( 1 ).atStartOfDay ( zoneId );
        Duration untilTomorrow = Duration.between ( moment , nextDayStart );
        //  ZonedDateTime oldMoment = moment;  // Debugging.
        Duration allocation = null;
        if ( untilTomorrow.compareTo ( toAllocate ) >= 0 ) { // If getting to tomorrow exceeds our remaining duration to allocate, we are done.
            // Done -- we can allocate the last of the duration. Remaining to allocate is logically zero after this step.
            moment = moment.plus ( toAllocate );
            allocation = toAllocate; // Allocating all of our remaining duration.
            // Do not exit here; do not call "return". Code below checks to see if the day-of-week of this date is prohibited.
        } else { // Else more duration to allocate, so increment to next day.
            moment = nextDayStart;
            allocation = untilTomorrow; // Allocating the amount of time to take us to the start of tomorrow.
        }
        toAllocate = toAllocate.minus ( allocation ); // Subtract the amount of time allocated to get a fresh amount remaining to be
        // Check to see if the moment has a date which happens to be a prohibited day-of-week.
        while ( daysOfWeek.contains ( moment.getDayOfWeek () ) ) { // If 'moment' is a date which is a day-of-week on oun prohibited list, move on to the next date.
            moment = moment.toLocalDate ().plusDays ( 1 ).atStartOfDay ( zoneId ); // Move to start of the next day after. Continue loop to check this next day.
        }

    }
    return moment;
}

要调用该代码,我们将使用您的示例数据值。作为获取原始数据输入的一种方法,我们将字符串解析为LocalDateTime

String input = "2016-01-01T18:00:00";
LocalDateTime ldt = LocalDateTime.parse ( input );

LocalDateTime对象表示时间轴上的实际时刻。因此,我们应用时区来获得实际时刻ZonedDateTime

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone ( zoneId );
Duration duration = Duration.ofHours ( 48 );

我也从&#34; the weekend&#34;对于任何一组星期值而不是硬编码星期六和星期六星期日。 java.time类包含一个方便的enumDayOfWeek - 比在代码中使用字符串或数字要好得多。

Java中的枚举工具比大多数语言中的简单数字掩码一样灵活得多。它的功能之一是一个特殊的Set实现EnumSet,用于收集枚举定义的可能项的子集。对于我们这里,我们想收集一对项目,包括星期六项目和星期日项目。

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

也许工作日是一个为期四天的工作日,如周一至周二加周四至周五,然后使用EnumSet.of ( DayOfWeek.WEDNESDAY , DayOfWeek.SATURDAY , DayOfWeek.SUNDAY )

现在我们准备调用我们的计算方法了。传递三个论点:

  • 起始ZonedDateTime
  • 要添加到开始日期时间的Duration
  • EnumSetDayOfWeek件。

我们收回计算的未来日期时间。

ZonedDateTime zdtLater = this.addDurationSkippingDaysOfWeek ( zdt , duration , daysOfWeek );

转储到控制台。

System.out.println ( "zdt: " + zdt + " plus duration: " + duration + " while skipping daysOfWeek: " + daysOfWeek + " is zdtLater: " + zdtLater );
  

zdt:2016-01-01T18:00-05:00 [美国/蒙特利尔]加上持续时间:PT48H同时跳过daysOfWeek:[SATURDAY,SUNDAY]是zdtLater:2016-01-05T18:00-05:00 [美国/蒙特利尔]

答案 3 :(得分:1)

下面的代码确实解决了目的。

public static Date addBusinessHours(Calendar startDate, int hours, int workingHourStart, int workingHourEnd){
    System.out.println("Entering: Date Time " + startDate.getTime() + " | Remaining Hours: "+ hours + " | Working hours ("+workingHourStart+"-"+workingHourEnd+")");

    if(hours == 0){
        return startDate.getTime();
    }
    int hourOfDay = startDate.get(Calendar.HOUR_OF_DAY);
    if(startDate.get(Calendar.MINUTE) > 0){
        hourOfDay = hourOfDay +1;
    }

    int dayOfWeek = startDate.get(Calendar.DAY_OF_WEEK);

    if(dayOfWeek == Calendar.SATURDAY){
        startDate.add(Calendar.DATE, 2);
        startDate.set(Calendar.HOUR_OF_DAY, workingHourStart);
        startDate.set(Calendar.MINUTE, 0);
        startDate.set(Calendar.SECOND, 0);
        addBusinessHours(startDate, hours, workingHourStart, workingHourEnd);
    }
    if(dayOfWeek == Calendar.SUNDAY){
        startDate.add(Calendar.DATE, 1);
        startDate.set(Calendar.HOUR_OF_DAY, workingHourStart);
        startDate.set(Calendar.MINUTE, 0);
        startDate.set(Calendar.SECOND, 0);
        addBusinessHours(startDate, hours, workingHourStart, workingHourEnd);
    }

    if(dayOfWeek != Calendar.SATURDAY && dayOfWeek != Calendar.SUNDAY){
        if(hourOfDay < workingHourStart){
            startDate.set(Calendar.HOUR_OF_DAY, workingHourStart);
            startDate.set(Calendar.MINUTE, 0);
            startDate.set(Calendar.SECOND, 0);
            hourOfDay = startDate.get(Calendar.HOUR_OF_DAY);
            dayOfWeek = startDate.get(Calendar.DAY_OF_WEEK);
            addBusinessHours(startDate, hours, workingHourStart, workingHourEnd);
        }
        else if(hourOfDay >= workingHourEnd){

            startDate.add(Calendar.DATE, 1);
            startDate.set(Calendar.HOUR_OF_DAY, workingHourStart);
            startDate.set(Calendar.MINUTE, 0);
            startDate.set(Calendar.SECOND, 0);
            hourOfDay = startDate.get(Calendar.HOUR_OF_DAY);
            dayOfWeek = startDate.get(Calendar.DAY_OF_WEEK);
            addBusinessHours(startDate, hours, workingHourStart, workingHourEnd);
        }

        else if(hourOfDay >= workingHourStart && hourOfDay < workingHourEnd){
            if(hours+hourOfDay <= workingHourEnd){
                startDate.add(Calendar.HOUR_OF_DAY, hours);
                return startDate.getTime();
            }else{
                //System.out.println("¤¤" + startDate.getTime() );
                startDate.add(Calendar.DATE, 1);
                //System.out.println("¤¤" + startDate.getTime() );
                startDate.set(Calendar.HOUR_OF_DAY, workingHourStart);
                startDate.set(Calendar.MINUTE, 0);
                startDate.set(Calendar.SECOND, 0);
                //System.out.println("¤¤" + startDate.getTime() );

                System.out.println("##"+hours+ "##"+ workingHourEnd + "##" + hourOfDay);
                int remaining_hours = hours - (workingHourEnd - hourOfDay);
                addBusinessHours(startDate, remaining_hours, workingHourStart, workingHourEnd);
            }
        }
    }

    return startDate.getTime();
}

答案 4 :(得分:0)

基本上你需要做的是首先计算一周剩余的小时数(这是当前时间与周五24:00之间的差异)。如果小时数小于您刚添加的小时数。

否则你从小时中减去该数量,然后如果余数超过120小时(一周),你取整数商并跳过那么多周。最后,将剩余部分添加到当周00:00 @周一。

在你的例子中,你的开始时间是在你的开始时间和24:00 @ Fri 6h之间,这个时间不到48小时,所以你从中减去6h并得到42h。现在42小时不到120小时,所以你不要错过几周,然后在星期一晚上18点到达星期五00:00加上42小时。

答案 5 :(得分:0)

请参考以下代码,看看是否对您有帮助。

public static Date getTaskEndTime(Date startDate, int hours) {
    // calculate the end time by adding the hours ignoring the weekends
    Calendar endCal = Calendar.getInstance();
    endCal.setTime(startDate);

    for (int i = 0; i < hours; hours = hours - 8) {
        if (!(endCal.get(Calendar.DAY_OF_WEEK) == 1 || endCal.get(Calendar.DAY_OF_WEEK) == 7)) {
            endCal.add(Calendar.DAY_OF_MONTH, 1);
        } else {
            endCal.add(Calendar.DAY_OF_MONTH, 1);
            hours = hours + 8;
        }
    }
    return endCal.getTime();
}

答案 6 :(得分:-1)

您需要以自定义方式处理它:

  public Date getTaskEndTime(Calendar startDate, int hours) {
        Calendar endTime = Calendar.getInstance();
        endTime.setTime(startDate.getTime());
        endTime.add(Calendar.HOUR, hours); // add 2 for saturday and sunday
        int dayOfWeek = endTime.get(Calendar.DAY_OF_WEEK);
        if (dayOfWeek == Calendar.SATURDAY) {
            endTime.add(Calendar.DATE, 2); // add 2 for saturday and sunday
        } else if (dayOfWeek == Calendar.SATURDAY) {
            endTime.add(Calendar.DATE, 1); // add 1 for sunday
        }
        return endTime.getTime();
    }