Ruby'de kişinin yaşını öğrenin


125

Doğum gününden itibaren bir kişinin yaşını öğrenmek isterim. now - birthday / 365çalışmıyor, çünkü bazı yıllarda 366 gün var. Aşağıdaki kodu buldum:

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

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

Yaşı hesaplamanın daha Ruby'ye özgü bir yolu var mı?


6
Bu soruyu seviyorum çünkü işleri yapmanın "daha fazla Ruby" ve "daha az Ruby" olduğu fikrini vurguluyor. Sadece mantıksal olarak doğru olmak (ki bunu C # cevabını kopyalayarak yapabilirsiniz) değil, aynı zamanda biçimsel olarak da doğru olmak önemlidir. Ve Adinochestva'nın cevabı Ruby deyiminden iyi bir şekilde yararlanıyor.
James A. Rosen

Yanıtlar:


410

Buradaki partiye geç kaldığımı biliyorum, ancak kabul edilen yanıt, artık yılda 29 Şubat'ta doğan birinin yaşını hesaplamaya çalışırken korkunç bir şekilde bozulacak. Bunun nedeni, çağrının birthday.to_date.change(:year => now.year)geçersiz bir tarih oluşturmasıdır.

Bunun yerine aşağıdaki kodu kullandım:

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

4
Artık yılları işleyemeyen onay işaretli olanı değil bunu kullanın
bgcode

Neden 0 döndürüyorsunuz || true|| yerine 1 false?
0112

1
@ alex0112 Çünkü o kuşkusuz kafa karıştırıcı koşulun sonucu (0 veya 1), bugün ile doğum tarihi arasındaki yıl farkından çıkarılır. Kişinin bu yıl henüz doğum gününü yapıp yapmadığının ve değilse de yıllar arasındaki farktan 1 yaş küçük olduğunun öğrenilmesi amaçlanmaktadır.
philnash

2
@andrej now = Date.today çalışıyor ancak Timezone ile ilgili sorunları ele almadığını unutmayın. Rails'de Date.today, sistem saat dilimine göre bir tarih döndürür. ActiveRecord, Uygulamalarınızın yapılandırılmış saat dilimine göre bir saat döndürür. Sistem saat dilimi uygulamanızın saat diliminden farklıysa, zamanı iki farklı saat diliminden etkili bir şekilde karşılaştırırsınız ki bu çok doğru olmaz.
Favori Onwuemene

Bu gerçekten en basit çözüm mü?
Marco Prins

50

Bu çözümü iyi çalışacak ve diğer insanlar tarafından okunabilir buldum:

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

Kolaydır ve artık yıl ve benzeri şeylerle uğraşmak için endişelenmenize gerek yoktur.


3
Bu, Rails'i gerektirir (age.years için), ancak buna benzer bir şey yaptıysanız Rails'i gerektirmeyecek şekilde yapılabilir Date.today.month < birthday.month or Date.today.month == birthday.month && Date.today.mday < birthday.mday.
Chuck

Haklısın - üzgünüm soru onunla etiketlendiği için Rails'i varsaydım. Ancak evet, yalnızca Ruby için kolayca değiştirilebilir.
PJ.

1
Bunu ilk başta en güzel olduğu için seçmiştim, ama yapımda, anlamadığım nedenlerden dolayı çoğu zaman yanlış. Time.now.utc.to_date kullanan yukarıdakinin daha iyi çalıştığı görülüyor.
Kevin

@Kevin İlginç. Ben kendim de bununla hiç sorun yaşamadım ama bu olmadığı anlamına gelmez. Bana belirli bir örnek verebilir misiniz? Bir hata olup olmadığını bilmek isterim. Teşekkürler.
PJ.

2
@sigvei - bu bir özellik, hata değil;) ABD de dahil olmak üzere çoğu ülkede, artık bir bebekseniz, 28'inci yasal olarak artık olmayan bir yılda doğum gününüz olarak kabul edilir. Kişi gerçekten 10 olarak kabul
edilirdi.

33

Bunu kullan:

def age
  now = Time.now.utc.to_date
  now.year - birthday.year - (birthday.to_date.change(:year => now.year) > now ? 1 : 0)
end

41
Birthday.to_date artık bir yılsa ve mevcut yıl değilse bu sona erer. Büyük bir olay değil, ama bana sorun yaratıyor.
philnash 01

1
Philnash'ın cevabını teşvik etmek için olumsuz oylama.
Nick Sonneveld

2
Philnash'in cevabını tercih etmenin bir başka nedeni de , eski Ruby ile çalışması, kabul edilen cevap ise sadece işe yaramasıdırrails/activesupport .
sheldonh

16

Ruby on Rails'de (ActiveSupport) bir satır. Artık yılları, artık saniyeleri ve her şeyi yönetir.

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

Buradan mantık - C # cinsinden yaşı hesapla

Her ikisinde de daha utc()önce çağrı yapılmıyorsa, her iki tarihin aynı saat diliminde olduğunu varsayarsak to_s().


