Holen Sie sich das Alter der Person in Ruby

125

Ich möchte das Alter einer Person von ihrem Geburtstag an erhalten. now - birthday / 365funktioniert nicht, weil einige Jahre 366 Tage haben. Ich habe mir den folgenden Code ausgedacht:

now = Date.today
year = now.year - birth_date.year

if (date+year.year) > now
  year = year - 1
end

Gibt es einen rubinroteren Weg, um das Alter zu berechnen?

Mantas
quelle
6
Ich mag diese Frage, weil sie die Idee hervorhebt, dass es "mehr Ruby" und "weniger Ruby" gibt, um Dinge zu tun. Es ist wichtig, nicht nur logisch korrekt zu sein (was Sie durch Kopieren der C # -Antwort tun können), sondern auch stilistisch korrekt. Und Adinochestvas Antwort nutzt die Ruby-Sprache.
James A. Rosen

Antworten:

410

Ich weiß, dass ich zu spät zur Party komme, aber die akzeptierte Antwort wird schrecklich brechen, wenn ich versuche, das Alter von jemandem herauszufinden, der am 29. Februar in einem Schaltjahr geboren wurde. Dies liegt daran, dass der Aufruf zum birthday.to_date.change(:year => now.year)Erstellen eines ungültigen Datums.

Ich habe stattdessen den folgenden Code verwendet:

require 'date'

def age(dob)
  now = Time.now.utc.to_date
  now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end
Philnash
quelle
4
Verwenden Sie dies, nicht das Häkchen, das Schaltjahre nicht bewältigen kann
bgcode
Warum geben Sie 0 || zurück? 1 statt true|| false?
0112
1
@ alex0112 Weil das Ergebnis (0 oder 1) dieser zugegebenermaßen verwirrenden Bedingung von der Differenz der Jahre zwischen jetzt und dem Geburtsdatum abgezogen wird. Es soll herausgefunden werden, ob die Person dieses Jahr schon Geburtstag hatte und wenn nicht, ist sie 1 Jahr weniger alt als die Differenz zwischen den Jahren.
Philnash
2
@andrej now = Date.today funktioniert, aber beachten Sie, dass es keine Probleme mit Timezone behandelt. In Rails gibt Date.today ein Datum zurück, das auf der Systemzeitzone basiert. ActiveRecord gibt eine Zeit zurück, die auf der von Ihren Apps konfigurierten Zeitzone basiert. Wenn sich die Systemzeitzone von Ihrer Anwendungszeitzone unterscheidet, würden Sie effektiv die Zeit aus zwei verschiedenen Zeitzonen vergleichen, die nicht sehr genau wären.
Lieblings Onwuemene
Ist das wirklich die einfachste Lösung?
Marco Prins
50

Ich habe festgestellt, dass diese Lösung gut funktioniert und für andere Menschen lesbar ist:

    age = Date.today.year - birthday.year
    age -= 1 if Date.today < birthday + age.years #for days before birthday

Einfach und Sie müssen sich keine Gedanken über die Handhabung des Schaltjahres und dergleichen machen.

PJ.
quelle
3
Dies erfordert Rails (für age.years), könnte aber dazu führen, dass Rails nicht benötigt wird, wenn Sie so etwas getan haben Date.today.month < birthday.month or Date.today.month == birthday.month && Date.today.mday < birthday.mday.
Chuck
Sie haben Recht - Entschuldigung, ich habe Rails angenommen, da die Frage damit markiert war. Aber ja, nur für Ruby leicht zu modifizieren.
PJ.
1
Ich hatte dieses zuerst gewählt, weil es das schönste ist, aber in der Produktion ist es häufig falsch, aus Gründen, die ich nicht verstehe. Das obige mit Time.now.utc.to_date scheint besser zu funktionieren.
Kevin
@ Kevin Interessant. Ich hatte selbst nie ein Problem damit, aber das bedeutet nicht, dass es keines gibt. Können Sie mir ein konkretes Beispiel geben? Ich würde gerne wissen, ob es einen Fehler gibt. Vielen Dank.
PJ.
2
@sigvei - das ist eine Funktion, kein Fehler;) In den meisten Ländern, einschließlich den USA, wird der 28. gesetzlich als Ihr Geburtstag in einem Nicht-Schaltjahr angesehen, wenn Sie ein Schaltbaby sind. Die Person würde in der Tat als 10 betrachtet werden.
PJ.
33

