ดูอายุของคนใน Ruby


125

ฉันต้องการระบุอายุจากวันเกิดของบุคคลนั้น now - birthday / 365ไม่ได้ผลเพราะบางปีมี 366 วัน ฉันคิดรหัสต่อไปนี้:

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

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

มีวิธีการคำนวณอายุของ Ruby'ish เพิ่มเติมหรือไม่?


6
ฉันชอบคำถามนี้เพราะเน้นย้ำแนวคิดที่ว่ามีวิธีการทำสิ่งต่างๆ "ทับทิมมากกว่า" และ "ทับทิมน้อยกว่า" สิ่งสำคัญไม่เพียง แต่จะต้องถูกต้องตามหลักเหตุผลเท่านั้น (ซึ่งคุณสามารถทำได้โดยการคัดลอกคำตอบ C #) แต่ยังแก้ไขด้วยโวหารด้วย และคำตอบของ Adinochestva ใช้สำนวน Ruby ได้ดี
James A. Rosen

คำตอบ:


410

ฉันรู้ว่าฉันมางานปาร์ตี้ที่นี่ช้า แต่คำตอบที่ได้รับการยอมรับจะพังทลายอย่างน่ากลัวเมื่อพยายามหาอายุของคนที่เกิดในวันที่ 29 กุมภาพันธ์ในปีอธิกสุรทิน เนื่องจากการเรียกร้องให้birthday.to_date.change(:year => now.year)สร้างวันที่ไม่ถูกต้อง

ฉันใช้รหัสต่อไปนี้แทน:

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
ใช้สิ่งนี้ไม่ใช่เครื่องหมายถูกซึ่งไม่สามารถรองรับปีอธิกสุรทินได้
bgcode

ทำไมกลับ 0 || 1 แทนtrue|| false?
0112

1
@ alex0112 เนื่องจากผลลัพธ์ (0 หรือ 1) ของเงื่อนไขที่ทำให้สับสนนั้นถูกหักออกจากความแตกต่างในปีระหว่างนี้กับวันเกิด มีจุดมุ่งหมายเพื่อค้นหาว่าบุคคลนั้นมีวันเกิดในปีนี้หรือไม่และถ้าไม่แสดงว่าบุคคลนั้นมีอายุน้อยกว่า 1 ปีเมื่อเทียบกับปี
philnash

2
@andrej now = Date.today ใช้งานได้ แต่โปรดทราบว่าจะไม่จัดการปัญหาเกี่ยวกับเขตเวลา ใน Rails Date.today ส่งกลับวันที่ตามเขตเวลาของระบบ ActiveRecord ส่งคืนเวลาตามเขตเวลาที่กำหนดค่าแอปของคุณ หากเขตเวลาของระบบแตกต่างจากเขตเวลาแอปพลิเคชันของคุณคุณจะเปรียบเทียบเวลาจากสองเขตเวลาที่แตกต่างกันได้อย่างมีประสิทธิภาพซึ่งจะไม่แม่นยำมากนัก
Onwuemene รายการโปรด

นี่เป็นวิธีแก้ปัญหาที่ง่ายที่สุดหรือไม่?
Marco Prins

50

ฉันพบว่าโซลูชันนี้ทำงานได้ดีและสามารถอ่านได้สำหรับผู้อื่น:

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

ง่ายและไม่ต้องกังวลกับการจัดการปีอธิกสุรทินและอื่น ๆ


3
นี้ต้อง Rails (สำหรับ age.years) Date.today.month < birthday.month or Date.today.month == birthday.month && Date.today.mday < birthday.mdayแต่อาจจะทำไม่ได้ที่จะต้องใช้รางถ้าคุณไม่ได้สิ่งที่ต้องการ
Chuck

คุณพูดถูก - ขอโทษฉันคิดว่า Rails เป็นคำถามที่ถูกแท็กด้วย แต่ใช่แก้ไขได้ง่ายสำหรับ Ruby เท่านั้น
พี.เจ.

1
ตอนแรกฉันเลือกอันนี้เพราะสวยที่สุด แต่ในการผลิตมักจะผิดพลาดด้วยเหตุผลที่ฉันไม่เข้าใจ อันข้างต้นโดยใช้ Time.now.utc.to_date ดูเหมือนว่าจะทำงานได้ดีกว่า
Kevin

@ เควินน่าสนใจ. ฉันไม่เคยมีปัญหากับตัวเอง แต่นั่นไม่ได้หมายความว่าไม่มีเลย คุณช่วยยกตัวอย่างเฉพาะให้ฉันได้ไหม ฉันต้องการทราบว่ามีข้อบกพร่องหรือไม่ ขอบคุณ
พี.เจ.

2
@sigvei - นั่นคือคุณสมบัติไม่ใช่ข้อผิดพลาด) ในประเทศส่วนใหญ่รวมถึงสหรัฐอเมริกาวันที่ 28 ถือเป็นวันเกิดของคุณตามกฎหมายในปีที่ไม่ใช่ปีอธิกสุรทินหากคุณเป็นทารกที่ก้าวกระโดด บุคคลนั้นจะได้รับการพิจารณาอย่างแน่นอน 10.
PJ.

