IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

Pierre Fauconnier

[Actualité] VBA: Créer un objet Date

Noter ce billet
par , 22/10/2021 à 21h11 (5143 Affichages)
En VBA, une date n'est pas un objet, et ça ne fait pas toujours notre affaire... Et si on créait un objet Date?


Salut


- Pierre?
- Mmmmh?
- Je dois trouver la date du premier jour du trimestre de la date d'entrée d'un employé...
- Arf... Tiens, voilà un calendrier...
- Pierre, j'ai 700 employés à vérifier, là...
- ...
- Et? T'as pas de solution?
- Si... On va systématiser notre approche des dates
- Ah, et comment on fait pour "systématiser notre approche des dates"?
- Ben, on crée un objet Date, puisque ça n'existe pas en VBA.


En préambule, je précise qu'Excel permet de simplifier l'approche avec les fonctions de dates utilisables en VBA via Application.WorksheetFunction, mais j'ai choisi de faire du VBA pur pour pouvoir utiliser l'outil dans d'autres applications qu'Excel.

Trouver le premier jour du trimestre d'une date donnée

Avec une variable MyDate, on peut trouver la date du premier jour du trimestre avec le calcul suivant:
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
DateSerial(Year(MyDate), (Int((Month(MyDate) - 1) / 3) + 1) * 3 - 2, 1)

C'est chouette, mais il faut se mettre ce truc dans un OneNote, un NotePad++, un bloc-notes quelconque pour le réutiliser... Pas certain que ce soit très efficace, surtout si vous ne rangez pas bien vos notes. De plus, il faudra aussi aller chercher le bout de code, le snippet comme on dit, pour la fin du trimestre, le début de la semaine, la fin de l'année, etc, etc...



Fonction procédurale

On pourrait bien entendu se créer une fonction procédurale qui reçoit une date en argument et qui renvoie la date voulue en retour. En voilà deux, celle qui donne le début du trimestre et celle qui donne la fin:
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
Function getFirstDayOfQuarter(MyDate As Date) As Date
getFirstDayOfQuarter = DateSerial(Year(MyDate), (Int((Month(MyDate) - 1) / 3) + 1) * 3 - 2, 1)
End Function
 
Function getLastDayOfQuarter(MyDate As Date) As Date
getLastDayOfQuarter = DateSerial(Year(MyDate), (Int((Month(MyDate) - 1) / 3) + 1) * 3 + 1, 0)
End Function

Et hop, on arrête de réinventer la roue. Il suffit de mettre ces fonctions dans le module Tools, voire de créer un module DateManager et le tour est joué...

Nom : 2021-10-22_082941.png
Affichages : 7172
Taille : 43,1 Ko

Si on veut la date du 1er jour de la semaine du premier jour du trimestre, on se crée une fonction GetFirstDayOfWeek et on lui passe le résultat de la fonction GetFirstDayOfQuarter...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
Function getFirstDayOfWeek(MyDate As Date) As Date
  getFirstDayOfWeek = MyDate - (WeekDay(MyDate, vbMonday) - 1)
End Function
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
? datemanager.getfirstdayofweek(datemanager.getFirstDayOfQuarter(date))


Ça devient lourd comme écriture, mais c'est faisable.


Création d'un objet Date

Plutôt que de traiter les dates en procédural, on peut les traiter en objet. VBA n'offre pas d'objet date, mais rien n'empêche de nous en créer un. C'est en fait très simple. Si vous n'êtes pas familier de la création d'une classe personnalisée, lisez mon tuto sur le sujet. On crée une classe, et l'on pourra alors créer un objet sur base de cette classe, on lui passe une date puis on peut utiliser des méthodes ou des propriétés liées à cet objet.

Création de la classe

On crée un nouveau module de classe et on lui donne un nom... Par exemple oDate.

Nom : 2021-10-22_193107.png
Affichages : 5885
Taille : 112,0 Ko

On va avoir besoin d'une variable privée pour stocker la date qui sera gérée par l'objet. On aura également besoin d'une procédure qui passe la date à l'objet. Tant qu'à faire, on initialisera l'objet avec la date du jour. Ca ne mange pas de pain et ça peut être pratique.

Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
Option Explicit
 
