【问题标题】:Checking dates if it is in a range检查日期是否在范围内
【发布时间】:2018-04-30 05:13:59
【问题描述】:

我的 Java FX 应用处理工作时间。我在 2 个日期字段中有工作开始和结束时间。我成功计算了两个日期时间之间的差异;但现在我如何检查结果是在夜间还是白天范围内????这一天从 6 点开始,到 22 点结束。例如,在凌晨 3 点到晚上 11 点之间工作的人。 下面是我如何获得总工作小时数。

public void CalculNbreJourTravaille() {
    SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyy HH:mm");

    try {
        Date ddtt = format.parse(ddt.getText());
        Date dftt = format.parse(dft.getText());
        long diff = dftt.getTime() - ddtt.getTime();
        long diffhours = diff / (60*60*1000)%24;
        long diffdays = diff/(24*60*60*1000);
        long total = diffhours + (diffdays*24);
        result.setText(total + " Hours");   
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

我们有可以工作超过晚上 10 点的工人,但工资不会一样。如果他们在晚上 10 点之后工作,他们将获得特殊报酬。我们在工作结束时付款。他们只能工作 10 天或更长时间。

【问题讨论】:

  • 你也应该问一个问题。
  • 尝试使用LocalDate
  • 在夏令时开始或结束时,通过除以 (24*60*60*1000) 将差值转换为天数可能会失败。
  • 查看 Java 8 的新日期和时间 API:tutorialspoint.com/java8/java8_datetime_api.htm
  • 如果有人从 3 点工作到 23 点,那就是晚上 3 小时,然后是白天 16 小时,最后是晚上 1 小时。你需要所有这些信息,还是只需要知道这个人白天和晚上都工作了,总共工作了 20 个小时?您的要求不清楚,抱歉。

标签: java date javafx fxml


【解决方案1】:

您应该使用新的 DateTimeFormatter 类为您提供一个 LocalDateTime 对象,您可以从中提取小时数。

DateTimeFormatter format = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
LocalDateTime localDateTimeFrom = format.parse(dateFrom.getText(), LocalDateTime::from);
LocalDateTime localDateTimeTo = format.parse(dateTo.getText(), LocalDateTime::from);

int hoursFrom = localDateTimeFrom.getHour();
int hoursTo = localDateTimeTo.getHour();

boolean workedNight = hoursFrom < 6 || hoursTo > 22;

【讨论】:

  • 这似乎是正确的。我会检查并恢复。谢谢
  • 如果硬编码时间 6 和 22 是可以的,这可以工作。如果您希望能够将其配置为 5:30 和 21:45,则需要与 LocalTime 进行比较。如果可以在早上 23 点到 5 点工作,那就更复杂了。
  • 是的。例如,工作时间是从凌晨 2 点到早上 9 点。当日夜工作混在一起时
  • 这不能准确检测到正常工作日的结束:22:59 将被截断为 22,因此将被视为正常工作日的一部分...
  • @fabian 好的,当然,但是 OP 的困难在于如何从两个文本字段中找到小时值。我已经向他/她展示了如何做到这一点。然后他/她将什么逻辑应用于这些小时值实际上是 OP 自己解决的问题。
【解决方案2】:

这是我试图满足您的所有要求的尝试。我在阅读之前编写了代码,您不需要考虑夏令时 (DST),因此我使用 ZonedDateTime 在夏令时转换期间获得正确的时间。出于同样的原因,我需要每天迭代。对于每个日期,我都会计算夜间工作时间和白天工作时间。

如果您想确保考虑夏令时,请使用LocalDateTime 而不是ZonedDateTime。在这种情况下,一次性计算整个工作日而不是一次计算一天也可能会提高性能。

以下代码使用 28/03/2018 03:00 和 29/03/2018 23:30 作为开始和结束时间的示例。预计总工作时间为 44.5 小时,因为一天是 24 小时,从 03:00 到 23:30 有 20.5 小时。预计白天时间为 32 小时,因为这两天每天都有 16 小时。这留下了 12.5 小时作为夜间时间。确实是代码打印

一天 32.0 小时;晚上 12.5 小时

程序如下。请填写我放置美国/蒙特雷的正确时区。

static ZoneId zone = ZoneId.of("America/Monterrey");
static LocalTime dayStart = LocalTime.of(6, 0);
static LocalTime dayEnd = LocalTime.of(22, 0);
static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/uuuu H:mm");

public static void main(String[] args) {

    String workStartString = "28/03/2018 03:00";
    String workEndString = "29/03/2018 23:30";
    calculateWorkingHours(workStartString, workEndString);
}

public static void calculateWorkingHours(String workStartString, String workEndString) {
    ZonedDateTime workStart
            = LocalDateTime.parse(workStartString, formatter).atZone(zone);
    ZonedDateTime workEnd
            = LocalDateTime.parse(workEndString, formatter).atZone(zone);

    if (workEnd.isBefore(workStart)) {
        throw new IllegalArgumentException("Work end must not be before work start");
    }

    LocalDate workStartDate = workStart.toLocalDate();
    LocalDate workEndDate = workEnd.toLocalDate();

    Duration workedDaytime = Duration.ZERO;
    // first calculate work at nighttime before the start date, that is, work before 06:00
    Duration workedNighttime 
            = calculateNightTime(workStartDate.minusDays(1), workStart, workEnd);

    for (LocalDate d = workStartDate; ! d.isAfter(workEndDate); d = d.plusDays(1)) {
        workedDaytime = workedDaytime.plus(calculateDayTime(d, workStart, workEnd));
        workedNighttime = workedNighttime.plus(calculateNightTime(d, workStart, workEnd));
    }

    double dayHours = workedDaytime.toMinutes() / (double) TimeUnit.HOURS.toMinutes(1);
    double nightHours = workedNighttime.toMinutes() / (double) TimeUnit.HOURS.toMinutes(1);

    System.out.println("Day " + dayHours + " hours; night " + nightHours + " hours");

}

/**
 * Calculates amount of work in daytime on d,
 * that is between 06:00 and 22:00 on d.
 * Only time that falls with in workStart to workAnd
 * and also falls within 06:00 to 22:00 on d is included.
 * 
 * @param d The date for which to calculate day work
 * @param workStart
 * @param workEnd
 * @return Amount of daytime work on the said day
 */
private static Duration calculateDayTime(LocalDate d, ZonedDateTime workStart, ZonedDateTime workEnd) {
    ZonedDateTime dayStartToday = d.atTime(dayStart).atZone(zone);
    ZonedDateTime dayEndToday = d.atTime(dayEnd).atZone(zone);
    if (workStart.isAfter(dayEndToday) || workEnd.isBefore(dayStartToday)) {
        return Duration.ZERO;
    }

    // restrict calculation to daytime on d
    if (workStart.isBefore(dayStartToday)) {
        workStart = dayStartToday;
    }
    if (workEnd.isAfter(dayEndToday)) {
        workEnd = dayEndToday;
    }

    return Duration.between(workStart, workEnd);
}

/**
 * Calculates amount of night work in the night after d,
 * that is from 22:00 on d until 06:00 the next morning.
 * 
 * @param d The date for which to calculate night work
 * @param workStart
 * @param workEnd
 * @return Amount of nighttime work in said night
 */
private static Duration calculateNightTime(LocalDate d, ZonedDateTime workStart, ZonedDateTime workEnd) {
    assert ! workEnd.isBefore(workStart);

    ZonedDateTime nightStart = d.atTime(dayEnd).atZone(zone);
    ZonedDateTime nightEnd = d.plusDays(1).atTime(dayStart).atZone(zone);

    if (workEnd.isBefore(nightStart) || workStart.isAfter(nightEnd)) {
        return Duration.ZERO;
    }

    // restrict calculation to the night after d
    if (workStart.isBefore(nightStart)) {
        workStart = nightStart;
    }
    if (workEnd.isAfter(nightEnd)) {
        workEnd = nightEnd;
    }

    return Duration.between(workStart, workEnd);
}

【讨论】:

  • 亲爱的 Ole 非常感谢您花时间帮助我。是的,我在非洲大陆,特别是东非。所以白天和晚上的时间在夏天和冬天都不会改变。我将尝试您的代码并还原您。
  • 非常感谢奥莱。它有效并且非常高兴。你是天才,很自豪能成为 stackoverflow 成员的一员。干得好!!!!!!!
【解决方案3】:

您可以检查LocalDateTimeLocalTime 部分,以便使用isAfterisBefore 进行简单检查。

我将在本示例中使用这些值。

LocalDateTime start = LocalDateTime.of(2018, Month.APRIL, 30, 23, 0);
LocalDateTime end = LocalDateTime.of(2018, Month.MAY, 1, 5, 0);

然后定义夜间的限制。

LocalTime startNight = LocalTime.of(22, 0);
LocalTime endNight = LocalTime.of(6, 0);

只需使用获取两个日期的LocalTime 并检查它们是否在范围内。您可以使用toLocalTime 获取值。

if(start.toLocalTime().isAfter(startNight) &&
        end.toLocalTime().isBefore(endNight)){
    System.out.println("NIGHT TIME");
} else {
    System.out.println("DAY TIME");
}

夜间时间

输出有效,因为我们从 23:00 开始,到 05:00 结束。


如果您需要为 5:45 定义像 LocalTime.of(5,45) 这样的时间,则使用此方法可以实现更简单的解决方案

这是一个示例,如果允许从第 22 部分开始但在第 6 部分之后继续工作,则可能需要进行一些调整。这只是说明如何使用这些方法的示例。

【讨论】:

    【解决方案4】:

    如果您使用java.time API,这会更容易。您只需检查日期是否不同或开始时间是否不在6:0022:00 的范围内:

    private static final LocalTime START_TIME = LocalTime.of(6, 0); // 06:00
    private static final LocalTime END_TIME = LocalTime.of(22, 0); // 22:00
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
    
    // parse from input strings
    LocalDateTime start = LocalDateTime.parse(startText, FORMATTER);
    LocalDateTime end = LocalDateTime.parse(endText, FORMATTER);
    
    boolean nightTime = 
            !start.toLocalDate().equals(end.toLocalDate())
            || start.toLocalTime().isBefore(START_TIME)
            || end.toLocalTime().isAfter(END_TIME);
    
    // todo: change output to gui
    System.out.println("night time: " + nightTime);
    System.out.println("duration  : " + Duration.between(start, end).toHours());
    

    【讨论】:

      【解决方案5】:

      定义两个格式化程序。一个 Fromatter 从 edittext 获取日期和时间。等到当天凌晨 12 点。现在我们需要对应于同一天早上 6 点和晚上 11 点的日期对象。我们可以通过向 12AM 对象添加那么多毫秒来获得这些。这些添加的日期可用于比较。

      SimpleDateFormat df_zero_hours = new SimpleDateFormat("dd/MM/yyy");
      SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm");
      
      Date ddtt = format.parse(ddt.getText()); //Work Start Time
      Date dftt = format.parse(dft.getText()); //Work End Time
      
      
      Date dateStart = df_zero_hours.parse(ddt.getText()); //12AM of the day job started
      Date dayStart = new Date();
          dayStart.setTime(dateStart.getTime()+6*60*60*1000); // Get 6AM of that day
      Date dayEnd = new Date();
          dayEnd.setTime(dateStart.getTime()+22*60*60*1000); //Get 10PM of that day
      
      //        Now check the worked hours. in Whatever way you need
      boolean isBefore6AM = (dayStart.getTime()-ddtt.getTime())>0;
      boolean isAfter10PM = (dftt.getTime()-dayEnd.getTime())>0;
      

      【讨论】:

      • 我认为这不会像您期望的那样工作。您将午夜与同一天的早上 6 点和晚上 10 点进行比较。这将始终给出相同的结果。
      • 是的,我也注意到了。
      • 感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。一个适当的解释将通过展示为什么这是解决问题的好方法来极大地提高其长期价值,并使其对有其他类似问题的未来读者更有用。请编辑您的答案以添加一些解释,包括您所做的假设。
      • 抱歉,我只是在比较日期。应该比较 Long 值以获得正确的结果。
      • @Dawood ibn Kareem 我现在已经更正了。希望它现在可以工作。
      猜你喜欢
      • 1970-01-01
      • 2017-01-28
      • 1970-01-01
      • 2016-01-25
      • 1970-01-01
      • 1970-01-01
      • 2019-07-07
      • 2012-08-25
      相关资源
      最近更新 更多