33

ใช้สิ่งนี้:

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 เป็นปีอธิกสุรทินและปีปัจจุบันไม่ใช่ ไม่ใช่เรื่องใหญ่ แต่มันทำให้ฉันมีปัญหา
philnash

1
การลงคะแนนเพื่อสนับสนุนคำตอบของ philnash
Nick Sonneveld

2
อีกเหตุผลหนึ่งที่ชอบคำตอบของ philnashคือมันใช้งานได้กับ Ruby รุ่นเก่าธรรมดาในขณะที่คำตอบที่ยอมรับจะใช้ได้กับrails/activesupportเท่านั้น
sheldonh

16

หนึ่งซับใน Ruby on Rails (ActiveSupport) จัดการกับปีอธิกสุรทินวินาทีอธิกและทั้งหมด

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

ตรรกะจากที่นี่ - คำนวณอายุใน C #

สมมติว่าทั้งสองวันอยู่ในเขตเวลาเดียวกันหากไม่โทรutc()มาก่อนto_s()ทั้งสอง


1
(Date.today.to_s(:number).to_i - birthday.to_date.to_s(:number).to_i)/1e4.to_iยังใช้งานได้
Grant Hutchins

FWIW แต่แล้วการรับประกัน "อธิกวินาที" ของฉันจะเป็นโมฆะ ;-) (FWIW ตอนที่ 2 Ruby ไม่รองรับ "อธิกวินาที" อยู่ดี) :-)
Vikrant Chaudhary

1
ไม่แน่ใจว่าทำไมฉันถึงได้รับการโหวตต่ำในเรื่องนี้ สนใจที่จะอธิบายผู้ลงคะแนนที่รัก?
Vikrant Chaudhary

@vikrantChaudhary ฉันไม่รู้มันเป็นคำตอบที่ดี ทดสอบแล้วใช้งานได้จริง
Hector Ordonez

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

ดังนั้นสิ่งนี้จึงคำนวณความแตกต่างเป็นจำนวนวันหากแต่ละเดือนยาว 100 วันและแต่ละปียาว 100 เดือน ซึ่งไม่ได้สร้างความแตกต่างหากคุณเก็บเฉพาะส่วนของปีไว้
Jonathan Allard

6

คำตอบจนถึงตอนนี้ค่อนข้างแปลก ความพยายามเดิมของคุณค่อนข้างใกล้เคียงกับวิธีที่ถูกต้องในการทำสิ่งนี้:

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

คุณจะได้ผลลัพธ์ที่เป็นเศษส่วนดังนั้นto_iอย่าลังเลที่จะแปลงผลลัพธ์เป็นจำนวนเต็มด้วย นี่เป็นวิธีแก้ปัญหาที่ดีกว่าเนื่องจากถือว่าความแตกต่างของวันที่เป็นช่วงเวลาที่วัดเป็นวัน (หรือวินาทีในกรณีของคลาสเวลาที่เกี่ยวข้อง) อย่างถูกต้องตั้งแต่เหตุการณ์ จากนั้นการหารง่ายๆด้วยจำนวนวันในหนึ่งปีจะช่วยให้คุณมีอายุ เมื่อคำนวณอายุเป็นปีด้วยวิธีนี้ตราบเท่าที่คุณยังคงรักษาค่า DOB เดิมไว้ไม่จำเป็นต้องมีการเผื่อไว้สำหรับปีอธิกสุรทิน


birthday = Time.mktime (1960,5,5) ทำให้ฉันอยู่นอกช่วง (ปัญหายุค?)
Andrew Grimm

ใช่ไปปัญหายุค ฉันได้อัปเดตคำตอบเพื่อแก้ไขปัญหานี้แล้ว
Bob Aman

birthday = DateTime.now - 1.yearทำให้ฉันอายุ 0 น่าเสียดายที่การหารด้วย 365.25 นั้นไม่ชัดเจนเล็กน้อย
Samir Talwar

