Trouver le premier et le dernier jour de la semaine, du mois, du trimestre, et autre calculs

Beaucoup de participants au forum SQL Server se compliquent la vie avec des calculs bizarres, à trouver le premier ou dernier jour d’un index temporel, comme celui d’une semaine, d’un mois, ou d’un trimestre.

Ces calculs se font souvent à base d’extractions de sous-chaînes après avoir converti une date en varchar(n), et on aboutit donc à des choses abominables.

Voici donc une série de calculs de valeurs, uniquement à base de fonctions de date …
Oui, les dates se manipulent avec des fonctions de date, et non pas avec des fonctions de chaîne !

J’en profite au passage pour rappeler mon billet sur le choix des types de données

La majorité des valeurs calculées ci-dessous repose sur la différence en semaines, mois, trimestres, …, entre la date courante et le 1er Janvier 1900.
En effet, on peut introduire directement cette date par un zéro dans les fonctions de date, que SQL Server caste automatiquement, et qui donne cette date.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
-------------------------------
-- Nicolas Souquet - 29/03/2011
-------------------------------
SELECT  --*** Semaine
  DATEADD(week, DATEDIFF(week, 0, GETDATE()) - 1, 0) AS premier_jour_de_la_semaine_precedente
  , DATEADD(week, DATEDIFF(week, 0, GETDATE()), 0) AS premier_jour_de_la_semaine_courante
  , DATEADD(week, DATEDIFF(week, 0, GETDATE()) + 1, 0) AS premier_jour_de_la_semaine_prochaine
  ---
  , DATEADD(DAY, -1, DATEADD(week, DATEDIFF(week, 0, GETDATE()), 0)) AS dernier_jour_de_la_semaine_precedente
  , DATEADD(DAY, -1, DATEADD(week, DATEDIFF(week, 0, GETDATE()) + 1, 0)) AS dernier_jour_de_la_semaine_courante
  , DATEADD(DAY, -1, DATEADD(week, DATEDIFF(week, 0, GETDATE()) + 2, 0)) AS dernier_jour_de_la_semaine_prochaine
  --*** Mois
  , DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 1, 0) AS premier_jour_du_mois_precedent
  , DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) AS premier_jour_du_mois_courant  
  , DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0) AS premier_jour_du_mois_prochain
  ---
  , DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)) AS dernier_jour_du_mois_precedent
  , DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0)) AS dernier_jour_du_mois_courant  
  , DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 2, 0)) AS dernier_jour_du_mois_prochain
  --*** Trimestre
  , DATEADD(quarter, DATEDIFF(quarter, 0, GETDATE()) - 1, 0) AS premier_jour_du_trimestre_precedent
  , DATEADD(quarter, DATEDIFF(quarter, 0, GETDATE()), 0) AS permier_jour_du_trimestre_courant
  , DATEADD(quarter, DATEDIFF(quarter, 0, GETDATE()) + 1, 0) AS premier_jour_du_trimestre_prochain
  ---
  , DATEADD(DAY, -1, DATEADD(quarter, DATEDIFF(quarter, 0, GETDATE()), 0)) AS dernier_jour_du_trimestre_precedent
  , DATEADD(DAY, -1, DATEADD(quarter, DATEDIFF(quarter, 0, GETDATE()) + 1, 0)) AS dernier_jour_du_trimestre_courant
  , DATEADD(DAY, -1, DATEADD(quarter, DATEDIFF(quarter, 0, GETDATE()) + 2, 0)) AS dernier_jour_du_trimestre_prochain
  --*** Semestre
  , DATEADD(MONTH, ((DATEDIFF(quarter, 0, GETDATE()) / 2) * 6) - 6, 0) AS premier_jour_du_semestre_precedent
  , DATEADD(MONTH, (DATEDIFF(quarter, 0, GETDATE()) / 2) * 6, 0) AS premier_jour_du_semestre_courant
  , DATEADD(MONTH, ((DATEDIFF(quarter, 0, GETDATE()) / 2) * 6) + 6, 0) AS premier_jour_du_semestre_prochain
  ---
  , DATEADD(DAY, -1, DATEADD(MONTH, (DATEDIFF(quarter, 0, GETDATE()) / 2) * 6, 0)) AS dernier_jour_du_semestre_precedent
  , DATEADD(DAY, -1, DATEADD(MONTH, ((DATEDIFF(quarter, 0, GETDATE()) / 2) * 6) + 6, 0)) AS dernier_jour_du_semestre_courant
  , DATEADD(DAY, -1, DATEADD(MONTH, ((DATEDIFF(quarter, 0, GETDATE()) / 2) * 6) + 12, 0)) AS dernier_jour_du_semestre_prochain
  --*** Annee
  , DATEADD(YEAR , DATEDIFF(YEAR, 0, GETDATE()) - 1, 0) AS premier_jour_annee_precedente
  , DATEADD(YEAR , DATEDIFF(YEAR, 0, GETDATE()), 0) AS premier_jour_annee_courante
  , DATEADD(YEAR , DATEDIFF(YEAR, 0, GETDATE()) + 1, 0) AS premier_jour_annee_suivante
  ---
  , DATEADD(DAY, -1, DATEADD(YEAR , DATEDIFF(YEAR, 0, GETDATE()), 0)) AS dernier_jour_annee_precedente
  , DATEADD(DAY, -1, DATEADD(YEAR , DATEDIFF(YEAR, 0, GETDATE()) + 1, 0)) AS dernier_jour_annee_courante
  , DATEADD(DAY, -1, DATEADD(YEAR , DATEDIFF(YEAR, 0, GETDATE()) + 2, 0)) AS dernier_jour_annee_suivante
  --*** Siècle
  , DATEADD(YEAR, -YEAR(GETDATE()) % 100, DATEADD(YEAR , DATEDIFF(YEAR, 0, GETDATE()), 0)) AS premier_jour_du_siecle_courant
  , DATEADD(YEAR, -YEAR(GETDATE()) % 100 + 100, DATEADD(DAY, -1, DATEADD(YEAR , DATEDIFF(YEAR, 0, GETDATE()), 0))) AS dernier_jour_du_siecle_courant

