[英]C# calculate accurate age
任何人都知道如何根据日期(生日)获取年龄
我在想这样的事情
string age = DateTime.Now.GetAccurateAge();
输出将类似于 20Years 5Months 20Days
public static class DateTimeExtensions
{
public static string ToAgeString(this DateTime dob)
{
DateTime today = DateTime.Today;
int months = today.Month - dob.Month;
int years = today.Year - dob.Year;
if (today.Day < dob.Day)
{
months--;
}
if (months < 0)
{
years--;
months += 12;
}
int days = (today - dob.AddMonths((years * 12) + months)).Days;
return string.Format("{0} year{1}, {2} month{3} and {4} day{5}",
years, (years == 1) ? "" : "s",
months, (months == 1) ? "" : "s",
days, (days == 1) ? "" : "s");
}
}
请参阅如何在 C# 中计算某人的年龄? 对于想法。
不确定它是否总是正确的(还没有考虑过是否存在闰年等可能使其失败的情况......),但这是一种摆脱年份和月份的简单方法:
DateTime bd = DateTime.Parse("2009-06-17");
TimeSpan ts = DateTime.Now.Subtract(bd);
DateTime age = DateTime.MinValue + ts;
string s = string.Format("{0} Years {1} months {2} days", age.Year -1 , age.Month - 1, age.Day - 1);
由于我无法将代码发布到评论中,这里是基于@LukeH 答案的代码,并修复了错误
public static int GetAge( DateTime dob, DateTime today, out int days, out int months ) {
DateTime dt = today;
if( dt.Day < dob.Day ) {
dt = dt.AddMonths( -1 );
}
months = dt.Month - dob.Month;
if( months < 0 ) {
dt = dt.AddYears( -1 );
months += 12;
}
int years = dt.Year - dob.Year;
var offs = dob.AddMonths( years * 12 + months );
days = (int)( ( today.Ticks - offs.Ticks ) / TimeSpan.TicksPerDay );
return years;
}
我的答案不完全是答案; 这是在此主题和类似主题中找到答案的一种方式。 LukeH已经提供了正确答案,我这里的 2 美分是给任何想知道哪个是更正确答案的人*。
*更正确,因为正如你在散布的一些讨论和评论中看到的那样,我们必须在闰年妥协一些先决条件 - 正常年份的 dob 是 1 月 1 日还是 2 月 28 日?
我正在使用这个和其他这样做的网站作为基准,我的大脑也是 ;)
我在这里实现了 LukeH、@Panos Theof 和 @xr280xr 的答案:
public static class DateTimeExtensions
{
public static int HowOld(this DateTime initialDay, DateTime dayToCalculate, out int days, out int months)
{
//https://stackoverflow.com/a/3055445/2752308
//LukeH: essentially right
months = dayToCalculate.Month - initialDay.Month;
int years = dayToCalculate.Year - initialDay.Year;
if (dayToCalculate.Day < initialDay.Day)
{
months--;
}
if (months < 0)
{
years--;
months += 12;
}
days = (dayToCalculate - initialDay.AddMonths((years * 12) + months)).Days;
Console.WriteLine(
$"{years} year{((years == 1) ? "" : "s")}, {months} month{((months == 1) ? "" : "s")} and {days} day{((days == 1) ? "" : "s")}");
return years;
}
public static int HowOld2(this DateTime initialDay, DateTime dayToCalculate, out int days, out int months)
{
//@Panos Theof: wrong
months = dayToCalculate.Month - initialDay.Month;
int years = dayToCalculate.Year - initialDay.Year;
if (dayToCalculate.Day < initialDay.Day)
{
dayToCalculate = dayToCalculate.AddMonths(-1);
}
if (months < 0)
{
dayToCalculate = dayToCalculate.AddYears(-1);
months += 12;
}
years = dayToCalculate.Year - initialDay.Year;
var offs = initialDay.AddMonths(years * 12 + months);
days = (int)((dayToCalculate.Ticks - offs.Ticks) / TimeSpan.TicksPerDay);
Console.WriteLine(
$"{years} year{((years == 1) ? "" : "s")}, {months} month{((months == 1) ? "" : "s")} and {days} day{((days == 1) ? "" : "s")}");
return years;
}
public static int HowOld3(this DateTime initialDay, DateTime dayToCalculate, out int days, out int months)
{
//@xr280xr: wrong
//Get the relative difference between each date part
days = dayToCalculate.Day - initialDay.Day;
months = dayToCalculate.Month - initialDay.Month;
int years = dayToCalculate.Year - initialDay.Year;
if (days < 0)
{
days = DateTime.DaysInMonth(initialDay.Year, initialDay.Month) - initialDay.Day + //Days left in month of birthday +
dayToCalculate.Day; //Days passed in dayToCalculate's month
months--; //Subtract incomplete month that was already counted
}
if (months < 0)
{
months += 12; //Subtract months from 12 to convert relative difference to # of months
years--; //Subtract incomplete year that was already counted
}
Console.WriteLine(string.Format("{0} year{1}, {2} month{3} and {4} day{5}",
years, (years == 1) ? "" : "s",
months, (months == 1) ? "" : "s",
days, (days == 1) ? "" : "s"));
return years;
}
}
使用 VS2019 和 XUnit 我创建了一个 Inlinedata 生成器类:
public class CalculatorTestData : IEnumerable<object[]>
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new DateTime(1966, 7, 27), new DateTime(2020, 7, 26), 53, 11, 29 };
yield return new object[] { new DateTime(1966, 7, 27), new DateTime(2020, 7, 27), 54, 0, 0 };
yield return new object[] { new DateTime(1966, 7, 27), new DateTime(2020, 7, 28), 54, 0, 1 };
yield return new object[] { new DateTime(1968, 2, 29), new DateTime(2020, 2, 28), 51, 11, 30 };
yield return new object[] { new DateTime(1968, 2, 29), new DateTime(2020, 2, 29), 52, 0, 0 };
yield return new object[] { new DateTime(1968, 2, 29), new DateTime(2020, 3, 01), 52, 0, 1 };
yield return new object[] { new DateTime(2016, 2, 29), new DateTime(2017, 2, 28), 0, 11, 30 };
}
IEnumerator<object[]> IEnumerable<object[]>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
并设置三种方法:
[Theory]
[ClassData(typeof(CalculatorTestData))]
public void TestHowOld(DateTime initialDay, DateTime dayToCalculate,int expectedYears, int expectedMonths, int expectedDays)//, out int days, out int months
{
//LukeH: essentially right
int resultMonths, resultDays;
int age = initialDay.HowOld(dayToCalculate,out resultDays,
out resultMonths); //https://stackoverflow.com/questions/28970265/how-to-test-method-with-out-parameters
Assert.Equal(age, expectedYears);
Assert.Equal(resultMonths, expectedMonths);
Assert.Equal(resultDays, expectedDays);
}
[Theory]
[ClassData(typeof(CalculatorTestData))]
public void TestHowOld2(DateTime initialDay, DateTime dayToCalculate, int expectedYears, int expectedMonths, int expectedDays)//, out int days, out int months
{
//@Panos Theof: wrong
int resultMonths, resultDays;
int age = initialDay.HowOld2(dayToCalculate, out resultDays,
out resultMonths); //https://stackoverflow.com/questions/28970265/how-to-test-method-with-out-parameters
Assert.Equal(age, expectedYears);
Assert.Equal(resultMonths, expectedMonths);
Assert.Equal(resultDays, expectedDays);
}
[Theory]
[ClassData(typeof(CalculatorTestData))]
public void TestHowOld3(DateTime initialDay, DateTime dayToCalculate, int expectedYears, int expectedMonths, int expectedDays)//, out int days, out int months
{
//@xr280xr: wrong
int resultMonths, resultDays;
int age = initialDay.HowOld3(dayToCalculate, out resultDays,
out resultMonths); //https://stackoverflow.com/questions/28970265/how-to-test-method-with-out-parameters
Assert.Equal(age, expectedYears);
Assert.Equal(resultMonths, expectedMonths);
Assert.Equal(resultDays, expectedDays);
}
当然,预期的结果是在 InlineData 类上。
输出结果为:
所以,LukeH 有正确的答案。 而且,令我惊讶的是,两个站点都不同意跳跃 DOB,恕我直言 Calculator.net 是正确的,而 timeanddate.com 显然是错误的,产生了以下错误输出:
对于 DOB 2 月 29 日:
28/02 和 29/02 都会产生 52, 0, 0 (?!)
01/03 产生 52, 0, 2 (!!!???)
2016 年 2 月 29 日 => 2017 年 2 月 28 日 => 1 年、0 米、1 天(!!!???)
希望显示测试设置可以帮助某人。
这是我使用的:
public static int GetAge(DateTime dateOfBirth)
{
int age = DateTime.Now.Year - dateOfBirth.Year;
if (dateOfBirth.AddYears(age) > DateTime.Now)
{
age = age - 1;
}
return age;
}
为了获得年龄,我将使用生日的日期时间并找到它与当前系统时间之间的差异。 此链接显示了如何查找两个日期时间之间的差异。 只需将开始时间设为用户的生日,将结束时间设为现在 (DateTime.Now;)
这听起来像是一个很好的练习,可以更好地了解TimeSpan 类。
试试这个吧。 它在两个日期之间的所有日子循环,并在途中计算天,月和年。 这就是我需要的。
public static class DateTimeExtension
{
/// <summary>
/// Returns day, month, and year by subtracting date2 from date1
/// </summary>
/// <param name="date1">the date from which date2 will be subtracted</param>
/// <param name="date2">this date will be subtracted from date1</param>
/// <returns></returns>
public static string GetAge(this DateTime date1, DateTime date2)
{
int y = 0, m = 0, d = 0;
for (DateTime i = date2; i < date1; i = i.AddDays(1))
{
d++;
if (d >= DateTime.DaysInMonth(i.Year, i.Month))
{
d = 0;
m++;
}
if (m >= 12)
{
y++;
m = 0;
}
}
return "Time-span: " + y + " Year(s), " + m + " Month(s), " + d + " Day(s)";
}
}
使用方法:
Console.WriteLine(new DateTime(2018, 4, 24).GetAge(new DateTime(2018,1,3)));
//Time-span: 0 Year(s), 3 Month(s), 20 Day(s)
Console.WriteLine(new DateTime(2010, 4, 24).GetAge(new DateTime(2000, 7, 23)));
//Time-span: 9 Year(s), 9 Month(s), 2 Day(s)
@if (Model.CF_DateOfBirth.HasValue)
{
var today = DateTime.Today;
var months = DateTime.Today;
var age = today.Year - Model.CF_DateOfBirth.Value.Year;
var mons = months.Month - Model.CF_DateOfBirth.Val`enter code here`ue.Month;
if (Model.CF_DateOfBirth > today.AddYears(-age) && Model.CF_DateOfBirth>months.AddMonths(-mons))
{
age--; mons--;
}
<i class="" style="color:cadetblue"> Date of birth: </i><b style="color:teal">@Convert.ToDateTime(Model.CF_DateOfBirth).ToShortDateString().ToString()</b> <span> ( <b>@age</b>Years <b>@mons</b> Months)</span>
}
我觉得这个最准确。
private int GetAge(DateTime birthDate)
{
TimeSpan ageTimeSpan = DateTime.UtcNow.Subtract(birthDate);
int age = new DateTime(ageTimeSpan.Ticks).Year;
return age;
}
我注意到 LukeH 的回答和闰年的特殊性。 最简单的例子可能是一个dob = 2/29/2016
作为2/28/2017
和3/1/2017
。 在我看来,2016 年 2 月已经没有天数了,中间有 11 个完整的月份(3 月至 1 月),到目前为止,2017 年 2 月有 28 天,因此截至2/28/2017
,我会称这个人为 11 个月 28 天。 截至3/1/2017
3 月 1 日 1 岁 1 天。 (虽然一些闰日婴儿确实会在平常年的 28 日庆祝……我很好奇法律上的考虑是什么。)
由于LukeH的方法使得使用DateTime.AddMonths
,它计算11个月30天,因为它发现之间的天差2/28/2017
和1/29/2017
。 因此,它计算出2/28/2017
是2/29/2016
后 30 天后的 11 个月,以及3/1/2016
后 27 天后的 11 个月。 生日相差 3 天,仅相隔 1 天。 如果像我“手工”那样得出 28 天,他们就会有一天的年龄差异。
我不太确定如何描述方法的根本区别,但这是我的尝试。 似乎总是有日期的问题,所以请仔细检查。
internal static void CalculateAge(DateTime dateOfBirth, DateTime asOfDate, out int years, out int months, out int days)
{
Console.Write("As of " + asOfDate.ToShortDateString() + ": ");
//Get the relative difference between each date part
days = asOfDate.Day - dateOfBirth.Day;
months = asOfDate.Month - dateOfBirth.Month;
years = asOfDate.Year - dateOfBirth.Year;
if (days < 0)
{
days = DateTime.DaysInMonth(dateOfBirth.Year, dateOfBirth.Month) - dateOfBirth.Day + //Days left in month of birthday +
asOfDate.Day; //Days passed in asOfDate's month
months--; //Subtract incomplete month that was already counted
}
if (months < 0)
{
months += 12; //Subtract months from 12 to convert relative difference to # of months
years--; //Subtract incomplete year that was already counted
}
Console.WriteLine(string.Format("{0} year{1}, {2} month{3} and {4} day{5}",
years, (years == 1) ? "" : "s",
months, (months == 1) ? "" : "s",
days, (days == 1) ? "" : "s"));
}
如果您有一个名为 DateOfBirth 的实例变量/属性,则可以使用此方法。 否则,您可以将出生日期作为方法的参数传递。 我做了一些微积分,我证明这永远不会失败。
public int AgeCalc(){
DateTime now = DateTime.Now;
double age = Math.Floor((DateTime.Now - DateOfBirth).TotalDays)/((DateTime.IsLeapYear(year: now.Year)? 366 : 365));
return (age % 1) >= 0.951 ? Math.Round(age) : Math.Floor(age);
}
我希望这可以帮助你:)
对于 2016 年 2 月 29 日出生的人,在 2017 年 3 月 1 日他们的年龄输出:
年数:1
月数:1(二月 28 天)
天数:0
var today = new DateTime(2020,11,4);
//var today = DateTime.Today;
// Calculate the age.
var years = today.Year - dateOfBirth.Year;
// Go back to the year in which the person was born in case of a leap year
if (dateOfBirth.Date > today.AddYears(-years))
{
years--;
}
var months = today.Month - dateOfBirth.Month;
// Full month hasn't completed
if (today.Day < dateOfBirth.Day)
{
months--;
}
if (months < 0)
{
months += 12;
}
Years = years;
Months = months;
Days = (today - dateOfBirth.AddMonths((years * 12) + months)).Days;
我想把它放到第二个,因此想出了一些更精确的东西(也许有点过度设计)。
这使用反射从 DateTime 对象中提取字段,然后迭代提供的每个字段,计算用于相关单位的正确数量。
/// <summary>
/// For calculating age
/// </summary>
/// <param name="dob">Date of birth</param>
/// <returns>Age in text format</returns>
public static string CalculateAge(DateTime dob)
{
// Initialise now and list of properties to retrieve for calculating age
var now = DateTime.Now;
var propertyNames = new List<string>()
{
"Second",
"Minute",
"Hour",
"Day",
"Month",
"Year"
};
// Get the properties and order them in accordance to smallest unit
// Important to do so as you perform standard subtraction starting with the smallest unit
var properties = typeof(DateTime)
.GetProperties()
.Where(x => propertyNames.Contains(x.Name))
.OrderBy(x => propertyNames.FindIndex(y => y == x.Name));
// Create string builder to build age as a string
var returnVal = new StringBuilder();
foreach(var property in properties)
{
// Calculate the amount for the current property
var amount = (int)property.GetValue(now) - (int)property.GetValue(dob);
if (amount < 0)
switch(property.Name)
{
case "Month":
amount += 12;
dob = dob.AddYears(-1);
break;
case "Day":
amount += DateTime.DaysInMonth(dob.Year, dob.Month);
dob = dob.AddMonths(-1);
break;
case "Hour":
amount += 24;
dob = dob.AddDays(-1);
break;
case "Minute":
amount += 60;
dob = dob.AddHours(-1);
break;
case "Second":
amount += 60;
dob = dob.AddMinutes(-1);
break;
}
// Write to the stringbuilder
returnVal.Insert(0, property.Name + "s: " + amount + ", ");
}
// Trim final ', ' at the end
return returnVal.ToString().TrimEnd().TrimEnd(',');
}
这使您可以达到您想要的任何精度(如果您希望年份在西方文化中计算年龄是正确的,那么您至少需要与当天一样精确)
此外,我刚刚在开始时手动初始化了列表,但是如果这是您的解决方案所需要的,您当然可以将其设置为可配置,但特别是列表必须从最低单位到最高单位排序(并且值必须特别指到 DateTime 的属性名称)。 如果您想在令人担忧的情况下验证这一点,配置值以错误的顺序设置为列表,您可以初始化一个包含所有 DateTime 字段的列表,然后拉出您的可配置列表,并在我获得时使用相同的 LINQ属性使用反射对传递的列表进行排序。
除了稍微复杂的代码之外,我喜欢我的解决方案的一点是它的运行方式很清晰,并且非常灵活。 也许唯一的问题是 switch 语句应该以相反的顺序排列,这样更容易理解。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.