Obtention du premier et du dernier jour d'un mois, à l'aide d'un objet DateTime donné


196

Je veux obtenir le premier et le dernier jour du mois où se trouve une date donnée. La date provient d'une valeur dans un champ d'interface utilisateur.

Si j'utilise un sélecteur de temps, je pourrais dire

var maxDay = dtpAttendance.MaxDate.Day;

Mais j'essaye de l'obtenir à partir d'un objet DateTime. Donc si j'ai ça ...

DateTime dt = DateTime.today;

Comment obtenir le premier et le dernier jour du mois dt?


Ce que vous demandez n'est pas clair. Il y a une seule valeur stockée dans la _Datevariable. Quel "minimum et maximum" essayez-vous d'obtenir de cette valeur?
David

il est sous-voté parce que les gens se demandent pourquoi vous voudriez faire une telle réflexion et où vous utiliseriez une telle chose. Vous ne nous dites rien sur votre problème initial ici
Mo Patel

Il vaut mieux demander ce que tu veux faire? pas comme ça Comment tu veux faire? une autre utilisation peut vous suggérer correctement.
Shell

2
@Chathuranga vous savez quelle sera la date minimale de chaque mois .... mais la question est quelle est la dernière date du mois en cours .. date .. maintenant vous obtiendrez la dernière date de votre mois en cours
Shell

Réponses:


473

DateTimela structure stocke une seule valeur, pas une plage de valeurs. MinValueet MaxValuesont des champs statiques, qui contiennent une plage de valeurs possibles pour les instances de DateTimestructure. Ces champs sont statiques et ne se rapportent pas à une instance particulière de DateTime. Ils concernent le DateTimetype lui-même.

Lecture suggérée: statique (référence C #)

MISE À JOUR: Obtenir la plage de mois:

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

16
Je sais que je suis difficile ici, mais ne devrait pas l' lastDayofMonthêtre firstDayOfMonth.AddMonths(1).AddSeconds(-1);?
Karl Gjertsen

36
@KarlGjertsen vous n'êtes pas assez pointilleux :) La solution parfaite sera AddTicks(-1), mais si nous ne nous soucions pas de la partie temps et ne pensons qu'à la partie date, alors les jours fonctionnent bien
Sergey Berezovskiy

7
Maintenant, c'est difficile! ;-) La question ne dit pas comment les valeurs vont être utilisées, j'ai donc tendance à coder défensivement.
Karl Gjertsen

@SergeyBerezovskiy Je détesterais soulever ce point, mais ne perdez-vous pas les informations de fuseau horaire lorsque vous créez un DateTime comme celui-ci? Les informations de fuseau horaire attachées à l'instance DateTime d'origine sont perdues lorsque vous créez une nouvelle instance comme celle-ci.
Marko

2
@KarlGjertsen, vous voulez voir pointilleux ... personnellement, je le fais < firstDayOfNextMonthau lieu de <= lastDayOfMonth. De cette façon, cela fonctionnera toujours quelle que soit la granularité. (Je suis sûr que les tiques iront bien, mais qui sait ce que l'avenir nous réserve ... nanoticks?)
adam0101

99

Il s'agit plutôt d'un long commentaire sur les réponses de @Sergey et @ Steffen. Ayant écrit moi-même un code similaire dans le passé, j'ai décidé de vérifier ce qui était le plus performant tout en se rappelant que la clarté est également importante.

Résultat

Voici un exemple de résultat de test pour 10 millions d'itérations:

2257 ms for FirstDayOfMonth_AddMethod()
2406 ms for FirstDayOfMonth_NewMethod()
6342 ms for LastDayOfMonth_AddMethod()
4037 ms for LastDayOfMonth_AddMethodWithDaysInMonth()
4160 ms for LastDayOfMonth_NewMethod()
4212 ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()
2491 ms for LastDayOfMonth_SpecialCase()

Code

J'ai utilisé LINQPad 4 (en mode programme C #) pour exécuter les tests avec l'optimisation du compilateur activée. Voici le code testé pris en compte comme méthodes d'extension pour plus de clarté et de commodité:

public static class DateTimeDayOfMonthExtensions
{
    public static DateTime FirstDayOfMonth_AddMethod(this DateTime value)
    {
        return value.Date.AddDays(1 - value.Day);
    }

    public static DateTime FirstDayOfMonth_NewMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, 1);
    }

    public static DateTime LastDayOfMonth_AddMethod(this DateTime value)
    {
        return value.FirstDayOfMonth_AddMethod().AddMonths(1).AddDays(-1);
    }

    public static DateTime LastDayOfMonth_AddMethodWithDaysInMonth(this DateTime value)
    {
        return value.Date.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - value.Day);
    }

    public static DateTime LastDayOfMonth_SpecialCase(this DateTime value)
    {
        return value.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - 1);
    }

    public static int DaysInMonth(this DateTime value)
    {
        return DateTime.DaysInMonth(value.Year, value.Month);
    }

    public static DateTime LastDayOfMonth_NewMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, DateTime.DaysInMonth(value.Year, value.Month));
    }

    public static DateTime LastDayOfMonth_NewMethodWithReuseOfExtMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, value.DaysInMonth());
    }
}

