web-dev-qa-db-fra.com

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

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

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

var maxDay = dtpAttendance.MaxDate.Day;

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

DateTime dt = DateTime.today;

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

149
CAD

DateTime structure stocke une seule valeur, pas une plage de valeurs. MinValue et MaxValue sont des champs statiques, qui contiennent une plage de valeurs possibles pour les instances de la structure DateTime. Ces champs sont statiques et ne concernent pas une instance particulière de DateTime. Ils se rapportent au type DateTime lui-même.

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

UPDATE: Obtenir la plage de mois:

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

C'est plus un long commentaire sur les réponses de @Sergey et de @ Steffen. Ayant moi-même écrit un code similaire dans le passé, j'ai décidé de vérifier ce qui était le plus { performant } _ tout en gardant à l'esprit que la clarté est également importante.

Résultat

Voici un exemple de résultat d'exécution 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é factorisé en tant que 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 beaucoup dedans, le FirstDayOfMonth_AddMethod était légèrement plus rapide que FirstDayOfMonth_NewMethod dans la plupart des exécutions du test. Cependant, je pense que le dernier a une intention légèrement plus claire et donc j'ai une préférence pour cela.

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

LastDayOfMonth_SpecialCase suppose que vous fournissez le premier jour du mois dans le cas spécial où vous avez peut-être déjà calculé cette date et qu'il utilise la méthode add avec DateTime.DaysInMonth pour obtenir le résultat. C’est plus rapide que les autres versions, comme on peut s’y attendre, mais à moins que vous n’ayez un besoin pressant de vitesse, je ne vois pas l’intérêt d’avoir ce cas particulier 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 avez si loin, merci pour le temps! Son été amusant: ¬). Veuillez commenter si vous avez d'autres suggestions pour ces algorithmes.

71
WooWaaBob

Obtenir une 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));
12
Steffen Mangold

"Last day of month" est en réalité "First day of *next* month, minus 1". Alors voici ce que j'utilise, pas besoin de la 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: La raison pour laquelle j’utilise AddMinutes(-1), et non AddDays(-1), c’est parce que vous avez généralement besoin de ces fonctions de date pour le reporting pour une période donnée, et lorsque vous créez un rapport pour une période, la "date de fin" doit être en fait quelque chose comme Oct 31 2015 23:59:59 pour que votre rapport fonctionne correctement, y compris toutes les données du dernier jour du mois.

C'est à dire. vous obtenez réellement le "dernier moment du mois" ici. Pas le dernier jour.

OK, je vais me taire maintenant.

4
jazzcat
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));
4
sphaze

Ici, vous pouvez ajouter un mois pour le premier jour du mois en cours que supprimer un jour à partir de ce jour.

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

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 tu veux 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);
2
Vitaly

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 un type UTC, créez une nouvelle instance DateTime pour créer une instance Type inconnu, qui sera ensuite traitée comme une heure locale en fonction des paramètres du serveur. Par conséquent, le moyen le plus approprié d’obtenir la première et la dernière date du mois serait le suivant:

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 préservé.

1
Marko

Je l'ai utilisé dans mon script (fonctionne pour moi) mais j'avais besoin d'une date complète sans qu'il soit nécessaire de le réduire à 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
Maarten Frouws

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
Mohammad Daliri

Essaye celui-là:

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