Dim mDate As Date
 
Function init(ByVal Value As Date) As oDate
  mDate = Value
  Set init = Me
End Function
 
Private Sub Class_Initialize()
  mDate = Date
End Sub

A la création de l'objet, on passe par Class_Initialize qui attribue la date du jour à la variable privée mDate. La fonction Init permet de modifier la date en passant la date de son choix. On remarquera au passage que cette fonction renvoie un objet de type oDate, ce qui permettra d'en exploiter directement les propriétés et méthodes.

Nom : 2021-10-25_110656.png
Affichages : 5872
Taille : 6,5 Ko


Propriété de l'objet qui renvoie une date

Telle quelle, la classe ne sert pas à grand-chose. On va créer une propriété qui renvoie la date passée en argument lors de l'initialisation.
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
Property Get ToDate() As Date
  ToDate = mDate
End Property

Maintenant, on peut utiliser notre classe. Comme on le voit, l'objet initialisé l'est avec la date du jour. On peut bien sûr initialiser l'objet avec une autre date

Nom : 2021-10-22_194942.png
Affichages : 5830
Taille : 64,7 Ko

Jusqu'ici, la classe n'apporte rien d'intéressant par rapport à une simple variable de date. Mais si maintenant on ajoute nos deux propriétés permettant d'obtenir les premier et dernier jour du trimestre ainsi que la fonction vue plus haut pour obtenir le premier jour de la semaine de la date, en les réécrivant comme propriétés de notre classe oDate, ça commence à devenir intéressant, surtout si l'on décide que ces propriétés ne renvoient pas une date, mais un objet oDate. On peut alors chainer les propriétés... Au passage, on enrichit la propriété FirstDayOfWeek d'un argument optionnel pour pouvoir démarrer la semaine un autre jour que le lundi...

Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Property Get FirstDayOfQuarter() As oDate
  Set FirstDayOfQuarter = New oDate
  FirstDayOfQuarter.init DateSerial(Year(mDate), (Int((Month(mDate) - 1) / 3) + 1) * 3 - 2, 1)
End Property
 
Property Get LastDayOfQuarter() As oDate
  Set LastDayOfQuarter = New oDate
  LastDayOfQuarter.init DateSerial(Year(mDate), (Int((Month(mDate) - 1) / 3) + 1) * 3 + 1, 0)
End Property
 
Property Get FirstDayOfWeek(Optional FirstWeekDay As VbDayOfWeek = vbMonday) As oDate
  Set FirstDayOfWeek = New oDate
  FirstDayOfWeek.init mDate - (WeekDay(mDate, FirstWeekDay) - 1)
End Property

Nom : 2021-10-22_200319.png
Affichages : 5843
Taille : 89,2 Ko


Vous commencez à voir l'intérêt de travailler avec un objet oDate qui, pour certaines propriétés, renvoie un objet oDate? Ca devient amusant, non?

Création de propriétés selon vos besoins

Continuons un peu. On va ajouter:
  • une propriété qui renvoie la date selon un format passé en argument;
  • une propriété qui renvoie la date transformée en valeur numérique de type long;
  • une propriété qui renvoie un objet oDate pour le dernier jour de la semaine de la date.


Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
Property Get ToString(Pattern As String) As String
  ToString = Format(mDate, Pattern)
End Property
 
Property Get ToLong() As Long
  ToLong = mDate * 1
End Property
 
Property Get LastDayOfWeek(Optional FirstWeekDay As VbDayOfWeek = vbMonday) As oDate
  Set LastDayOfWeek = New oDate
  LastDayOfWeek.init mDate - (WeekDay(mDate, FirstWeekDay) - 1) + 6
End Property

On peut, là aussi, chainer les propriétés:
Nom : 2021-10-22_200941.png
Affichages : 5906
Taille : 7,2 Ko

Lorsque j'ai créé cette classe, j'ai trouvé intéressant d'y ajouter deux propriétés pour aller à la veille ou au lendemain, et qui renvoyaient évidemment un oDate. Ca permet de trouver par exemple la date du dernier jour du trimestre qui précède une date ou la date du premier jour du trimestre qui suit une date:
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
Property Get Inc() As oDate
  Set Inc = New oDate
  Inc.init mDate + 1