void Main()
{
    Random rnd = new Random();
    DateTime[] sampleData = new DateTime[10000000];

    for(int i = 0; i < sampleData.Length; i++) {
        sampleData[i] = new DateTime(1970, 1, 1).AddDays(rnd.Next(0, 365 * 50));
    }

    GC.Collect();
    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].FirstDayOfMonth_AddMethod();
    }
    string.Format("{0} ms for FirstDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].FirstDayOfMonth_NewMethod();
    }
    string.Format("{0} ms for FirstDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_AddMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_AddMethodWithDaysInMonth();
    }
    string.Format("{0} ms for LastDayOfMonth_AddMethodWithDaysInMonth()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_NewMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_NewMethodWithReuseOfExtMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()", sw.ElapsedMilliseconds).Dump();

    for(int i = 0; i < sampleData.Length; i++) {
        sampleData[i] = sampleData[i].FirstDayOfMonth_AddMethod();
    }

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_SpecialCase();
    }
    string.Format("{0} ms for LastDayOfMonth_SpecialCase()", sw.ElapsedMilliseconds).Dump();

}

Une analyse

J'ai été surpris par certains de ces résultats.

Bien qu'il n'y ait pas grand-chose, il FirstDayOfMonth_AddMethodétait légèrement plus rapide que FirstDayOfMonth_NewMethoddans la plupart des tests. Cependant, je pense que ce dernier a une intention légèrement plus claire et j'ai donc une préférence pour cela.

LastDayOfMonth_AddMethodétait un perdant clair contre LastDayOfMonth_AddMethodWithDaysInMonth, LastDayOfMonth_NewMethodet LastDayOfMonth_NewMethodWithReuseOfExtMethod. Entre les trois plus rapides, il n'y a pas grand-chose et cela dépend donc de vos préférences personnelles. Je choisis la clarté de LastDayOfMonth_NewMethodWithReuseOfExtMethodsa réutilisation d'une autre méthode d'extension utile. À mon humble avis, son intention est plus claire et je suis prêt à accepter le faible coût de performance.

LastDayOfMonth_SpecialCase suppose que vous fournissez le premier du mois dans le cas particulier où vous avez peut-être déjà calculé cette date et utilise la méthode add avec DateTime.DaysInMonth pour obtenir le résultat. C'est plus rapide que les autres versions, comme vous vous en doutez, mais à moins que vous n'ayez désespérément besoin de vitesse, je ne vois pas l'intérêt d'avoir ce cas spécial dans votre arsenal.

Conclusion

Voici une classe de méthode d'extension avec mes choix et en accord général avec @Steffen je crois:

public static class DateTimeDayOfMonthExtensions
{
    public static DateTime FirstDayOfMonth(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, 1);
    }

    public static int DaysInMonth(this DateTime value)
    {
        return DateTime.DaysInMonth(value.Year, value.Month);
    }

    public static DateTime LastDayOfMonth(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, value.DaysInMonth());
    }
}

Si vous êtes arrivé jusqu'ici, merci pour le temps! C'est amusant: ¬). Veuillez commenter si vous avez d'autres suggestions pour ces algorithmes.


5
Vous avez cependant beaucoup moins de crédit pour vos efforts. C'est utile!
Dion V.

2
Merci @DionV. - c'est agréable d'être apprécié! Les réponses courtes sont excellentes lorsque vous êtes pressé, mais je pense qu'une analyse plus approfondie à suivre est souvent utile.
WooWaaBob

Vous avez manqué une autre alternative: LastDayOfMonth_AddMethod_SpecialCase(ou quelque chose comme ça). En attendant le premier jour du mois comme paramètre, je pense que le plus rapide devrait être ce qui LastDayOfMonth_AddMethodfait! Donc, ce serait aussi simple que:return value.AddMonths(1).AddDays(-1);
Andrew

1
Merci @Andrew et voici mes résultats en utilisant cela: 2835 ms pour LastDayOfMonth_SpecialCase (), et; 4685 ms pour LastDayOfMonth_AddMethod_SpecialCase (). Ce qui est probablement logique lorsque l'on considère le temps de création de structure et que la représentation interne de DateTime fait probablement de l'ajout de jours une opération simple mais l'ajout de mois un algorithme plus complexe.
WooWaaBob

15

Obtenir la plage de mois avec l'API .Net (juste une autre façon):

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));

6

" Last day of month" est en fait " First day of *next* month, minus 1". Voici donc ce que j'utilise, pas besoin de méthode "DaysInMonth":

public static DateTime FirstDayOfMonth(this DateTime value)
{
    return new DateTime(value.Year, value.Month, 1);
}

public static DateTime LastDayOfMonth(this DateTime value)
{
    return value.FirstDayOfMonth()
        .AddMonths(1)
        .AddMinutes(-1);
}