Benutze das:

def age
  now = Time.now.utc.to_date
  now.year - birthday.year - (birthday.to_date.change(:year => now.year) > now ? 1 : 0)
end
Sadegh
quelle
41
Dies wird unterbrochen, wenn Birthday.to_date ein Schaltjahr ist und das aktuelle Jahr nicht. Kein großes Ereignis, aber es hat mir Probleme bereitet.
Philnash
1
Downvoting, um Philnashs Antwort zu fördern.
Nick Sonneveld
2
Ein weiterer Grund, Philnashs Antwort zu bevorzugen , ist, dass sie mit einfachem alten Ruby funktioniert, während die akzeptierte Antwort nur mit funktioniert rails/activesupport.
Sheldonh
16

Ein Liner in Ruby on Rails (ActiveSupport). Behandelt Schaltjahre, Schaltsekunden und alles.

def age(birthday)
  (Time.now.to_s(:number).to_i - birthday.to_time.to_s(:number).to_i)/10e9.to_i
end

Logik von hier - Berechnen Sie das Alter in C #

Angenommen, beide Daten befinden sich in derselben Zeitzone, wenn Sie nicht beide utc()vorher anrufen to_s().

Vikrant Chaudhary
quelle
1
(Date.today.to_s(:number).to_i - birthday.to_date.to_s(:number).to_i)/1e4.to_ifunktioniert auch
Grant Hutchins
FWIW, aber dann wird meine Garantie für "Schaltsekunden" ungültig. ;-) (FWIW Teil 2, Ruby unterstützt sowieso keine "Schaltsekunden"). :-)
Vikrant Chaudhary
1
Ich bin mir nicht sicher, warum ich dafür Abstimmungen bekomme. Möchtest du das erklären, liebe Downvoter?
Vikrant Chaudhary
@ VikrantChaudhary Ich weiß nicht, es ist eine gute Antwort. Getestet und funktioniert.
Hector Ordonez
9
(Date.today.strftime('%Y%m%d').to_i - dob.strftime('%Y%m%d').to_i) / 10000
pguardiario
quelle
Dies berechnet also im Wesentlichen die Differenz in Tagen, wenn jeder Monat 100 Tage und jedes Jahr 100 Monate lang war. Was keinen Unterschied macht, wenn Sie nur den Jahresteil behalten.
Jonathan Allard
6

Die bisherigen Antworten sind irgendwie komisch. Ihr ursprünglicher Versuch war ziemlich nah am richtigen Weg, dies zu tun:

birthday = DateTime.new(1900, 1, 1)
age = (DateTime.now - birthday) / 365.25 # or (1.year / 1.day)

Sie erhalten ein gebrochenes Ergebnis. Sie können das Ergebnis also in eine Ganzzahl mit konvertieren to_i. Dies ist eine bessere Lösung, da die Datumsdifferenz korrekt als Zeitraum in Tagen (oder Sekunden im Fall der zugehörigen Zeitklasse) seit dem Ereignis behandelt wird. Dann gibt Ihnen eine einfache Division durch die Anzahl der Tage in einem Jahr das Alter. Wenn Sie das Alter in Jahren auf diese Weise berechnen, müssen Schaltjahre nicht berücksichtigt werden, solange Sie den ursprünglichen DOB-Wert beibehalten.

Bob Aman
quelle
Geburtstag = Time.mktime (1960,5,5) gibt mir außer Reichweite (Epochenprobleme?)
Andrew Grimm
Ja, geh, geh Epochenprobleme. Ich habe die Antwort aktualisiert, um dies zu beheben.
Bob Aman
birthday = DateTime.now - 1.yeargibt mir ein Alter von 0. Leider ist das Teilen durch 365,25 etwas ungenau.
Samir Talwar
Sie können 1.year so nicht von einem DateTime-Objekt subtrahieren. 1.Jahr wird auf die Anzahl der Sekunden in einem Jahr aufgelöst. DateTime-Objekte basieren auf Tagen. Zum Beispiel: (DateTime.now - 365.25) .strftime ("% D") Wenn Sie sich wirklich nur mit Geburtstagen beschäftigen, ist die Genauigkeit sehr genau. Tatsache ist, dass die Menschen in Bezug auf das Alter bereits ziemlich ungenau sind. Wir werden zu einem bestimmten Zeitpunkt geboren, aber normalerweise geben wir nicht die genaue Stunde, Minute und Sekunde unserer Geburt an, wenn wir unser Geburtsdatum aufschreiben. Mein Argument hier ist, dass Sie diese Berechnung wirklich nicht manuell durchführen möchten.
Bob Aman
1
Dies funktioniert nicht für Personen, die vor 1900 geboren wurden. Zum Beispiel soll Gertrude Baines an ihrem Geburtstag im Jahr 2009 ein Alter von 114,9979 Jahren haben.
Andrew Grimm
6