คุณไม่สามารถลบ 1 ปีแบบนั้นออกจากวัตถุ DateTime ได้ 1. ปีกำหนดจำนวนวินาทีในหนึ่งปี วัตถุ DateTime ทำงานตามวัน ตัวอย่างเช่น: (DateTime.now - 365.25) .strftime ("% D") สำหรับความแม่นยำถ้าคุณกำลังจัดการกับวันเกิดจริงๆก็มีความแม่นยำมากมาย ข้อเท็จจริงของเรื่องนี้คือคนเราค่อนข้างเข้าใจไม่ตรงกันเมื่อถึงยุคสมัย เราเกิดในช่วงเวลาที่แน่นอน แต่โดยปกติเราจะไม่ระบุชั่วโมงนาทีและวินาทีของการเกิดที่แน่นอนเมื่อเราเขียน DOB ของเรา ข้อโต้แย้งของฉันที่นี่คือคุณไม่ต้องการคำนวณด้วยตนเองจริงๆ
Bob Aman

1
สิ่งนี้ใช้ไม่ได้กับคนที่เกิดก่อนปี 1900 ตัวอย่างเช่น Gertrude Baines มีรายงานว่ามีอายุ 114.9979 ปีในวันเกิดของเธอในปี 2009
Andrew Grimm

6

คำแนะนำของฉัน:

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

เคล็ดลับคือการดำเนินการลบด้วยเวลาจะส่งกลับวินาที


นี่เกือบถูกแล้ว ปีอธิกสุรทินหมายความว่าปีหนึ่งยาว 365.25 วัน นั่นหมายความว่าอย่างดีที่สุดวิธีนี้อาจไม่เพิ่มอายุของคุณจนถึง 18 ชั่วโมงในวันเกิดของคุณ
Ryan Lue

5

ฉันชอบอันนี้:

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

หากคุณคิดว่านี่เป็นข้อโต้แย้งที่สมเหตุสมผลสำหรับคำถาม 3yo ที่มีคำตอบอื่น ๆ อยู่แล้ว 10 คำคุณควรระบุเหตุผลมากกว่าความชอบส่วนตัว มิฉะนั้นคุณจะไม่ได้รับความสนใจมากนัก
John Dvorak

1
หยุดพักเมื่อปีหนึ่งเป็นปีอธิกสุรทินและอีกปีหนึ่งไม่ใช่
artm

หากเดือนกุมภาพันธ์ปีที่แล้วได้ 29 วันการคำนวณนี้จะล้มเหลว
phil88530

5

คำตอบนี้ดีที่สุดให้โหวตคะแนนแทน


ฉันชอบโซลูชันของ @ philnash แต่เงื่อนไขอาจเป็น compacter สิ่งที่นิพจน์บูลีนทำคือการเปรียบเทียบคู่ [เดือน, วัน] โดยใช้ลำดับพจนานุกรมดังนั้นเราจึงสามารถใช้การเปรียบเทียบสตริงของทับทิมแทน:

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

1
เกี่ยวกับอะไร(Date.today.strftime('%Y%m%d').to_i - dob.strftime('%Y%m%d').to_i) / 10000?
Ryan Lue

ว้าวววววววว คุณควรตอบและรับรางวัล!
artm

อืมจริงๆแล้วฉันเห็นว่ามีคำตอบนี้อยู่แล้วก่อนหน้าฉัน
artm

โอ้ตอนนี้ฉันก็ทำเกินไป ... -_- '
Ryan Lue

4

นี่คือการแปลงคำตอบนี้ (ได้รับการโหวตเป็นจำนวนมาก):

# 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

วิธีการนี้ไม่เหมือนใครอย่างแน่นอน แต่สมเหตุสมผลเมื่อคุณรู้ว่ามันทำอะไร:

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

เพิ่งรู้ว่านี่คือ anser เดียวกัน
br3nt

1

เนื่องจากมีการแท็ก Ruby on Rails อัญมณีdotiwจะแทนที่ Rails ในตัวdistance_of_times_in_wordsและให้distance_of_times_in_words_hashซึ่งสามารถใช้เพื่อกำหนดอายุได้ ปีอธิกสุรทินจะได้รับการจัดการที่ดีสำหรับส่วนของปีแม้ว่าโปรดทราบว่าวันที่ 29 กุมภาพันธ์จะมีผลกระทบต่อส่วนของวันที่รับประกันความเข้าใจหากต้องการรายละเอียดในระดับนั้น นอกจากนี้หากคุณไม่ชอบวิธีที่ dotiw เปลี่ยนรูปแบบของ distance_of_time_in_words ให้ใช้ตัวเลือก: vague เพื่อเปลี่ยนกลับเป็นรูปแบบเดิม

เพิ่ม dotiw ไปยัง Gemfile:

gem 'dotiw'

ในบรรทัดคำสั่ง:

bundle

รวม DateHelper ในโมเดลที่เหมาะสมเพื่อเข้าถึง distance_of_time_in_words และ distance_of_time_in_words_hash ในตัวอย่างนี้โมเดลคือ 'ผู้ใช้' และฟิลด์วันเกิดคือ 'วันเกิด

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

เพิ่มวิธีนี้ในโมเดลเดียวกันนั้น

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

การใช้งาน:

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

1

ฉันเชื่อว่าสิ่งนี้เทียบเท่ากับคำตอบของ @ philnash แต่ IMO เข้าใจได้ง่ายกว่า

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

การใช้งาน:

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

0

สิ่งต่อไปนี้ดูเหมือนจะใช้งานได้ (แต่ฉันจะขอบคุณหากตรวจสอบแล้ว)

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

0

หากคุณไม่สนใจวันหรือสองวันสิ่งนี้จะสั้นลงและอธิบายตัวเองได้ดีทีเดียว

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

0

ตกลงแล้วสิ่งนี้:

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

นี้จะสมมติว่าเราจะใช้รางเรียกวิธีการในรูปแบบและรูปแบบที่มีคอลัมน์วันฐานข้อมูลage dobสิ่งนี้แตกต่างจากคำตอบอื่น ๆ เนื่องจากวิธีนี้ใช้สตริงเพื่อตรวจสอบว่าเราอยู่ก่อนวันเกิดของปีนี้หรือไม่

ตัวอย่างเช่นถ้าdobเป็น 2004/02/28 และtodayเป็น 2014/02/28, ageจะเป็นหรือ2014 - 2004 10ลอยจะเป็นและ0228 จะเป็นหรือ. สุดท้ายเราจะหักจากและได้รับ0229b4bday"0228" < "0229"true1age9

นี่จะเป็นวิธีเปรียบเทียบสองครั้งตามปกติ

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

วิธีนี้ใช้ได้เหมือนกัน แต่b4bdayบรรทัดยาวเกินไป 2016ปียังเป็นที่ไม่จำเป็น การเปรียบเทียบสตริงที่จุดเริ่มต้นเป็นผลลัพธ์

คุณยังสามารถทำได้

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

หากคุณไม่ได้ใช้รางลองสิ่งนี้

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

👍🏼


เพียงแค่ fyi คำตอบของคุณมีเหตุผลเหมือนกับของ philnash คำตอบของเขาสะอาดกว่ามากและไม่ต้องพึ่งพา Rails
Mantas

@Mantas Philnash กล่าวว่าเขาใช้วิธีนี้ในโครงการ Rails คุณมีรางในแท็ก วิธีการของเขามีการเปรียบเทียบมากกว่าของฉันสองครั้ง บรรทัดสุดท้ายของวิธีการของเขายากที่จะเข้าใจ
Cruz Nunez

ขออภัยรหัสของ philnash นั้นสะอาดกว่าของคุณมาก นอกจากนี้รหัสของเขาเป็นวิธีการง่ายๆ ของคุณขึ้นอยู่กับการมีค่า "dob" ซึ่งอาจไม่ชัดเจนนักสำหรับผู้ใช้ใหม่ และไม่ได้ช่วยโค้ดมากนัก ใช่ตัวอย่างสุดท้ายของคุณจะกำจัดมัน แต่ฟิลแนชเป็นเพียงชิ้นส่วนของรหัสที่เรียบง่ายและโง่เขลา
Mantas

0

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

DateHelper สามารถใช้เพื่อรับปีเท่านั้น

puts time_ago_in_words '1999-08-22'

เกือบ 20 ปี


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

ในการพิจารณาปีอธิกสุรทิน (และสมมติว่ามีการสนับสนุนที่ใช้งานอยู่):

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จะแก้ไขวันที่อย่างถูกต้องโดยคำนึงถึงปีที่ไม่ใช่ปีอธิกสุรทิน (เมื่อเป็นวันเกิด02-29)


-1

นี่คือวิธีแก้ปัญหาของฉันซึ่งช่วยให้สามารถคำนวณอายุ ณ วันที่ที่ระบุได้:

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

-2

ฉันต้องจัดการกับเรื่องนี้เช่นกัน แต่เป็นเวลาหลายเดือน กลายเป็นวิธีที่ซับซ้อนเกินไป วิธีที่ง่ายที่สุดที่ฉันคิดได้คือ:

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

คุณสามารถทำได้ใน 12 เดือน:

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

สิ่งนี้จะใช้คลาส Date เพื่อเพิ่มเดือนซึ่งจะจัดการกับ 28 วันและปีอธิกสุรทินเป็นต้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.