1
(Date.today.to_s(:number).to_i - birthday.to_date.to_s(:number).to_i)/1e4.to_iayrıca çalışır
Grant Hutchins

FWIW, ancak daha sonra "artık saniye" garantim geçersiz olacaktır. ;-) (FWIW bölüm 2, Ruby zaten "artık saniyeleri" desteklemiyor). :-)
Vikrant Chaudhary

1
Bu konuda neden olumsuz oy aldığımı bilmiyorum. Açıklamak ister misiniz sevgili olumsuz oy verenler?
Vikrant Chaudhary

@vikrantChaudhary Bilmiyorum, bu harika bir cevap. Test edildi ve çalışıyor.
Hector Ordonez

9
(Date.today.strftime('%Y%m%d').to_i - dob.strftime('%Y%m%d').to_i) / 10000

Yani bu, her ay 100 gün ve her yıl 100 ay uzunluğundaysa, esasen gün cinsinden farkı hesaplar. Bu sadece yıl kısmını tutarsanız bir fark yaratmaz.
Jonathan Allard

6

Şimdiye kadarki cevaplar biraz tuhaf. İlk denemeniz, bunu yapmanın doğru yoluna oldukça yakındı:

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

Kesirli bir sonuç alacaksınız, bu yüzden sonucu bir tam sayıya çevirmekten çekinmeyin to_i. Bu daha iyi bir çözümdür çünkü tarih farkını, olaydan sonraki günler (veya ilgili Zaman sınıfı durumunda saniye) olarak ölçülen bir zaman periyodu olarak doğru şekilde ele alır. O zaman bir yıldaki gün sayısına göre basit bir bölme size yaşı verir. Yıl cinsinden yaşı bu şekilde hesaplarken, orijinal DOB değerini koruduğunuz sürece, artık yıllar için herhangi bir ödenek ayrılmasına gerek yoktur.


doğum günü = Time.mktime (1960,5,5) beni menzil dışında veriyor (çağ problemleri mi?)
Andrew Grimm

Evet, git çağ sorunları. Bunu çözmek için cevabı güncelledim.
Bob Aman

birthday = DateTime.now - 1.yearbana 0 yaş veriyor. Maalesef 365.25'e bölmek biraz kesin değil.
Samir Talwar

Bir DateTime nesnesinden 1. yılı bu şekilde çıkaramazsınız. 1. yıl, bir yıldaki saniye sayısına karşılık gelir. DateTime nesneleri günlere göre çalışır. Örneğin: (DateTime.now - 365.25) .strftime ("% D") Kesinliğe gelince, gerçekten sadece doğum günleriyle uğraşıyorsanız, bu oldukça kesindir. İşin aslı, insanlar çağlar söz konusu olduğunda zaten oldukça belirsizler. Zamanın kesin bir anında doğarız, ancak doğumumuzun tam saatini, dakikasını ve saniyesini DOB'umuzu yazarken genellikle vermeyiz. Buradaki argümanım, bu hesaplamayı gerçekten manuel olarak yapmak istemediğinizdir.
Bob Aman

1
Bu, 1900'den önce doğmuş insanlar için işe yaramaz. Örneğin Gertrude Baines'in 2009'daki doğum gününde 114,9979 yaşında olduğu bildiriliyor.
Andrew Grimm

6

Benim önerim:

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

İşin püf noktası, Zaman ile yapılan eksi işlemin saniye döndürmesidir


Bu neredeyse doğrudur. Artık yıllar, bir yılın aslında 365,25 gün uzunluğunda olduğu anlamına gelir. Bu aynı zamanda en iyi ihtimalle, bu yöntemin yaşınızı 18 saat doğum gününüze kadar artırmayabileceği anlamına gelir.
Ryan Lue

5

Bunu beğendim:

now = Date.current
age = now.year - dob.year
age -= 1 if now.yday < dob.yday

Bunun zaten 10 başka cevabı olan 3 yaşındaki bir soru için makul bir rakip olduğunu düşünüyorsanız, kişisel tercihten daha fazla neden eklemelisiniz. Aksi takdirde, fazla dikkat
çekmezsiniz

1
bu, bir yıl artık bir
yılken

Geçen yıl
şubat

5

Bu cevap en iyisidir, onun yerine oy verin.


@ Philnash'ın çözümünü beğendim, ancak koşullu olabilir. Bu boole ifadesinin yaptığı şey, sözlük sırasını kullanarak [ay, gün] çiftlerini karşılaştırmaktır , bu nedenle bunun yerine Ruby'nin dize karşılaştırması kullanılabilir:

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

1
Peki ya (Date.today.strftime('%Y%m%d').to_i - dob.strftime('%Y%m%d').to_i) / 10000?
Ryan Lue

vay, bu çok daha düzgün. bir cevap vermeli ve ödülleri toplamalısın!
artm

hmm, aslında bu cevabın benden önce de olduğunu görüyorum
artm

Oh, şimdi bende ... -_- '
Ryan Lue

4