REMARQUE: L'utilisation de I raison AddMinutes(-1), pas AddDays(-1)ici parce que généralement vous avez besoin de ces fonctions de date pour des rapports pour une date, et lorsque vous construisez un rapport pour une période, la « date de fin » devrait effectivement être quelque chose comme Oct 31 2015 23:59:59si votre rapport fonctionne correctement - y compris toutes les données du dernier jour du mois.

C'est-à-dire que vous obtenez le "dernier moment du mois" ici. Pas le dernier jour.

OK, je vais me taire maintenant.


5
DateTime dCalcDate = DateTime.Now;
dtpFromEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, 1);
dptToEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, DateTime.DaysInMonth(dCalcDate.Year, dCalcDate.Month));

Évitez généralement les réponses de code uniquement. Pensez à ajouter un descriptionqui aide à expliquer votre code. Merci
MickyD

3

Ici, vous pouvez ajouter un mois pour le premier jour du mois en cours, puis supprimer 1 jour de ce jour.

DateTime now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);

2

Si vous ne vous souciez que de la date

var firstDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind).AddMonths(1).AddDays(-1);

Si vous voulez gagner du temps

var firstDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind).AddMonths(1).AddDays(-1);

Ça va complètement échouer en décembre dudeeeeeee. il va essayer de créer datetime avec le mois "13" et lever l'exception qu'il n'y a pas une telle date.
Pawel

Depuis quand le 13 décembre? Il y a un total de 12 mois. Quel calendrier utilisez-vous?
Vitaly

Si votre date est décembre, il essaiera d'ajouter un mois, ce qui entraînera une exception. new DateTime (date.Year, 12+ 1, 1, 0, 0, 0, date.Kind) .AddDays (-1); La soustraction d'un jour sera effectuée après la création de datetime, donc en décembre, il échouera lors de la tentative de date pour le mois "13". Tu peux l'essayer.
Pawel

1

La réponse acceptée ici ne prend pas en compte le type de l'instance DateTime. Par exemple, si votre instance DateTime d'origine était de type UTC, en créant une nouvelle instance de DateTime, vous créerez une instance de type inconnu qui sera ensuite traitée comme heure locale en fonction des paramètres du serveur. Par conséquent, la meilleure façon d'obtenir la première et la dernière date du mois serait la suivante:

var now = DateTime.UtcNow;
var first = now.Date.AddDays(-(now.Date.Day - 1));
var last = first.AddMonths(1).AddTicks(-1);

De cette façon, le type d'origine de l'instance DateTime est conservé.


1

Essaye celui-là:

string strDate = DateTime.Now.ToString("MM/01/yyyy");

1

J'ai utilisé cela dans mon script (fonctionne pour moi) mais j'avais besoin d'une date complète sans avoir besoin de la couper à la date et à l'heure.

public DateTime GetLastDayOfTheMonth()
{
    int daysFromNow = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) - (int)DateTime.Now.Day;
    return DateTime.Now.AddDays(daysFromNow);
}

1

Essayez ceci. Il calcule essentiellement le nombre de jours écoulés DateTime.Now, puis en soustrait un et utilise la nouvelle valeur pour trouver le premier du mois en cours. De là, il utilise cela DateTimeet utilise .AddMonths(-1)pour obtenir le premier du mois précédent.

Obtenir le dernier jour du mois dernier fait essentiellement la même chose, sauf qu'il ajoute un à un nombre de jours dans le mois et soustrait cette valeur DateTime.Now.AddDays, vous donnant le dernier jour du mois précédent.

int NumberofDays = DateTime.Now.Day;
int FirstDay = NumberofDays - 1;
int LastDay = NumberofDays + 1;
DateTime FirstofThisMonth = DateTime.Now.AddDays(-FirstDay);
DateTime LastDayOfLastMonth = DateTime.Now.AddDays(-LastDay);
DateTime CheckLastMonth = FirstofThisMonth.AddMonths(-1);

0

Pour la culture perse

PersianCalendar pc = new PersianCalendar();            

var today = pc.GetDayOfMonth(DateTime.Now);
var firstDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddDays(-(today-1)));
var lastDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddMonths(1).AddDays(-today));            
Console.WriteLine("First day "+ firstDayOfMonth);
Console.WriteLine("Last day " + lastDayOfMonth);

0

Tu peux le faire

DateTime dt = DateTime.Now; 
DateTime firstDayOfMonth = new DateTime(dt.Year, date.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

-1

moyen facile de le faire

Begin = new DateTime(DateTime.Now.Year, DateTime.Now.Month,1).ToShortDateString();
End = new DataFim.Text = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month)).ToShortDateString();

Ce code ne se compile pas à cause de "nouveau DataFim.Text".
Wazner

-1
DateTime dCalcDate = DateTime.Now;
var startDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), 1);
var endDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), DateTime.DaysInMonth((Convert.ToInt32(Year)), Convert.ToInt32(Month)));
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.