J’ai eu un peu la flemme sur le siècle, et j’ai trouvé comme prétexte pour m’arrêter que cela ne doit pas présenter grand intérêt :)

Bon calculs sur les dates !

ElSüket ;)

10 réflexions au sujet de « Trouver le premier et le dernier jour de la semaine, du mois, du trimestre, et autre calculs »

  1. salut Elusket
    Merci beaucoup pour cette article qui est trés interéssant
    par comptre a partir de SQL2008R2 il existe la fonction eomonth qui retourne le dernier jour du mois
    on est a maintenant prés a la version 2016 existe il des autres fonctions qui peuvent appraissent et facilite le traitement du date
    merci

    • Merci Abdallah.

      Effectivement tu as raison; néanmoins il faut noter que ces nouvelles fonctions sont des invocations .NET, et qu’elles sont souvent un ersatz de ce que l’on peut faire en C# par exemple.

      En ce qui concerne EOMONTH(), cette fonction a été introduite avec SQL Server 2012 ;)

      A bientôt !

  2. J’ai des problèmes de performance avec les fonctions date.
    Si j’exécute la même requête avec :
    DT > (CASE WHEN (MONTH({ fn CURDATE() }) – 3) DATEADD(month, DATEDIFF(month, 0, GETDATE()) – 3, 0)
    J’ai un résultat en 1min30

    Quelqu’un à une idée sur la différence ?

    • Et avec :
      DT > (CASE WHEN (MONTH({ fn CURDATE() }) – 3) <= 0 THEN '01/' + CAST(12 – 3 + MONTH({ fn CURDATE() }) AS char(2))
      + '/' + CAST(YEAR({ fn CURDATE() }) – 1 AS char(4)) ELSE '01/' + CAST(MONTH({ fn CURDATE() }) – 3 AS char(2)) + '/' + CAST(YEAR({ fn CURDATE() }) AS char(4)) END) le résultat sort en 4sec

  3. Bonjour,

    merci à tous les deux de vos commentaires.

    En ce qui concerne l’année fiscale, il me semble qu’en France c’est l’année civile.

    Je me rappelle m’être renseigné là-dessus quand j’étudiais la possibilité d’ouvrir ma propre entreprise au mois d’Octobre 2009 : le plafond annuel de chiffre d’affaire était à ce moment là de 32000€, et si j’avais ouvert mon entreprise en tant qu’auto-entrepreneur, je n’aurai pas pu dépasser les 8000€ d’Octobre à Décembre, puisque c’est au pro-rata.

    Je ne sais pas si en France il est possible pour les entreprise de choisir leur année fiscale comme c’est le cas aux États-Unis …

    @++ ;)

  4. select dateadd(month, datediff(month, 0, GETDATE()) – (12 + datepart(month, GETDATE()) – 10) % 12, 0),
    dateadd(month, datediff(month, 0, GETDATE()) – (12 + datepart(month, GETDATE()) – 10) % 12 + 12, -1)

  5. Salut Elsuket,

    Je suis tombé sur cette article car j’ai des problèmes de perfs … et j’essai de réaliser la même chose pour l’année fiscale en cours.

    Une idée pour éviter d’utiliser des CASE WHEN ELSE END ?

    Merci dans tous les cas pour cette article :)

Laisser un commentaire