我想计算出生日期的人的年龄以及相对于当前日期的年,月和日的当前日期。
例如:
>>> calculate_age(2008, 01, 01)
1 years, 0 months, 16 days
任何指向执行此操作的算法的指针都将受到赞赏。
答案 0 :(得分:16)
首先,请注意这样的假设:例如,如果您出生在2月1日然后在3月1日,那么您正好是1个月大,即使您的年龄仅为28天 - 小于平均一个月。年份也有可变长度(由于闰年),这意味着您的生日年龄通常不是完整的整数年。如果你想用几年/几个月/几天来表达你活着的确切时间,请参阅我的另一个答案。但更有可能你想恰到好处地捏造它,以便在2月1日出生意味着每年2月1日你是X年,0个月和0天,并且在任何一个月的第一天你是X年,Y个月,和0天。
在这种情况下,请继续阅读。 (注意:以下内容仅适用于过去的日期。)
根据出生日期(y,m,d)
,当前日期(ynow,mnow,dnow)
以及提供unix/epoch time for a given date的函数tm()
,以下内容将输出3个元素列表给予年龄{年,月,日}:
t0 = y*12 + m - 1; # total months for birthdate.
t = ynow*12 + mnow - 1; # total months for Now.
dm = t - t0; # delta months.
if(dnow >= d) return [floor(dm/12), mod(dm,12), dnow-d];
dm--; t--;
return [floor(dm/12), mod(dm,12),
(tm({ynow,mnow,dnow}) - tm({floor(t/12), mod(t,12)+1, d}))/60/60/24];
如果您不喜欢所有楼层和模组,则以下是等效算法。但我认为上述情况更好。首先,它避免在不需要时调用tm()
。
{yl, ml} = {ynow, mnow};
if(mnow < m || mnow == m && dnow < d) yl--;
if(dnow < d) ml--;
years = yl - y;
months = ml + 12*(ynow - yl) - m;
yl = ynow;
if(ml == 0) { ml = 12; yl--; }
days = (tm({ynow, mnow, dnow}) - tm({yl, ml, d}))/60/60/24;
return [years, months, days];
答案 1 :(得分:4)
这不是一个简单的问题,因为上面几天(如果我们不考虑leap-seconds),就没有简单的公式。
月份可以包括28天,29天,30天或31天;年可以是365天或366天。因此,当您尝试计算数月和数年的完整时间单位时会出现问题。
这是一个interesting article,它考虑了用Java解决问题的所有复杂方面。
答案 2 :(得分:3)
其他人有正确的想法 - 你只需要能够将其扩展到其他语言。许多计算机系统从特定点(“纪元”或“参考日期”)开始以秒为单位计算时间,并从那里计算出日期,并提供从秒到日期转换的方法。您应该能够以秒为单位获取当前时间,将出生日期从纪元转换为秒,减去这两个值,然后将其除以一天中的秒数:
int timenow = time(0);
struct tm birthday = { tm_mday = 16 , .tm_mon = 1 , .tm_year = 1963 };
int timebirth = mktime( &birthday_tm );
int diff_sec = timenow - timebirth;
int days = diff_sec / ( 24 * 60 * 60);
printf("%d - %d = %d seconds, or %d days\n", timenow, timebirth, diff_sec, days);
这个特定代码中的错误是mktime()无法处理在基于Unix的系统中1970年1月1日之前的日期。其他系统也会存在类似的问题,具体取决于他们如何计算时间。一般算法应该有效,你只需知道你对特定系统的限制。
答案 3 :(得分:2)
如果你愿意违反“我应该在我生日那年的整数年”的原则,这是一种方法。请注意,由于闰年,该原则并不严格,因此以下实际上更准确,但可能不太直观。如果你想知道某人的实际确切年数,这就是我的方式。
首先将生日和当前时间转换为纪元时间(自时间开始以来的秒数,即1970年在unix土地上)并减去它们。例如,请参阅此问题:How do I convert a date/time to epoch time (aka unix time -- seconds since 1970) in Perl?。您现在拥有该人的确切年龄(以秒为单位),需要将其转换为“36年,3个月,8天”之类的字符串。
这是伪代码。删除几周或几小时或任何你不想要的部分应该是直截了当的......
daysInYear = 365.2425; # Average lengh of a year in the gregorian calendar.
# Another candidate for len of a year is a sidereal year,
# 365.2564 days. cf. http://en.wikipedia.org/wiki/Year
weeksInYear = daysInYear / 7;
daysInMonth = daysInYear / 12;
weeksInMonth = daysInMonth / 7;
sS = 1;
mS = 60*sS;
hS = 60*mS;
dS = 24*hS;
wS = 7*dS;
oS = daysInMonth*dS;
yS = daysInYear*dS;
# Convert a number of seconds to years,months,weeks,days,hrs,mins,secs.
seconds2str[secs]
{
local variables:
y, yQ= False, o, oQ= False, w, wQ= False,
d, dQ= False, h, hQ= False, m, mQ= False, s= secs;
if(secs<0) return "-" + seconds2str(-secs); # "+" is string concatenation.
y = floor(s/yS);
if(y>0) yQ = oQ = wQ = dQ = hQ = mQ = True;
s -= y * yS;
o = floor(s/oS);
if(o>0) oQ = wQ = dQ = hQ = mQ = True;
s -= o * oS;
w = floor(s/wS);
if(w>0) wQ = dQ = hQ = mQ = True;
s -= w * wS;
d = floor(s/dS);
if(d>0) dQ = hQ = mQ = True;
s -= d * dS;
h = floor(s/hS);
if(h>0) hQ = mQ = True;
s -= h * hS;
m = floor(s/mS);
if(m>0) mQ = True;
s -= m * mS;
return
(yQ ? y + " year" + maybeS(y) + " " : "") +
(oQ ? o + " month" + maybeS(o) + " " : "") +
(wQ ? w + " week" + maybeS(w) + " " : "") +
(dQ ? d + " day" + maybeS(d) + " " : "") +
(hQ ? dd(h) + ":" : "") +
(mQ ? dd(m) + ":" + dd(round(s)) + "s" : s + "s");
}
# Returns an "s" if n!=1 and "" otherwise.
maybeS(n) { return (n==1 ? "" : "s"); }
# Double-digit: takes a number from 0-99 and returns it as a 2-character string.
dd(n) { return (n<10 ? "0" + tostring(n) : tostring(n)); }
答案 4 :(得分:1)
由于多年,几个月甚至几天的长度不均匀,因此您无法从减去两个日期开始,以获得简单的持续时间。如果您希望结果与人类如何处理日期相匹配,则您必须使用您的语言内置函数来处理实际日期,这些函数比以往更好地理解日期。
如何编写该算法取决于您使用的语言,因为每种语言都有不同的功能集。我自己在Java中的实现,使用尴尬但技术上正确的GregorianCalendar:
Term difference (Date from, Date to) {
GregorianCalendar cal1 = new GregorianCalendar();
cal1.setTimeInMillis(from.getTime());
GregorianCalendar cal2 = new GregorianCalendar();
cal2.setTimeInMillis(to.getTime());
int years = cal2.get(Calendar.YEAR) - cal1.get(Calendar.YEAR);
int months = cal2.get(Calendar.MONTH) - cal1.get(Calendar.MONTH);
int days = cal2.get(Calendar.DAY_OF_MONTH) - cal1.get(Calendar.DAY_OF_MONTH);
if (days < 0) {
months--;
days += cal1.getActualMaximum(Calendar.DAY_OF_MONTH);
}
if (months < 0) {
years--;
months += 12;
}
return new Term(years, months, days);
}
这可能不完美,但它提供了人类可读的结果。 Term是我自己用于存储人类时期的类,因为Java的日期库没有。
对于未来的项目,我打算转移到更好的Joda日期/时间库,里面有一个Period类,可能需要重写这个函数。
答案 5 :(得分:1)
我认为问题比一种编程语言更广泛。 如果您使用的是C#,则可以使用DateTimeOffset来计算它。
var offset = DateTimeOffset.MinValue;
var lastDate = DateTime.Today;
var firstDate = new DateTime(2006,11,19);
var diff = (lastDate- firstDate);
var resultWithOffset = DateTimeOffset.MinValue.AddDays(diff.TotalDays);
var result = resultWithOffset.AddDays(-offset.Day).AddMonths(-offset.Month).AddYears(-offset.Year);
result.Day
,result.Month
,result.Year
将保留您需要的信息。
答案 6 :(得分:0)
这是使用数学的借用规则。
DateTime dateOfBirth = new DateTime(2000, 4, 18);
DateTime currentDate = DateTime.Now;
int ageInYears = 0;
int ageInMonths = 0;
int ageInDays = 0;
ageInDays = currentDate.Day - dateOfBirth.Day;
ageInMonths = currentDate.Month - dateOfBirth.Month;
ageInYears = currentDate.Year - dateOfBirth.Year;
if (ageInDays < 0)
{
ageInDays += DateTime.DaysInMonth(currentDate.Year, currentDate.Month);
ageInMonths = ageInMonths--;
if (ageInMonths < 0)
{
ageInMonths += 12;
ageInYears--;
}
}
if (ageInMonths < 0)
{
ageInMonths += 12;
ageInYears--;
}
Console.WriteLine("{0}, {1}, {2}", ageInYears, ageInMonths, ageInDays);
答案 7 :(得分:0)
斯威夫特实施的“诡计”回答。
PS。而不是(y,m,d)(ynow,mnow,dnow)作为输入,我使用两个NSDate,这在现实世界中可能更方便。
extension NSDate {
convenience init(ageDateString:String) {
let dateStringFormatter = NSDateFormatter()
dateStringFormatter.dateFormat = "yyyy-MM-dd"
dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
let d = dateStringFormatter.dateFromString(ageDateString)!
self.init(timeInterval:0, sinceDate:d)
}
func ageFrom(date: NSDate) -> (Int, Int, Int) {
let cal = NSCalendar.currentCalendar()
let y = cal.component(NSCalendarUnit.Year, fromDate: date)
let m = cal.component(NSCalendarUnit.Month, fromDate: date)
let d = cal.component(NSCalendarUnit.Day, fromDate: date)
let ynow = cal.component(NSCalendarUnit.Year, fromDate: self)
let mnow = cal.component(NSCalendarUnit.Month, fromDate: self)
let dnow = cal.component(NSCalendarUnit.Day, fromDate: self)
let t0 = y * 12 + m - 1 // total months for birthdate.
var t = ynow * 12 + mnow - 1; // total months for Now.
var dm = t - t0; // delta months.
if(dnow >= d) {
return (Int(floor(Double(dm)/12)), dm % 12, dnow - d)
}
dm--
t--
return (Int(floor(Double(dm)/12)), dm % 12, Int((self.timeIntervalSince1970 - NSDate(ageDateString: "\(Int(floor(Double(t)/12)))-\(t%12 + 1)-\(d)").timeIntervalSince1970)/60/60/24))
}
}
// sample usage
let birthday = NSDate(ageDateString: "2012-7-8")
print(NSDate().ageFrom(birthday))
答案 8 :(得分:0)
我知道它有点过时,但这里有一个简单的代码,我使用了Python库的功能(Python 3.5用于注释,但没有工作)。
from datetime import date, timedelta
def age(birth: date) -> (int, int, int):
"""
Get a 3-int tuple telling the exact age based on birth date.
"""
today_age = date(1, 1, 1) + (date.today() - birth)
# -1 because we start from year 1 and not 0.
return (today_age.year - 1, today_age.month - 1, today_age.day - 1)
答案 9 :(得分:0)
from dateutil.relativedelta import *
from datetime import date
def calc_age(dob):
today = date.today()
age = relativedelta(today, dob)
print str(age.years)+" Years, "+str(age.months)+" Months,"+str(age.days)+" Days"
#test it
calc_age(date(1993,10,10))
答案 10 :(得分:0)
您可以使用 PHP date_diff http://php.net/manual/en/function.date-diff.php 或 http://www.php.net/manual/en/datetime.diff.php 来实现您想要的,并且您可以使用 PHP 日期格式以您想要的任何格式输出
$interval = date_diff(date_create(), date_create('2008-01-01 10:30:00')); echo $interval->format("You are %Y Year, %M Months, %d Days, %H Hours, %i Minutes, %s Seconds Old");
结果:
You are 04 Year, 04 Months, 1 Days, 00 Hours, 56 Minutes, 36 Seconds Old
答案 11 :(得分:-4)
因为你显然正在使用python:创建到datetime对象,一个带有“birthday”,一个带有“now”,减去它们并从生成的timedelta对象中读取年龄。
为什么要像闰年那样烦恼?