Mein Vorschlag:

def age(birthday)
    ((Time.now - birthday.to_time)/(60*60*24*365)).floor
end

Der Trick ist, dass die Minusoperation mit Time Sekunden zurückgibt

jlebrijo
quelle
Das ist fast richtig. Schaltjahre bedeuten, dass ein Jahr tatsächlich 365,25 Tage lang ist. Das bedeutet auch, dass diese Methode Ihr Alter bestenfalls erst 18 Stunden nach Ihrem Geburtstag erhöht.
Ryan Lue
5

Ich mag dieses:

now = Date.current
age = now.year - dob.year
age -= 1 if now.yday < dob.yday
tpalm
quelle
Wenn Sie der Meinung sind, dass dies ein vernünftiger Anwärter auf eine 3-jährige Frage ist, für die bereits 10 weitere Antworten vorliegen, sollten Sie mehr Gründe als persönliche Vorlieben angeben. Andernfalls erhalten Sie nicht viel Aufmerksamkeit
John Dvorak
1
Dies bricht, wenn ein Jahr ein Schaltjahr ist und ein anderes nicht
Artm
Wenn das letzte Jahr im Februar 29 Tage ist, schlägt diese Berechnung fehl
phil88530
5

Diese Antwort ist die beste, stimmen Sie sie stattdessen ab.


Ich mag die Lösung von @ philnash, aber die Bedingung könnte compacter sein. Dieser boolesche Ausdruck vergleicht [Monat, Tag] -Paare in lexikografischer Reihenfolge , sodass man stattdessen einfach den String-Vergleich von Ruby verwenden kann:

def age(dob)
  now = Date.today
  now.year - dob.year - (now.strftime('%m%d') < dob.strftime('%m%d') ? 1 : 0)
end
artm
quelle
1
Was ist mit (Date.today.strftime('%Y%m%d').to_i - dob.strftime('%Y%m%d').to_i) / 10000?
Ryan Lue
Wow, das ist so viel ordentlicher. Sie sollten eine Antwort geben und die Belohnungen sammeln!
Artm
hmm, eigentlich sehe ich, dass es diese Antwort schon vor meiner gab
artm
Oh, jetzt mache ich auch ... -_- '
Ryan Lue
4

Dies ist eine Konvertierung dieser Antwort (sie hat viele Stimmen erhalten):

# convert dates to yyyymmdd format
today = (Date.current.year * 100 + Date.current.month) * 100 + Date.today.day
dob = (dob.year * 100 + dob.month) * 100 + dob.day
# NOTE: could also use `.strftime('%Y%m%d').to_i`

# convert to age in years
years_old = (today - dob) / 10000

Es ist definitiv einzigartig in seiner Herangehensweise, macht aber durchaus Sinn, wenn Sie erkennen, was es tut:

today = 20140702 # 2 July 2014

# person born this time last year is a 1 year old
years = (today - 20130702) / 10000

# person born a year ago tomorrow is still only 0 years old
years = (today - 20130703) / 10000

# person born today is 0
years = (today - 20140702) / 10000  # person born today is 0 years old

# person born in a leap year (eg. 1984) comparing with non-leap year
years = (20140228 - 19840229) / 10000 # 29 - a full year hasn't yet elapsed even though some leap year babies think it has, technically this is the last day of the previous year
years = (20140301 - 19840229) / 10000 # 30

# person born in a leap year (eg. 1984) comparing with leap year (eg. 2016)
years = (20160229 - 19840229) / 10000 # 32
br3nt
quelle
Ich habe
1

