Date du jour de semaine suivant

Premier billet de la saga ‘Weekday’ qui répond ici aux questions suivantes : « Quelle est la date du prochain lundi ? du prochain jeudi ? … ».
Ce billet présente aussi la fonction VBA ‘WeekDay()’ et sa matrice des résultats qui sert de base à la compréhension des algorithmes basés sur les jours de semaine.

La fonction VBA Weekday()
Cette fonction retourne le n° du jour de semaine de la date passée en argument.
Elle comprend deux arguments dont le deuxième est facultatif :

  • Une expression qui représente une date. Idéalement, passer une variable de type Date.
  • Une constante numérique qui représente le premier jour de la semaine

Si un NULL est passé comme date, Weekday renvoie NULL.

Le deuxième argument accepte les constantes nommées suivantes de l’énumération VbDayOfWeek :

  • vbUseSystem (0)
  • vbSunday (1) – Dimanche
  • vbMonday (2) – Lundi
  • vbTuesday (3) – Mardi
  • vbWednesday (4) – Mercredi
  • vbThursday (5) – Jeudi
  • vbFriday (6) – Vendredi
  • vbSaturday (7) – Samedi

Par défaut, le premier jour de la semaine est dimanche pour correspondre au calendrier U.S (non ISO)…
La solution ‘vbUseSystem’ semble intéressante sur le papier mais, en pratique, le paramétrage de Windows n’est pas sous notre contrôle et s’appuyer dessus est parfois risqué. Si je n’avais qu’une devise avec MS Access, elle serait : « Moins j’ai de dépendances externes (références, paramètres windows,…), mieux je me porte ! »

