3 votes

Comment compter les heures ouvrables entre les horodatages en excluant le week-end en MATLAB?

Je me demande si quelqu'un sait comment calculer le nombre d'heures ou de minutes de travail entre des horodatages en MATLAB? Disons que j'ai deux horodatages :

début : 22/06/2017 18:00
fin : 26/06/2017 09:00

Je veux compter les minutes entre 08:00 et 17:00 en excluant le weekend. Cela devrait être 600 minutes.

Est-ce que quelqu'un sait comment écrire une fonction MATLAB pour le faire? Je pourrais aussi utiliser python et/ou R.

Nouvelle demande:
Si les horaires de travail changent pour 9:30-11:30 et 13:00-15:00 pour chaque jour ouvrable. Je me demande si quelqu'un sait comment le faire?

Merci d'avance!

Mises à jour:
@gnovice J'ai apporté quelques modifications à votre code original. Il fonctionne bien pour l'utilisation de deux plages distinctes! Qu'en pensez-vous?

function workMins = work_time(startTime, endTime)
  dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                      dateshift(endTime, 'start', 'day')).', 1, 4); %'
  dateBlock(:, 1) = dateBlock(:, 1)+hours(9)+minutes(30);
  dateBlock(:, 2) = dateBlock(:, 2)+hours(11)+minutes(30);
  dateBlock(:, 3) = dateBlock(:, 3)+hours(13)+minutes(00);
  dateBlock(:, 4) = dateBlock(:, 4)+hours(15);  
  dateBlock(1, 1) = max(dateBlock(1, 1), startTime);
  dateBlock(1, 3) = max(dateBlock(1, 3), startTime);  
  dateBlock(end, 2) = min(dateBlock(end, 2), endTime);
  dateBlock(end, 4) = min(dateBlock(end, 4), endTime);  
  dateBlock((datestr(dateBlock(:, 1), 'd') == 'S'), :) = [];
  workMins = max(diff(dateBlock, 1, 2), 0);
  workMins(:,2) = [];
  workMins = minutes(sum(workMins(:)));
end

3voto

gnovice Points 70970

La solution MATLAB n'est pas aussi moche que tout le monde le pense (bien qu'elle pourrait probablement être simplifiée en utilisant la Boîte à outils financiers). Pour aller droit au but, voici la solution sous forme d'une fonction qui accepte deux valeurs datetime:

function workMins = work_time(startTime, endTime)
  dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                      dateshift(endTime, 'start', 'day')).', 1, 2); %'
  dateBlock(:, 1) = dateBlock(:, 1)+hours(8);
  dateBlock(:, 2) = dateBlock(:, 2)+hours(17);
  dateBlock(1, 1) = max(dateBlock(1, 1), startTime);
  dateBlock(end, 2) = min(dateBlock(end, 2), endTime);
  dateBlock((datestr(dateBlock(:, 1), 'd') == 'S'), :) = [];
  workMins = minutes(sum(max(diff(dateBlock, 1, 2), 0)));
end

Et voici comment tout cela fonctionne...

Tout d'abord, nous avons nos heures de début et de fin en tant que valeurs de type datetime:

startTime = datetime('22/06/2017 18:00');
endTime = datetime('26/06/2017 09:00');

Maintenant, nous pouvons créer une matrice N-by-2 de datetimes. Chaque ligne correspondra à un jour couvert par la plage de startTime à endTime, avec les heures définies à 0:00:00 pour le moment:

dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                    dateshift(endTime, 'start', 'day')).', 1, 2);

Ensuite, nous définissons les horaires de la première colonne à 8:00:00 et les horaires de la deuxième colonne à 17:00:00:

dateBlock(:, 1) = dateBlock(:, 1)+hours(8);
dateBlock(:, 2) = dateBlock(:, 2)+hours(17);

Et maintenant, nous ajoutons startTime et endTime8:00:00 à 17:00:00:

dateBlock(1, 1) = max(dateBlock(1, 1), startTime);
dateBlock(end, 2) = min(dateBlock(end, 2), endTime);