Da Ruby on Rails markiert ist, die dotiw überschreibt gem die Rails integrierten in distance_of_times_in_words und bietet distance_of_times_in_words_hash , die verwendet werden können , das Alter zu bestimmen. Schaltjahre werden für den Teil der Jahre gut gehandhabt, obwohl zu beachten ist, dass der 29. Februar einen Einfluss auf den Teil der Tage hat, der Verständnis erfordert, wenn dieser Detaillierungsgrad benötigt wird. Wenn Ihnen nicht gefällt, wie dotiw das Format von distance_of_time_in_words ändert, verwenden Sie die Option: vague, um zum ursprünglichen Format zurückzukehren.

Füge dotiw zur Gemfile hinzu:

gem 'dotiw'

In der Befehlszeile:

bundle

Fügen Sie den DateHelper in das entsprechende Modell ein, um Zugriff auf distance_of_time_in_words und distance_of_time_in_words_hash zu erhalten. In diesem Beispiel lautet das Modell "Benutzer" und das Feld "Geburtstag" lautet "Geburtstag".

class User < ActiveRecord::Base
  include ActionView::Helpers::DateHelper

Fügen Sie diese Methode demselben Modell hinzu.

def age
  return nil if self.birthday.nil?
  date_today = Date.today
  age = distance_of_time_in_words_hash(date_today, self.birthday).fetch("years", 0)
  age *= -1 if self.birthday > date_today
  return age
end

Verwendung:

u = User.new("birthday(1i)" => "2011", "birthday(2i)" => "10", "birthday(3i)" => "23")
u.age
Mindriot
quelle
1

Ich glaube, dies entspricht funktional der Antwort von @ philnash, aber IMO ist leichter verständlich.

class BirthDate
  def initialize(birth_date)
    @birth_date = birth_date
    @now = Time.now.utc.to_date
  end

  def time_ago_in_years
    if today_is_before_birthday_in_same_year?
      age_based_on_years - 1
    else
      age_based_on_years
    end
  end

  private

  def age_based_on_years
    @now.year - @birth_date.year
  end

  def today_is_before_birthday_in_same_year?
    (@now.month < @birth_date.month) || ((@now.month == @birth_date.month) && (@now.day < @birth_date.day))
  end
end

Verwendung:

> BirthDate.new(Date.parse('1988-02-29')).time_ago_in_years
 => 31 
Jason Swett
quelle
0

Das Folgende scheint zu funktionieren (aber ich würde es begrüßen, wenn es aktiviert wäre).

age = now.year - bday.year
age -= 1 if now.to_a[7] < bday.to_a[7]
testr
quelle
0

Wenn Sie sich nicht für ein oder zwei Tage interessieren, wäre dies kürzer und ziemlich selbsterklärend.

(Time.now - Time.gm(1986, 1, 27).to_i).year - 1970
Hakunin
quelle
0

Ok was ist damit:

def age
  return unless dob
  t = Date.today
  age = t.year - dob.year
  b4bday = t.strftime('%m%d') < dob.strftime('%m%d')
  age - (b4bday ? 1 : 0)
end

Dies setzt voraus, dass wir Rails verwenden, die ageMethode für ein Modell aufrufen und das Modell über eine Datumsdatenbankspalte verfügt dob. Dies unterscheidet sich von anderen Antworten, da bei dieser Methode mithilfe von Zeichenfolgen festgestellt wird, ob wir vor dem diesjährigen Geburtstag sind.

Wenn zum Beispiel dob2004/2/28 und today2014/2/28 ist, agewird 2014 - 2004oder sein 10. Die Schwimmer werden 0228und sein 0229. b4bdaywird "0228" < "0229"oder sein true. Schließlich werden wir subtrahieren 1aus ageund erhalten 9.

Dies wäre der normale Weg, um die beiden Zeiten zu vergleichen.

def age
  return unless dob
  t = Date.today
  age = today.year - dob.year
  b4bday = Date.new(2016, t.month, t.day) < Date.new(2016, dob.month, dob.day)
  age - (b4bday ? 1 : 0)
end

Dies funktioniert genauso, aber die b4bdayLeitung ist zu lang. Das 2016Jahr ist auch unnötig. Der Stringvergleich am Anfang war das Ergebnis.

Sie können dies auch tun

Date::DATE_FORMATS[:md] = '%m%d'

def age
  return unless dob
  t = Date.today
  age = t.year - dob.year
  b4bday = t.to_s(:md) < dob.to_s(:md)
  age - (b4bday ? 1 : 0)
end

Wenn Sie keine Schienen verwenden, versuchen Sie dies

