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

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

VBA: Créer un objet Date

Le , par Pierre Fauconnier

0PARTAGES

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:
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:
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é...

607175

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...
Function getFirstDayOfWeek(MyDate As Date) As Date
getFirstDayOfWeek = MyDate - (WeekDay(MyDate, vbMonday) - 1)
End Function

? 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.

607233

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.

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.

607386


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.
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

607236

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...

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

607238


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.


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:
607240

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:
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...
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


607241


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...
Property Get Day() As Long
Day = Day(mDate)
End Property

607242

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é:

607243

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...

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:
607244


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?




.

Vous avez lu gratuitement 461 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.

Une erreur dans cette actualité ? Signalez-nous-la !