End Property
 
Property Get Dec() As oDate
  Set Dec = New oDate
  Dec.init mDate - 1
End Property

Par la suite, je me suis dit qu'il était intéressant de pouvoir décaler la date de x jours, mois, années...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function Offset(ByVal Value As Long, ByVal Unit As String) As oDate
  Dim DateItem As Date
  Set Offset = New oDate

  Select Case UCase(Unit)
    Case "Y"
      Offset.init DateSerial(Year(mDate) + Value, Month(mDate), Day(mDate))
    Case "M"
      DateItem = DateSerial(Year(mDate), Month(mDate) + Value, Day(mDate))
      If Day(DateItem) <> Day(mDate) Then DateItem = DateSerial(Year(DateItem), Month(DateItem), 0)
      Offset.init DateItem
    Case "D"
      Offset.init mDate + Value
  End Select
End Function
Nom : 2021-10-22_202343.png
Affichages : 5769
Taille : 6,5 Ko


Conflit de noms

Bien sûr, j'ai voulu doté ma classe des propriétés Day, Month et Year... Mais je me suis heurté à un problème. En créant la propriété Day, j'ai eu un souci...
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
Property Get Day() As Long
  Day = Day(mDate)
End Property

Nom : 2021-10-22_202939.png
Affichages : 5781
Taille : 10,1 Ko

Forcément, le VBA cherche la fonction Day "au plus près" du code qui l'utilise, et il la trouve dans la classe elle-même, dans ce cas-ci, alors que moi, à l'intérieur de la propriété, je voudrais utiliser la fonction Day du VBA. Deux solutions s'offrent à nous: Modifier le nom de la propriété ou définir le parent de la fonction Day utilisée à l'intérieur de la propriété Day. La fonction Day est issue de la bibliothèque VBA, on va donc la préfixer pour lever l'ambiguïté:

Nom : 2021-10-22_203325.png
Affichages : 5750
Taille : 3,7 Ko

On aura le même problème avec les propriétés Month et Year, et il faudra également modifier la propriété Offset qui utilise, elle aussi, ainsi que toutes les propriétés déjà construites qui utilisent les fonctions VBA.Year , VBA.Month et VBA.Day...

Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
Property Get Day() As Long
  Day = VBA.Day(mDate)
End Property
 
Property Get Month() As Long
  Month = VBA.Month(mDate)
End Property
 
Property Get Year() As Long
  Year = VBA.Year(mDate)
End Property


Vous pouvez ainsi développer à l'envi des propriétés pour votre objet oDate et enrichir la classe au fur et à mesure de vos besoins:
Nom : 2021-10-22_204346.png
Affichages : 5760
Taille : 9,9 Ko


Conclusions

Il vous suffit d'importer ce module cls dans vos projets comme vous le faites pour vos modules Tools, xlTools et autres, et vous cessez ainsi de réinventer la roue à chaque développement.

Comme vous le voyez, créer une classe personnalisée peut faciliter et systématiser votre écriture du code. A vous d'enrichir petit à petit la classe dont vous trouverez le fichier ci-dessous, ou de créer vos classes pour gérer des durées, etc...

Alors, ça vous donne des idées pour créer vos propres classes personnalisées?




.
Miniatures attachées Fichiers attachés

Envoyer le billet « VBA: Créer un objet Date » dans le blog Viadeo Envoyer le billet « VBA: Créer un objet Date » dans le blog Twitter Envoyer le billet « VBA: Créer un objet Date » dans le blog Google Envoyer le billet « VBA: Créer un objet Date » dans le blog Facebook Envoyer le billet « VBA: Créer un objet Date » dans le blog Digg Envoyer le billet « VBA: Créer un objet Date » dans le blog Delicious Envoyer le billet « VBA: Créer un objet Date » dans le blog MySpace Envoyer le billet « VBA: Créer un objet Date » dans le blog Yahoo

Mis à jour 25/10/2021 à 11h11 par Pierre Fauconnier

Catégories
VBA , MS Office , Boite à outils

Commentaires