La fonction retourne une valeur numérique entre 1 et 7 qui indique le n° du jour par rapport au premier jour de la semaine. L’aide d’Access est relativement ambiguë à ce niveau car elle laisse croire que, quelque soit la valeur du deuxième argument si le jour de la date est dimanche par exemple la fonction renvoie systématiquement la constante vbSunday soit la valeur 1, ce qui n’est pas le cas.
En effet, la fonction retourne la position du jour par rapport au premier jour de la semaine.
Par exemple, ?Weekday(#2012/10/7#) retourne la valeur 1 qui correspond bien à vbSunday et ?Weekday(#2012/10/7#, vbMonday) retourne 7 car dimanche est le 7ème jour de la semaine lorsqu’elle commence par lundi.

A savoir si vous travaillez avec des millisecondes…
Les fonctions VBA de date dont Weekday() considèrent qu’une date avec une heure supérieure à 23h59mn59s et 499ms a basculé sur le jour suivant.
Par exemple, ?weekday(86399.499/86400) retourne 7 soit samedi 30/12/1899 et ?weekday(86399.500/86400) retourne 1 soit dimanche 31/12/1899 (86400 = nombre de secondes en 24h).
Par commodité, on utilise souvent Int() pour extraire la date d’une variable date/heure. Si des millisecondes sont présentes, elle peut rendre également un résultat différent :

  • ?#2012/10/7#+86399.499/86400 ‘-> 07/10/2012 23:59:59
  • ?#2012/10/7#+86399.500/86400 ‘-> 08/10/2012
  • ?Int(#2012/10/7#+86399.500/86400) ‘-> 07/10/2012
  • ?#2012/10/7#+86400/86400 ‘-> 08/10/2012

La matrice des valeurs renvoyées
A ce niveau, il est intéressant d’avoir sous la main la matrice 7×7 des valeurs renvoyées par Weekday() en fonction du jour de la date transmise et du premier jour de la semaine (deuxième argument de la fonction) :

Weekday(Date,VbDayOfWeek)
                       VbDayOfWeek
Jour Date       L   M   M   J   V   S   D
Lundi           1   7   6   5   4   3   2
Mardi           2   1   7   6   5   4   3
Mercredi        3   2   1   7   6   5   4
Jeudi           4   3   2   1   7   6   5
Vendredi        5   4   3   2   1   7   6
Samedi          6   5   4   3   2   1   7
Dimanche        7   6   5   4   3   2   1

Par exemple, si le jour de la date en argument est lundi et le 1er jour de semaine est mercredi (vbWednesday), la fonction retourne 6.

Notre question du jour sous forme de matrice
On souhaite connaître la date du jour de semaine défini suivant la date passée en argument. Par exemple, le dimanche suivant le dimanche 7 octobre 2012 a pour date le 14 octobre et le lundi suivant est le 8 octobre.

Dasn ce cas, la matrice des valeurs attendues est :

Jour suivant
                       VbDayOfWeek
Jour date       L   M   M   J   V   S   D
Lundi           7   1   2   3   4   5   6
Mardi           6   7   1   2   3   4   5
Mercredi        5   6   7   1   2   3   4
Jeudi           4   5   6   7   1   2   3
Vendredi        3   4   5   6   7   1   2
Samedi          2   3   4   5   6   7   1
Dimanche        1   2   3   4   5   6   7

 
Comment passer de la matrice initiale à celle-ci ?
On remarque que la somme des deux matrices pour chaque case donne 8 :

Somme des matrices
                       VbDayOfWeek
Jour date       L   M   M   J   V   S   D
Lundi           8   8   8   8   8   8   8
Mardi           8   8   8   8   8   8   8
Mercredi        8   8   8   8   8   8   8
Jeudi           8   8   8   8   8   8   8
Vendredi        8   8   8   8   8   8   8
Samedi          8   8   8   8   8   8   8
Dimanche        8   8   8   8   8   8   8

Ces nouvelle matrice servira de pivot pour passer de la matrice ‘Weekday’ à la matrice ‘Jour suivant’. En effet, il suffit de retrancher à 8 la valeur de ‘Weekday’ pour obtenir la valeur ‘Jour suivant’ correspondante.
L’algorithme est DateJourSuivant = DateRef + 8 - Weekday(DateRef, Jour)

Code source de la fonction ‘DateJourSuivant’

Public Function DateJourSuivant(ByVal DateRef As Date, ByVal Jour As VbDayOfWeek) As Date
   DateJourSuivant = DateRef - CInt(Weekday(DateRef, Jour)) + 8
End Function

La conversion explicite par CInt() du Variant retourné par Weekday() autorise un gain sensible de rapidité.
L’argument ‘Jour’ accepte aussi bien une constante nommée de VbDayOfWeek (SAUF vbUseSystem !) qu’un entier compris entre 1 et 7. Ne pas oublier que la valeur 1 correspond au dimanche (vbSunday) !

Tester la fonction
La fonction suivante teste les 49 possibilités de la matrice et affiche les résultats dans la fenêtre ‘Exécution’ de l’éditeur VB.

Public Function TestDateJourSuivant()
   Dim d As Date, r As Date, dates(1 To 7) As Date
   Dim i As Integer, j As Integer, k As Integer, NbErr As Integer
   Dim s As String, Jours As Variant

   d = Int(Now())
   While Weekday(d) <> vbMonday: d = d + 1: Wend
   Jours = VBA.Array("Dimanche", "Lundi   ", "Mardi   ", "Mercredi", "Jeudi   ", "Vendredi", "Samedi  ")

   Debug.Print "Test DateJourSuivant()" & vbCrLf & "----------------------"
   For i = 1 To 7
      s = vbNullString
      Debug.Print "* Date de référence : " & Format(d, "dddd dd/mm/yyyy")
      Debug.Print vbTab & "Jour cherché" & vbTab & vbTab & "Jour trouvé" & vbTab & vbTab & "Conforme ?" & vbCrLf & _
                  vbTab & "----------------------------------------------"
      For k = 1 To 7: dates(Weekday(d + k)) = d + k: Next k

      For j = 1 To 7
         r = DateJourSuivant(d, j)
         s = s & vbTab & Jours(j - 1) & vbTab & vbTab & Format(r, "ddd dd/mm/yyyy") & vbTab & vbTab
         If r <> dates(j) Then
            NbErr = NbErr + 1
            s = s & "Erreur !" & vbCrLf
         Else
            s = s & "Ok" & vbCrLf
         End If
      Next j
      Debug.Print s
      d = d + 1
   Next i
   Debug.Print "***" & IIf(NbErr > 0, NbErr, " Aucune") & " erreur(s) commise(s) ***"
End Function

 
Voir le récapitulatif des fonctions.

@+ pour la suite des épisodes ‘Weekday’.

Philippe

Laisser un commentaire