En C #/.NET, TimeSpan
a TotalDays
, TotalMinutes
, etc., mais je ne peux pas trouver une formule pour la différence des mois totaux. Des jours variables par mois et des années bissextiles continuent à me jeter. Comment puis-je obtenir TotalMonths ?
Edit Désolé de ne pas être plus clair: je sais que je ne peux pas l'obtenir de TimeSpan
mais je pensais qu'utiliser TotalDays
et TotalMinutes
serait un bon exemple pour exprimer ce que je cherchais ... sauf que j'essaie pour obtenir le nombre total de mois.
Exemple: 25 décembre 2009 - 6 octobre 2009 = 2 TotalMonths. Du 6 octobre au 5 novembre correspond à 0 mois. Le 6 novembre, 1 mois. Le 6 décembre, 2 mois
Vous ne pourrez pas obtenir cela à partir d'une TimeSpan
, car un "mois" est une unité de mesure variable. Vous devrez le calculer vous-même et déterminer comment vous voulez que cela fonctionne.
Par exemple, des dates comme July 5, 2009
et August 4, 2009
doivent-elles générer une différence d'un mois ou de zéro mois? Si vous dites que cela devrait en rapporter un, qu’en est-il de July 31, 2009
et August 1, 2009
? Est-ce que ça un mois? S'agit-il simplement de la différence entre les valeurs Month
pour les dates ou est-ce davantage lié à une période de temps réelle? La logique permettant de déterminer toutes ces règles n’est pas anodine. Vous devrez donc déterminer la vôtre et mettre en œuvre l’algorithme approprié.
Si tout ce que vous voulez, c'est simplement une différence dans les mois - en ignorant complètement les valeurs de date - vous pouvez alors utiliser ceci:
public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
}
Notez que cela renvoie une différence relative, ce qui signifie que si rValue
est supérieur à lValue
, la valeur de retour sera négative. Si vous voulez une différence absolue, vous pouvez utiliser ceci:
public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
return Math.Abs((lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year));
}
(Je réalise que c'est une vieille question, mais ...)
C’est relativement pénible à faire en .NET pur. Je recommanderais ma propre bibliothèque { Noda Time } _, qui est particulièrement conçue pour des choses comme celle-ci:
LocalDate start = new LocalDate(2009, 10, 6);
LocalDate end = new LocalDate(2009, 12, 25);
Period period = Period.Between(start, end);
int months = period.Months;
(Il existe d'autres options, par exemple, si vous voulez seulement compter le nombre de mois, même sur plusieurs années, utilisez Period period = Period.Between(start, end, PeriodUnits.Months);
)
Peut-être que vous ne voulez pas connaître les fractions mensuelles; Qu'en est-il de ce code?
public static class DateTimeExtensions
{
public static int TotalMonths(this DateTime start, DateTime end)
{
return (start.Year * 12 + start.Month) - (end.Year * 12 + end.Month);
}
}
// Console.WriteLine(
// DateTime.Now.TotalMonths(
// DateTime.Now.AddMonths(-1))); // prints "1"
Vous devrez commencer par définir ce que vous entendez par TotalMonths.
Une définition simple met un mois à 30,4 jours (365.25/12).
Au-delà de cela, toute définition incluant des fractions semble inutile, et la valeur entière plus commune (mois entiers entre dates) dépend également de règles commerciales non standard.
Vous devez vous en sortir vous-même. La façon dont vous traitez avec les jours d'arrêt à la fin dépendra de l'utilisation que vous souhaitez en faire.
Une méthode consiste à compter le mois puis à corriger les jours à la fin. Quelque chose comme:
DateTime start = new DateTime(2003, 12, 25);
DateTime end = new DateTime(2009, 10, 6);
int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
double daysInEndMonth = (end - end.AddMonths(1)).Days;
double months = compMonth + (start.Day - end.Day) / daysInEndMonth;
J'ai écrit une méthode d'extension très simple sur DateTime
et DateTimeOffset
pour le faire. Je voulais que cela fonctionne exactement comme une propriété TotalMonths
sur TimeSpan
fonctionnerait: c.-à-d. Renvoyer le nombre de mois complets entre deux dates, en ignorant les mois partiels. Comme il est basé sur DateTime.AddMonths()
, il respecte différentes durées de mois et renvoie ce que l’être humain comprendrait comme une période de mois.
(Malheureusement, vous ne pouvez pas l'implémenter en tant que méthode d'extension sur TimeSpan car cela ne permet pas de connaître les dates réelles utilisées et, pendant des mois, elles sont importantes.)
Le code et les tests sont tous deux disponibles sur GitHub . Le code est très simple:
public static int GetTotalMonthsFrom(this DateTime dt1, DateTime dt2)
{
DateTime earlyDate = (dt1 > dt2) ? dt2.Date : dt1.Date;
DateTime lateDate = (dt1 > dt2) ? dt1.Date : dt2.Date;
// Start with 1 month's difference and keep incrementing
// until we overshoot the late date
int monthsDiff = 1;
while (earlyDate.AddMonths(monthsDiff) <= lateDate)
{
monthsDiff++;
}
return monthsDiff - 1;
}
Et il réussit tous ces cas de tests unitaires:
// Simple comparison
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
// 31 Jan to 28 Feb
Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
// Leap year 29 Feb to 29 Mar
Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
// Whole year minus a day
Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
// Whole year
Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
// 29 Feb (leap) to 28 Feb (non-leap)
Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
// 100 years
Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
// Same date
Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
// Past date
Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));
Je le ferais comme ça:
static int TotelMonthDifference(this DateTime dtThis, DateTime dtOther)
{
int intReturn = 0;
dtThis = dtThis.Date.AddDays(-(dtThis.Day-1));
dtOther = dtOther.Date.AddDays(-(dtOther.Day-1));
while (dtOther.Date > dtThis.Date)
{
intReturn++;
dtThis = dtThis.AddMonths(1);
}
return intReturn;
}
Il n’ya pas beaucoup de réponses claires à ce sujet car vous assumez toujours des choses.
Cette solution calcule entre deux dates les mois entre le moment où vous supposez que vous souhaitez enregistrer le jour du mois aux fins de comparaison (ce qui signifie que le jour du mois est pris en compte dans le calcul).
Par exemple, si vous avez une date du 30 janvier 2012 au 29 février 2012, ce ne sera pas un mois, mais le 1er mars 2013.
Il a été testé assez soigneusement, le nettoyera probablement plus tard au fur et à mesure de son utilisation, et prend deux dates au lieu d'un Timespan, ce qui est probablement mieux. J'espère que cela aide quelqu'un d'autre.
private static int TotalMonthDifference(DateTime dtThis, DateTime dtOther)
{
int intReturn = 0;
bool sameMonth = false;
if (dtOther.Date < dtThis.Date) //used for an error catch in program, returns -1
intReturn--;
int dayOfMonth = dtThis.Day; //captures the month of day for when it adds a month and doesn't have that many days
int daysinMonth = 0; //used to caputre how many days are in the month
while (dtOther.Date > dtThis.Date) //while Other date is still under the other
{
dtThis = dtThis.AddMonths(1); //as we loop, we just keep adding a month for testing
daysinMonth = DateTime.DaysInMonth(dtThis.Year, dtThis.Month); //grabs the days in the current tested month
if (dtThis.Day != dayOfMonth) //Example 30 Jan 2013 will go to 28 Feb when a month is added, so when it goes to march it will be 28th and not 30th
{
if (daysinMonth < dayOfMonth) // uses day in month max if can't set back to day of month
dtThis.AddDays(daysinMonth - dtThis.Day);
else
dtThis.AddDays(dayOfMonth - dtThis.Day);
}
if (((dtOther.Year == dtThis.Year) && (dtOther.Month == dtThis.Month))) //If the loop puts it in the same month and year
{
if (dtOther.Day >= dayOfMonth) //check to see if it is the same day or later to add one to month
intReturn++;
sameMonth = true; //sets this to cancel out of the normal counting of month
}
if ((!sameMonth)&&(dtOther.Date > dtThis.Date))//so as long as it didn't reach the same month (or if i started in the same month, one month ahead, add a month)
intReturn++;
}
return intReturn; //return month
}
Vieille question que je connais, mais qui pourrait aider quelqu'un. J'ai utilisé la réponse acceptée de @Adam ci-dessus, mais j'ai ensuite vérifié si la différence était de 1 ou -1, puis vérifiez s'il s'agit d'une différence d'un mois calendaire complet. Ainsi, 21/07/55 et 20/08/55 ne seraient pas un mois complet, mais 21/07/55 et 21/07/55 le seraient.
/// <summary>
/// Amended date of birth cannot be greater than or equal to one month either side of original date of birth.
/// </summary>
/// <param name="dateOfBirth">Date of birth user could have amended.</param>
/// <param name="originalDateOfBirth">Original date of birth to compare against.</param>
/// <returns></returns>
public JsonResult ValidateDateOfBirth(string dateOfBirth, string originalDateOfBirth)
{
DateTime dob, originalDob;
bool isValid = false;
if (DateTime.TryParse(dateOfBirth, out dob) && DateTime.TryParse(originalDateOfBirth, out originalDob))
{
int diff = ((dob.Month - originalDob.Month) + 12 * (dob.Year - originalDob.Year));
switch (diff)
{
case 0:
// We're on the same month, so ok.
isValid = true;
break;
case -1:
// The month is the previous month, so check if the date makes it a calendar month out.
isValid = (dob.Day > originalDob.Day);
break;
case 1:
// The month is the next month, so check if the date makes it a calendar month out.
isValid = (dob.Day < originalDob.Day);
break;
default:
// Either zero or greater than 1 month difference, so not ok.
isValid = false;
break;
}
if (!isValid)
return Json("Date of Birth cannot be greater than one month either side of the date we hold.", JsonRequestBehavior.AllowGet);
}
else
{
return Json("Date of Birth is invalid.", JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
case TipoIntervalo.Mes:
retorno = inicio.AddMonths(-fim.Month).Month.ToString();
break;
case TipoIntervalo.Ano:
retorno = (inicio.Year - fim.Year).ToString();
break;
La réponse acceptée fonctionne parfaitement lorsque vous voulez des mois entiers.
J'ai eu besoin de mois partiels. Voici la solution que je propose depuis des mois et demi:
/// <summary>
/// Calculate the difference in months.
/// This will round up to count partial months.
/// </summary>
/// <param name="lValue"></param>
/// <param name="rValue"></param>
/// <returns></returns>
public static int MonthDifference(DateTime lValue, DateTime rValue)
{
var yearDifferenceInMonths = (lValue.Year - rValue.Year) * 12;
var monthDifference = lValue.Month - rValue.Month;
return yearDifferenceInMonths + monthDifference +
(lValue.Day > rValue.Day
? 1 : 0); // If end day is greater than start day, add 1 to round up the partial month
}
J'avais aussi besoin d'une différence d'un an avec le même besoin pour des années partielles. Voici la solution que j'ai trouvée:
/// <summary>
/// Calculate the differences in years.
/// This will round up to catch partial months.
/// </summary>
/// <param name="lValue"></param>
/// <param name="rValue"></param>
/// <returns></returns>
public static int YearDifference(DateTime lValue, DateTime rValue)
{
return lValue.Year - rValue.Year +
(lValue.Month > rValue.Month // Partial month, same year
? 1
: lValue.Day > rValue.Day // Partial month, same year and month
? 1 : 0);
}
Vous trouverez ci-dessous le moyen le plus précis de procéder. En effet, la définition du terme "1 mois" change en fonction du mois et aucune autre réponse n'en tient compte! Si vous souhaitez plus d’informations sur le problème qui n’est pas intégré au framework, vous pouvez lire ce post: Un objet en temps réel avec .years & .Months (toutefois, la lecture de ce post n’est pas nécessaire pour comprendre et utiliser la fonction ci-dessous, elle fonctionne à 100%, sans les imprécisions inhérentes à l'approximation que d'autres aiment utiliser - et n'hésitez pas à remplacer la fonction .ReverseIt par la fonction intégrée .Reverse que vous pourriez avoir sur votre cadre (c'est juste ici pour être complet ).
Veuillez noter que vous pouvez obtenir un nombre illimité de dates et d'heures, secondes et minutes, ou secondes, minutes et jours, et ce, jusqu'à un nombre d'années (pouvant contenir 6 parties/segments). Si vous spécifiez les deux premiers et qu'il a plus d'un an, il retournera "il y a 1 an et 3 mois" et ne retournera pas le reste car vous avez demandé deux segments. s'il a seulement quelques heures, il ne reviendra que "Il y a 2 heures et 1 minute". Bien sûr, les mêmes règles s’appliquent si vous spécifiez 1, 2, 3, 4, 5 ou 6 segmets (maximum 6, car les secondes, les minutes, les heures, les jours, les mois, les années ne font que 6 types). Il corrigera également les problèmes de grammaire tels que "minutes" ou "minute" selon que le temps est égal ou supérieur à 1 minute, identique pour tous les types, et la "chaîne" générée sera toujours grammaticalement correcte.
Voici quelques exemples d'utilisation: BAllowSegments identifie le nombre de segments à afficher ... c'est-à-dire: si 3, chaîne de retour serait (par exemple) ... "3 years, 2 months and 13 days"
(n'inclut pas les heures, minutes et secondes les 3 premières catégories de temps sont retournées), si toutefois la date était une date plus récente, telle que quelque chose d'il y a quelques jours, spécifier les mêmes segments (3) renverra plutôt "4 days, 1 hour and 13 minutes ago"
, donc tout sera pris en compte!
si bAllowSegments vaut 2, il renverra "3 years and 2 months"
et si 6 (valeur maximale) renverra "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
, mais rappelez-vous qu'il va NEVER RETURN
quelque chose comme ceci "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
car il comprend qu'il n'y a pas de données de date dans les 3 premiers segments et les ignore, même si vous spécifiez 6 segments, alors ne vous inquiétez pas :). Bien sûr, s'il y a un segment contenant 0, il en tiendra compte lors de la formation de la chaîne et s'affichera sous la forme "3 days and 4 seconds ago"
en ignorant la partie "0 heures"! Profitez et s'il vous plaît commenter si vous aimez.
Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
Dim dtNow = DateTime.Now
Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)
rYears = dtNow.Year - dt.Year
rMonths = dtNow.Month - dt.Month
If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
rDays = dtNow.Day - dt.Day
If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
rHours = dtNow.Hour - dt.Hour
If rHours < 0 Then rHours += 24 : rDays -= 1
rMinutes = dtNow.Minute - dt.Minute
If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
rSeconds = dtNow.Second - dt.Second
If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1
' this is the display functionality
Dim sb As StringBuilder = New StringBuilder()
Dim iSegmentsAdded As Int16 = 0
If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1
parseAndReturn:
' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...
If sb.ToString = "" Then sb.Append("less than 1 second")
Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")
End Function
Bien sûr, vous aurez besoin d’une fonction "ReplaceLast", qui prend une chaîne source, un argument spécifiant ce qui doit être remplacé, et un autre argument spécifiant ce que vous voulez remplacer, et ne remplace que la dernière occurrence de cette chaîne. ... J'ai inclus mon exemplaire si vous n'en avez pas un ou si vous ne voulez pas le mettre en œuvre, alors le voici: cela fonctionnera "tel quel", sans aucune modification. Je sais que la fonction reverseit n’est plus nécessaire (existe dans .net), mais les fonctions ReplaceLast et ReverseIt func sont reportées de la période précédant la mise à disposition. Nous vous prions d’excuser de la date (elle fonctionne toujours à 100%). em depuis plus de dix ans, ils peuvent être garantis sans bug) ... :). à votre santé.
<Extension()> _
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String
' let empty string arguments run, incase we dont know if we are sending and empty string or not.
sReplacable = sReplacable.ReverseIt
sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version!
Return sReplacable.ReverseIt.ToString
End Function
<Extension()> _
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String
Dim strTempX As String = "", intI As Integer
If n > strS.Length Or n = -1 Then n = strS.Length
For intI = n To 1 Step -1
strTempX = strTempX + Mid(strS, intI, 1)
Next intI
ReverseIt = strTempX + Right(strS, Len(strS) - n)
End Function
This library calcule la différence en mois, en tenant compte de toutes les parties de DateTime:
// ----------------------------------------------------------------------
public void DateDiffSample()
{
DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
Console.WriteLine( "Date1: {0}", date1 );
// > Date1: 08.11.2009 07:13:59
DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
Console.WriteLine( "Date2: {0}", date2 );
// > Date2: 20.03.2011 19:55:28
DateDiff dateDiff = new DateDiff( date1, date2 );
// differences
Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
// > DateDiff.Years: 1
Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
// > DateDiff.Quarters: 5
Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
// > DateDiff.Months: 16
Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
// > DateDiff.Weeks: 70
Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
// > DateDiff.Days: 497
Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
// > DateDiff.Weekdays: 71
Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
// > DateDiff.Hours: 11940
Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
// > DateDiff.Minutes: 716441
Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
// > DateDiff.Seconds: 42986489
// elapsed
Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
// > DateDiff.ElapsedYears: 1
Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
// > DateDiff.ElapsedMonths: 4
Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
// > DateDiff.ElapsedDays: 12
Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
// > DateDiff.ElapsedHours: 12
Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
// > DateDiff.ElapsedMinutes: 41
Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
// > DateDiff.ElapsedSeconds: 29
} // DateDiffSample
Le problème avec les mois est que ce n'est pas vraiment une mesure simple - ils ne sont pas de taille constante. Vous devez définir vos règles pour ce que vous voulez inclure et travailler à partir de là. Par exemple, du 1 er janvier au 1 er février - vous pouvez affirmer que 2 mois y sont impliqués, ou vous pouvez dire que c'est un mois. Alors que dire de "1 janvier 20:00" à "1 février 00:00" - ce n'est pas tout à fait un mois entier. Est-ce 0? 1? qu'en est-il de l'inverse (1er janvier 00h00 au 1er février 20h00) ... 1? 2?
Définissez d'abord les règles, ensuite vous devrez le coder vous-même, j'ai bien peur ...
Si vous voulez avoir un résultat 1
entre 28th Feb
et 1st March
:
DateTime date1, date2;
int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month
Bien tard dans le jeu, mais j'imagine que cela peut être utile à quelqu'un. La majorité des gens ont tendance à mesurer mois par mois en excluant le fait que les mois présentent des variations différentes. En utilisant ce cadre de pensée, j'ai créé une ligne qui compare les dates pour nous. En utilisant le processus suivant.
Si l'année de fin n'est pas supérieure, nous obtenons les mêmes performances que 2A/2B, mais sans additionner les 12 mois car nous n'avons pas besoin d'évaluer autour de l'année.
DateTime date = new DateTime(2003, 11, 25);
DateTime today = new DateTime(2004, 12, 26);
var time = (today.Year - date.Year > 1 ? (today.Year - date.Year - 1) * 12 : 0) + (today.Year > date.Year ? (today.Day >= date.Day ? today.Month + 12 - date.Month : today.Month + 12 - (date.Month + 1)) : (today.Day >= date.Day ? today.Month - date.Month : today.Month - (date.Month + 1)));
Calculez le nombre de mois entre 2 dates:
$date1 = '2017-01-20';
$date2 = '2019-01-20';
$ts1 = strtotime($date1);
$ts2 = strtotime($date2);
$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);
$month1 = date('m', $ts1);
$month2 = date('m', $ts2);
echo $joining_months = (($year2 - $year1) * 12) + ($month2 - $month1);
Si vous souhaitez connaître le nombre exact, vous ne pouvez pas uniquement tenir compte de la période, car vous devez savoir à quels mois vous avez affaire et si vous avez affaire à une année bissextile, comme vous l'avez dit.
Soit optez pour un nombre approximatif, soit optez pour une modification des DateTimes d'origine
La méthode retourne une liste qui contient 3 éléments, en premier lieu année, en second mois et fin élément en jour:
public static List<int> GetDurationInEnglish(DateTime from, DateTime to)
{
try
{
if (from > to)
return null;
var fY = from.Year;
var fM = from.Month;
var fD = DateTime.DaysInMonth(fY, fM);
var tY = to.Year;
var tM = to.Month;
var tD = DateTime.DaysInMonth(tY, tM);
int dY = 0;
int dM = 0;
int dD = 0;
if (fD > tD)
{
tM--;
if (tM <= 0)
{
tY--;
tM = 12;
tD += DateTime.DaysInMonth(tY, tM);
}
else
{
tD += DateTime.DaysInMonth(tY, tM);
}
}
dD = tD - fD;
if (fM > tM)
{
tY--;
tM += 12;
}
dM = tM - fM;
dY = tY - fY;
return new List<int>() { dY, dM, dD };
}
catch (Exception exception)
{
//todo: log exception with parameters in db
return null;
}
}
DateTime start = new DateTime(2003, 12, 25);
DateTime end = new DateTime(2009, 10, 6);
int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
double daysInEndMonth = (end - end.AddMonths(1)).Days;
double months = compMonth + (start.Day - end.Day) / daysInEndMonth;
http://www.astro.uu.nl/~strous/AA/en/reken/juliaansedag.html
Si vous pouvez convertir l'heure d'une date grégorienne en le nombre de jours du calendrier Julien , vous pouvez simplement créer un opérateur pour comparer le nombre de jours zulian, qui peut être saisi en double pour obtenir des mois, des jours, des secondes, etc. Consultez le lien ci-dessus pour un algorithme de conversion de Grégorien en Julian.
Il n'y a pas de manière intégrée de le faire avec précision dans idiomatic-c #. Il existe certaines solutions de contournement, telles que cet exemple CodeProject que les utilisateurs ont cependant codé.
Voici ma contribution pour obtenir la différence en mois que j'ai trouvé exacte:
namespace System
{
public static class DateTimeExtensions
{
public static Int32 DiffMonths( this DateTime start, DateTime end )
{
Int32 months = 0;
DateTime tmp = start;
while ( tmp < end )
{
months++;
tmp = tmp.AddMonths( 1 );
}
return months;
}
}
}
Usage:
Int32 months = DateTime.Now.DiffMonths( DateTime.Now.AddYears( 5 ) );
Vous pouvez créer une autre méthode appelée DiffYears et appliquer exactement la même logique que ci-dessus et AddYears au lieu de AddMonths dans la boucle while.
Si vous avez des mois et des années, vous avez besoin de quelque chose qui sache combien de jours chaque mois et quelles années sont des années bissextiles.
Entrez le Calendrier grégorien (et autres implémentations spécifiques à la culture Calendrier ).
Alors que Calendar ne fournit pas de méthodes pour calculer directement la différence entre deux instants, il existe des méthodes telles que
DateTime AddWeeks(DateTime time, int weeks)
DateTime AddMonths(DateTime time, int months)
DateTime AddYears(DateTime time, int years)