Ensuite, nous supprimons les lignes avec une valeur de jour datestr de 'S' (c'est-à-dire un jour de week-end):

dateBlock((datestr(dateBlock(:, 1), 'd') == 'S'), :) = [];

Et enfin, nous prenons les différences de colonnes, les ajoutons (en ignorant les valeurs négatives) et convertissons en minutes:

workMins = minutes(sum(max(diff(dateBlock, 1, 2), 0)));

Et nous obtenons le résultat souhaité:

workMins =

   600

ÉDIT:

En ce qui concerne la nouvelle demande, vous pouvez modifier légèrement la fonction pour permettre de passer les heures de début et de fin d'une journée de travail comme ceci:

function workMins = work_time(startTime, endTime, workDayStart, workDayEnd)
  dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                      dateshift(endTime, 'start', 'day')).', 1, 2); %'
  dateBlock(:, 1) = dateBlock(:, 1)+hours(workDayStart);
  dateBlock(:, 2) = dateBlock(:, 2)+hours(workDayEnd);
  ...

Et maintenant vous pouvez l'appeler avec vos plages horaires de travail séparées et ajouter les résultats:

startTime = datetime('22/06/2017 18:00');
endTime = datetime('26/06/2017 09:00');
workTotal = work_time(startTime, endTime, 9.5, 11.5) ...
            + work_time(startTime, endTime, 13, 15);

Ou soustraire une sous-plage d'une plage plus grande:

workTotal = work_time(startTime, endTime, 9.5, 15) ...
            - work_time(startTime, endTime, 11.5, 13);

Si vous préférez spécifier les heures de travail comme des chaînes de caractères, vous pouvez écrire la fonction de cette manière:

function workMins = work_time(startTime, endTime, workDayStart, workDayEnd)
  dateBlock = repmat((dateshift(startTime, 'start', 'day'):...
                      dateshift(endTime, 'start', 'day')).', 1, 2); %'
  workDayStart = datevec(workDayStart);
  workDayEnd = datevec(workDayEnd);
  dateBlock(:, 1) = dateBlock(:, 1)+duration(workDayStart(4:6));
  dateBlock(:, 2) = dateBlock(:, 2)+duration(workDayEnd(4:6));
  ...

Et l'utiliser de cette façon:

workTotal = work_time(startTime, endTime, '9:30', '11:30') ...
            + work_time(startTime, endTime, '13:00', '15:00');

2voto

d.b Points 19348

Voici une solution en R

DONNÉES

start = "22/06/2017 18:00"
end = "26/06/2017 09:00"

FONCTION

foo = function(start, end, filter_weekend = TRUE,
        daystart = "08:00:00", dayend = "17:00:00", units = "mins"){
#start and end should be POSIXct

    require(lubridate)

    #Obtenez une séquence de toutes les dates entre le début et la fin
    df = data.frame(col1 = seq.Date(from = as.Date(start),
                                    to = as.Date(end),
                                    by = "days"))

    #Définir le début de chaque jour à daystart
    df$start = ymd_hms(paste(df$col1, daystart))

    #Définir la fin de chaque jour à dayend
    df$end = ymd_hms(paste(df$col1, dayend))
    df$days = weekdays(df$start)

    #Définir le premier jour$start pour qu'il soit la date et l'heure correctes
    df$start[1] = start
    #Définir le dernier jour$end pour qu'il soit la date et l'heure correctes
    df$end[NROW(df)] = end

    if (filter_weekend == TRUE){
        #Supprimer les week-ends
        df = df[!df$days %in% c("Saturday", "Sunday"), ]
    }
    #Supprimer les lignes si la fin est inférieure au début (pertinent dans la première et la dernière ligne)
    df = df[df$end > df$start, ]

    #Calculer la différence de temps pour chaque ligne
    df$time = difftime(time1 = df$end, time2 = df$start, units = units)

    #Sortie
    return(sum(df$time))
}

UTILISATION

library(lubridate)
foo(start = dmy_hm(start), end = dmy_hm(end))
#Différence de temps de 600 mins

foo(start = dmy_hms("22/06/2017 14:21:19"),
        end = dmy_hms("26/06/2017 06:20:53"),
        units = "secs", filter_weekend = TRUE)
#Différence de temps de 41921 secs

2voto

Majo Points 176

Ce n'est pas très joli et il doit y avoir une manière plus élégante, mais cela devrait fonctionner en R de base :

x <- seq(from = as.POSIXct("22/06/2017-18:00", format = '%d/%m/%Y-%H:%M'), to = as.POSIXct("26/06/2017-09:00", format = '%d/%m/%Y-%H:%M'), by= (60*60))
x <- x[!weekdays(x) %in% c('Samedi','Dimanche')]
x <- x[x %in% as.POSIXct(unlist(lapply(unique(as.Date(x)), function(x){paste0(x, ' ', ifelse(nchar(8:16) == 1, as.character(paste0('0', 8:16)), 8:16))})), format = "%Y-%m-%d %H")]
(length(x)-1)*60

En y réfléchissant, il pourrait être plus intelligent de générer les jours ouvrés avec la plage horaire désirée et d'additionner le résultat de la différence de temps pour chaque jour.

1voto

Matt Jewett Points 2187

Voici une fonction personnalisée qui permettra de calculer les minutes travaillées. C'est un peu long, mais elle comporte quelques vérifications qui aident à garantir que les valeurs sont calculées correctement. De plus, elle prend l'unité comme argument, vous pouvez donc calculer le total en secondes, minutes, heures, jours ou semaines.

work.time <- function(Date1, Date2, work.start, work.end, unit){

  # Si les dates sont égales, retourner 0
  if(Date1 == Date2){
    return(0)
  }

  # Vérifier que Date1 est toujours avant Date2
  if(Date1 > Date2){
    D <- Date1
    Date1 <- Date2
    Date2 <- D
    rm(D)
  }

  # Obtenir les horodatages de début et de fin de la journée de travail pour les deux jours
  D1.start <- as.POSIXct(format(Date1, format = paste("%d/%m/%Y", work.start)), format = "%d/%m/%Y %H:%M")
  D1.end <- as.POSIXct(format(Date1, format = paste("%d/%m/%Y", work.end)), format = "%d/%m/%Y %H:%M")

  D2.start <- as.POSIXct(format(Date2, format = paste("%d/%m/%Y", work.start)), format = "%d/%m/%Y %H:%M")
  D2.end <- as.POSIXct(format(Date2, format = paste("%d/%m/%Y", work.end)), format = "%d/%m/%Y %H:%M")

  # Calculer la valeur pour une journée de travail complète
  full.day <- as.numeric(difftime(D1.end, D1.start, units = unit))

  # Calculer la valeur si les dates tombent le même jour
  if(as.Date(Date1) == as.Date(Date2)){
    if(weekdays(as.Date(Date1)) %in% c("samedi", "dimanche")){
      val <- 0
    } else if(Date1 >= D1.start & Date2 <= D1.end){
      val <- as.numeric(difftime(Date2, Date1, units = unit))
    } else if(Date1 >= D1.start & Date2 > D1.end){
      val <- as.numeric(difftime(D1.end, Date1, units = unit))
    } else if(Date1 < D1.start & Date2 <= D1.end){
      val <- as.numeric(difftime(Date2, D1.start, units = unit))
    } else if(Date1 < D1.start & Date2 > D1.end){
      val <- full.day
    } else{
      val <- 0
    }
    return(val)
  }

  # Calculer la valeur pour le premier jour de travail
  if(weekdays(as.Date(Date1)) %in% c("samedi", "dimanche")){
    d1.val <- 0
  } else 
    if(Date1 >= D1.start & Date1 <= D1.end){
    d1.val <- as.numeric(difftime(D1.end, Date1, units = unit))
  } else if(Date1 > D1.end){
    d1.val <- 0
  } else if(Date1 < D1.start){
    d1.val <- full.day
  }

  # Calculer la valeur pour le dernier jour de travail
  if(weekdays(as.Date(Date2)) %in% c("samedi", "dimanche")){
    d2.val <- 0
  } else if(Date2 >= D2.start & Date2 <= D2.end){
    d2.val <- as.numeric(difftime(Date2, D2.start, units = unit))
  } else if(Date2 > D2.end){
    d2.val <- full.day
  } else if(Date2 < D2.start){
    d2.val <- 0
  }

  # Calculer la valeur de travail pour tous les jours ouvrés entre Date1 et Date2
  work.days <- sum(!weekdays(seq(as.Date(Date1) + 1, as.Date(Date2) - 1, "days")) %in% c("samedi", "dimanche"))

  # Ajouter la valeur finale du travail total
  work.val <- (work.days * full.day) + d1.val + d2.val

  return(work.val)
}

Date1 <- as.POSIXct("24/06/2017 18:00", format = "%d/%m/%Y %H:%M")
Date2 <- as.POSIXct("26/06/2017 09:00", format = "%d/%m/%Y %H:%M") 

work.time(Date1, Date2, work.start = "08:00", work.end = "17:00", unit = "mins")

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X