Je souhaite calculer l'âge d'une personne en fonction de sa date de naissance et de la date du jour en années, mois et jours par rapport à la date du jour.
Par exemple:
>>> calculate_age(2008, 01, 01)
1 years, 0 months, 16 days
Tout pointeur sur un algorithme faisant cela sera apprécié.
Tout d'abord, notez l'hypothèse que si, par exemple, vous êtes né le 1er février, puis le 1er mars suivant, vous avez exactement 1 mois, alors que votre âge en jours n'est que de 28 ans - inférieur à la durée d'un mois moyen . Les années ont également une durée variable (en raison des années bissextiles), ce qui signifie que votre âge à votre anniversaire n'est généralement pas un nombre entier exact d'années. Si vous voulez exprimer le temps exact pendant lequel vous êtes en vie en années/mois/jours, voyez mon autre réponse. Mais il est plus probable que vous souhaitiez vous tromper juste pour que naître le 1er février signifie que chaque 1er février, vous ayez X ans, 0 mois et 0 jours, et le 1er de chaque mois, vous ayez X années, Y mois, et 0 jours.
Dans ce cas, lisez la suite. (NB: ce qui suit ne fonctionne que pour les dates antérieures.)
Étant donné la date de naissance, (y,m,d)
, la date actuelle, (ynow,mnow,dnow)
, et une fonction tm()
qui donne unix/Epoch time pour une date donnée , le texte suivant affichera une liste à 3 éléments donnant {age, mois, journées}:
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];
Ce qui suit est un algorithme équivalent si vous n'aimez pas tous les étages et les mods. Mais je pense que ce qui précède est meilleur. D'une part, il évite d'appeler tm()
s'il n'en a pas besoin.
{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];
Ce n'est pas une question facile, car au-dessus des jours (si nous ne prenons pas en compte secondes bissextiles ), il n'y a pas de formules faciles.
Les mois peuvent comprendre 28, 29, 30 ou 31 jours; les années peuvent être 365 ou 366 jours. Par conséquent, des problèmes surviennent lorsque vous essayez de calculer des unités de temps complètes pour des mois et des années.
Voici un article intéressant qui prend en compte tous les aspects complexes pour résoudre votre question en Java.
D'autres ici ont la bonne idée - vous devez simplement pouvoir l'étendre à d'autres langues. De nombreux systèmes informatiques comptent le temps en secondes à partir d'un point spécifique (l '"époque" ou la "date de référence"), déterminent la date à partir de cet endroit et proposent des méthodes pour convertir les secondes en dates. Vous devriez pouvoir obtenir l'heure actuelle en secondes, convertir la date de naissance en secondes à partir de l'époque, soustraire les deux valeurs, puis diviser par le nombre de secondes d'une journée:
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);
Le bogue dans ce code particulier est que mktime () ne peut pas traiter les dates antérieures à Epoch, c'est-à-dire le 1er janvier 1970 dans les systèmes Unix. Des problèmes similaires existeront sur d’autres systèmes en fonction de la manière dont ils comptent le temps. L'algorithme général devrait fonctionner, il vous suffit de connaître vos limites sur des systèmes spécifiques.
Comme les années, les mois et même les jours peuvent avoir des longueurs inégales, vous ne pouvez pas commencer par soustraire les deux dates pour obtenir une durée simple. Si vous voulez que le résultat corresponde à la façon dont les êtres humains traitent les dates, vous devez plutôt travailler avec des dates réelles, en utilisant les fonctions intégrées de votre langue qui comprennent les dates mieux que jamais.
La manière dont cet algorithme est écrit dépend du langage que vous utilisez, car chaque langage a un ensemble de fonctions différent. Ma propre implémentation en Java, en utilisant le GregorianCalendar maladroit mais techniquement correct:
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);
}
Cela peut ne pas être parfait, mais cela donne des résultats lisibles par l'homme. Term est une classe à moi pour stocker des périodes de style humain, car les bibliothèques de dates de Java n'en ont pas.
Pour les projets futurs, je prévois de passer à la bien meilleure bibliothèque de date/heure de Joda, qui contient une classe Period et devra probablement réécrire cette fonction.
Il y a une discussion sur python-list sur la raison pour laquelle calculer un âge n'est pas une tâche si simple .
À partir de cette discussion, j'ai trouvé cette implémentation (que je ne vais pas copier et coller ici car ce n'est pas la mienne).
Je n'ai cependant trouvé aucune bibliothèque de dépôt.
Voici un moyen de le faire si vous êtes prêt à violer le principe "Je devrais avoir un nombre entier d'années pour mon anniversaire". Notez que ce principe n'est pas strictement vrai, à cause des années bissextiles, donc ce qui suit est en réalité plus précis, bien que peut-être moins intuitif. Si vous voulez connaître le nombre exact de personnes âgées, voici comment je le ferais.
Convertissez d’abord la date de naissance et l’heure actuelle en heure de l’époque (nombre de secondes depuis l’aube des temps, c’est-à-dire 1970 en pays unix) et soustrayez-les. Voir, par exemple, cette question pour savoir comment procéder: Comment convertir une date/heure en heure Epoch (ou heure unix - secondes depuis 1970) en Perl? . Vous avez maintenant l'âge exact de la personne en secondes et vous devez le convertir en chaîne telle que "36 ans, 3 mois, 8 jours".
Voici le pseudocode pour le faire. Il devrait être simple de supprimer les semaines ou les heures ou tout autre élément que vous ne souhaitez pas ...
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)); }
Je suppose que les questions sont plus larges qu'un seul langage de programmation. Si vous utilisez C #, vous pouvez utiliser DateTimeOffset pour le calculer.
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
conservera les informations dont vous avez besoin.
Je sais que c'est un peu dépassé, mais voici un code simple que j'ai utilisé, avec toute la puissance de la bibliothèque Python (Python 3.5 pour les annotations, mais ne fonctionne pas).
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)
L'implémentation rapide de la réponse de dreeves.
ps. Au lieu de (y, m, d) (ynow, mnow, dnow) comme entrées, j'utilise deux NSDate, ce qui peut être plus pratique dans les usages du monde réel.
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))
Je pense que vous devez préciser les exigences un peu mieux.
Supposons que la date de naissance est le 2008-02-01 et que le jour est le 2009-01-31
Quel est le résultat?
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))
Voici ceci en utilisant la règle d'emprunt des mathématiques.
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);