Bu, bu cevabın bir dönüşümüdür (çok sayıda oy almıştır):

# 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

Yaklaşımı açısından kesinlikle benzersizdir, ancak ne yaptığını anladığınızda mükemmel bir anlam ifade eder:

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

Sadece fark bu aynı Anser olduğunu
br3nt

1

Ruby on Rails etiketlendiğinden, dotiw gem Rails yerleşik distance_of_times_in_words değerini geçersiz kılar ve yaşı belirlemek için kullanılabilecek distance_of_times_in_words_hash sağlar . Artık yıllar, yıllar bölümü için iyi işlenir, ancak 29 Şubat'ın, bu ayrıntı düzeyinin gerekli olup olmadığını anlamayı garanti eden günler bölümü üzerinde bir etkisi olduğunu unutmayın. Ayrıca, dotiw'in distance_of_time_in_words biçimini nasıl değiştirdiğini beğenmiyorsanız, orijinal biçime dönmek için: vague seçeneğini kullanın.

Gemfile'a dotiw ekleyin:

gem 'dotiw'

Komut satırında:

bundle

Distance_of_time_in_words ve distance_of_time_in_words_hash'e erişim sağlamak için DateHelper'ı uygun modele dahil edin. Bu örnekte model 'Kullanıcı' ve doğum günü alanı 'doğum günüdür.

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

Bu yöntemi aynı modele ekleyin.

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

Kullanımı:

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

1

Bunun işlevsel olarak @ philnash'ın cevabına eşdeğer olduğuna inanıyorum, ancak IMO daha kolay anlaşılabilir.

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

Kullanımı:

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

0

Aşağıdakiler işe yarıyor gibi görünüyor (ancak kontrol edildiyse memnun olurum).

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

0

Bir veya iki gün umurunuzda değilse, bu daha kısa ve oldukça kendini açıklayıcı olacaktır.

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

0

Tamam, buna ne dersin:

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

Bu, rayları kullandığımızı age, bir model üzerinde yöntemi çağırdığımızı ve modelin bir tarih veritabanı sütununa sahip olduğunu varsayar dob. Bu, diğer cevaplardan farklıdır çünkü bu yöntem, bu yılın doğum gününden önce olup olmadığımızı belirlemek için dizeler kullanır.

Örneğin dob, 2004/2/28 ve today2014/2/28 ise, veya ageolacaktır . Şamandıralar ve olacak . veya olacak . Son olarak, düşeriz gelen ve almak .2014 - 20041002280229b4bday"0228" < "0229"true1age9

Bu, iki zamanı karşılaştırmanın normal yolu olacaktır.

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

Bu aynı şekilde çalışır, ancak b4bdayçizgi çok uzun. 2016Yıl da gereksizdir. Sonuç baştaki dizge karşılaştırmasıydı.

Bunu da yapabilirsin

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

Ray kullanmıyorsanız, bunu deneyin

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

👍🏼


Sadece, cevabınız mantıksal olarak philnash'ın cevabıyla aynı. Cevabı çok daha net ve Rails'e güvenmiyor.
Mantas

@Mantas Philnash, bu yöntemi bir Rails projesinde kullandığını söyledi. Etiketlerde raylar vardı. Metodunun benimkinden iki tane daha fazla karşılaştırması var. Yönteminin son satırını anlamak zor.
Cruz Nunez

üzgünüm, ama philnash'ın kodu seninkinden çok daha temiz. Ayrıca, kodu basit bir yöntemdir. Sizinki "dob" değerine sahip olmanıza bağlı. Yeni kullanıcılar için bu o kadar net olmayabilir. Ve koda çok da yardımcı olmuyor. Evet, son örneğiniz ondan kurtulur. Ama philnash's sadece mükemmel, basit tut aptalca bir kod parçası.
Mantas

0

Bence ayları saymamak çok daha iyi, çünkü kullanarak yılın tam gününü elde edebilirsiniz 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

0

Bu çözümün bir Rails varyasyonuyla geldi

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

0

DateHelper yalnızca yıl almak için kullanılabilir

puts time_ago_in_words '1999-08-22'

neredeyse 20 yıl


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```

-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

-1
Time.now.year - self.birthdate.year - (birthdate.to_date.change(:year => Time.now.year) > Time.now.to_date ? 1 : 0)

-1

Artık yılları hesaba katmak için (ve aktif destek varlığını varsayarak):

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_sinceartık olmayan yılları hesaba katmak için tarihi doğru şekilde değiştirecektir (doğum günü olduğunda 02-29).


-1

İşte yaşın belirli bir tarihte hesaplanmasına izin veren çözümüm:

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

-2

Bununla da başa çıkmak zorunda kaldım, ama aylarca. Çok karmaşık hale geldi. Düşünebildiğim en basit yol şuydu:

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

Aynısını 12 ay için de yapabilirsiniz:

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

Bu, 28 gün ve artık yıl vb. İle ilgilenecek olan ayı artırmak için Tarih sınıfını kullanacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.