def age(dob)
  t = Time.now
  age = t.year - dob.year
  b4bday = t.strftime('%m%d') < dob.strftime('%m%d')
  age - (b4bday ? 1 : 0)
end

👍🏼

Cruz Nunez
quelle
Nur zu Ihrer Information, Ihre Antwort ist logischerweise dieselbe wie die von Philnash. Seine Antwort ist viel sauberer und verlässt sich jedoch nicht auf Rails.
Mantas
@Mantas Philnash sagte, er habe diese Methode in einem Rails-Projekt verwendet. Sie hatten Schienen in den Tags. Seine Methode hat zwei Vergleiche mehr als meine. Die letzte Zeile seiner Methode ist schwer zu verstehen.
Cruz Nunez
Entschuldigung, aber Philnashs Code ist viel sauberer als deins. Auch sein Code ist eine einfache Methode. Ihre hängt davon ab, ob Sie einen "dob" -Wert haben. Was für neue Benutzer möglicherweise nicht so klar ist. Und hilft dem Code nicht einmal viel. Ja, Ihre allerletzte Probe wird es los. Aber Philnashs ist einfach perfekt, um es einfach und dumm zu halten.
Mantas
0

Ich denke, es ist viel besser, keine Monate zu zählen, weil man mit genau den Tag eines Jahres ermitteln kann Time.zone.now.yday.

def age
  years  = Time.zone.now.year - birthday.year
  y_days = Time.zone.now.yday - birthday.yday

  y_days < 0 ? years - 1 : years
end
Oleksiy Nosov
quelle
0

Kam mit einer Rails-Variante dieser Lösung

def age(dob)
    now = Date.today
    age = now.year - dob.year
    age -= 1 if dob > now.years_ago(age)
    age
end
derosm2
quelle
0

DateHelper kann nur verwendet werden, um Jahre abzurufen

puts time_ago_in_words '1999-08-22'

fast 20 Jahre

hsul4n
quelle
0
  def computed_age
    if birth_date.present?
      current_time.year - birth_date.year - (age_by_bday || check_if_newborn ? 0 : 1)
    else
      age.presence || 0
    end
  end


  private

  def current_time
    Time.now.utc.to_date
  end

  def age_by_bday
    current_time.month > birth_date.month
  end

  def check_if_newborn
    (current_time.month == birth_date.month && current_time.day >= birth_date.day)
  end```
Sandesh Bodake
quelle
-1
  def birthday(user)
    today = Date.today
    new = user.birthday.to_date.change(:year => today.year)
    user = user.birthday
    if Date.civil_to_jd(today.year, today.month, today.day) >= Date.civil_to_jd(new.year, new.month, new.day)
      age = today.year - user.year
    else
      age = (today.year - user.year) -1
    end
    age
  end
Brian
quelle
-1
Time.now.year - self.birthdate.year - (birthdate.to_date.change(:year => Time.now.year) > Time.now.to_date ? 1 : 0)
Tim Van Ginderen
quelle
-1

Um Schaltjahre zu berücksichtigen (und die aktive Unterstützung zu übernehmen):

def age
  return unless birthday
  now = Time.now.utc.to_date
  years = now.year - birthday.year
  years - (birthday.years_since(years) > now ? 1 : 0)
end

years_sinceändert das Datum korrekt, um Nicht-Schaltjahre (wenn Geburtstag ist 02-29) zu berücksichtigen .

Vitaly Kushner
quelle
-1

Hier ist meine Lösung, mit der auch das Alter zu einem bestimmten Zeitpunkt berechnet werden kann:

def age on = Date.today
  (_ = on.year - birthday.year) - (on < birthday.since(_.years) ? 1 : 0)
end
hurikhan77
quelle
-2

Ich musste mich auch damit befassen, aber monatelang. Wurde viel zu kompliziert. Der einfachste Weg, den ich mir vorstellen konnte, war:

def month_number(today = Date.today)
  n = 0
  while (dob >> n+1) <= today
    n += 1
  end
  n
end

Sie könnten das gleiche mit 12 Monaten tun:

def age(today = Date.today)
  n = 0
  while (dob >> n+12) <= today
    n += 1
  end
  n
end

Dies verwendet die Datumsklasse, um den Monat zu erhöhen, der 28 Tage und Schaltjahre usw. behandelt.

Mooktakim Ahmed
quelle