Skip to content

Commit

Permalink
Refactor Time methods (#6581)
Browse files Browse the repository at this point in the history
* Refactor and document Time.absolute_days

* Refactor and document Time.year_month_day_year

* Remove unncessary validation from Time.days_in_month

The value range is already checked in `leap_year?`
  • Loading branch information
straight-shoota authored and bcardiff committed Apr 5, 2019
1 parent b42631e commit b89ad33
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 35 deletions.
13 changes: 13 additions & 0 deletions spec/std/time/time_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,19 @@ describe Time do
end
end

it "#year" do
Time.utc(2008, 12, 31).year.should eq 2008
Time.utc(2000, 12, 31).year.should eq 2000
Time.utc(1900, 12, 31).year.should eq 1900
Time.utc(1800, 12, 31).year.should eq 1800
Time.utc(1700, 12, 31).year.should eq 1700
Time.utc(1600, 12, 31).year.should eq 1600
Time.utc(400, 12, 31).year.should eq 400
Time.utc(100, 12, 31).year.should eq 100
Time.utc(4, 12, 31).year.should eq 4
Time.utc(1, 1, 1).year.should eq 1
end

describe "#to_s" do
it "prints string" do
with_zoneinfo do
Expand Down
79 changes: 44 additions & 35 deletions src/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -991,10 +991,6 @@ struct Time
raise ArgumentError.new "Invalid month"
end

unless 1 <= year <= 9999
raise ArgumentError.new "Invalid year"
end

days = leap_year?(year) ? DAYS_MONTH_LEAP : DAYS_MONTH
days[month]
end
Expand Down Expand Up @@ -1402,17 +1398,25 @@ struct Time
end
{% end %}

protected def self.absolute_days(year, month, day)
days = leap_year?(year) ? DAYS_MONTH_LEAP : DAYS_MONTH
# Returns the number of days from `0001-01-01` to the date indicated
# by *year*, *month*, *day* in the proleptic Gregorian calendar.
#
# The valid range for *year* is `1..9999` and for *month* `1..12`. The value
# of *day* is not validated and can exceed the number of days in the specified
# month or even a year.
protected def self.absolute_days(year, month, day) : Int32
days_per_month = leap_year?(year) ? DAYS_MONTH_LEAP : DAYS_MONTH

temp = 0
m = 1
while m < month
temp += days[m]
m += 1
days_in_year = day - 1
month_index = 1
while month_index < month
days_in_year += days_per_month[month_index]
month_index += 1
end

(day - 1) + temp + (365*(year - 1)) + ((year - 1)/4) - ((year - 1)/100) + ((year - 1)/400)
year -= 1

year * 365 + year / 4 - year / 100 + year / 400 + days_in_year
end

protected def total_seconds
Expand All @@ -1423,48 +1427,53 @@ struct Time
@seconds + offset
end

protected def year_month_day_day_year
m = 1

days = DAYS_MONTH
totaldays = offset_seconds / SECONDS_PER_DAY
# Returns the calendrical representation of this instance's date.
#
# The return value is a tuple consisting of year (`1..9999`), month (`1..12`),
# day (`1..31`) and ordinal day of the year (`1..366`).
protected def year_month_day_day_year : {Int32, Int32, Int32, Int32}
total_days = (offset_seconds / SECONDS_PER_DAY).to_i

num400 = totaldays / DAYS_PER_400_YEARS
totaldays -= num400 * DAYS_PER_400_YEARS
num400 = total_days / DAYS_PER_400_YEARS
total_days -= num400 * DAYS_PER_400_YEARS

num100 = totaldays / DAYS_PER_100_YEARS
num100 = total_days / DAYS_PER_100_YEARS
if num100 == 4 # leap
num100 = 3
end
totaldays -= num100 * DAYS_PER_100_YEARS
total_days -= num100 * DAYS_PER_100_YEARS

num4 = totaldays / DAYS_PER_4_YEARS
totaldays -= num4 * DAYS_PER_4_YEARS

numyears = totaldays / 365
num4 = total_days / DAYS_PER_4_YEARS
total_days -= num4 * DAYS_PER_4_YEARS

numyears = total_days / 365
if numyears == 4 # leap
numyears = 3
end
total_days -= numyears * 365

year = num400*400 + num100*100 + num4*4 + numyears + 1
year = num400 * 400 + num100 * 100 + num4 * 4 + numyears + 1

totaldays -= numyears * 365
day_year = totaldays + 1
ordinal_day_in_year = total_days + 1

if (numyears == 3) && ((num100 == 3) || !(num4 == 24)) # 31 dec leapyear
days = DAYS_MONTH_LEAP
days_per_month = DAYS_MONTH_LEAP
else
days_per_month = DAYS_MONTH
end

while totaldays >= days[m]
totaldays -= days[m]
m += 1
month = 1
while true
days_in_month = days_per_month[month]
break if total_days < days_in_month

total_days -= days_in_month
month += 1
end

month = m
day = totaldays + 1
day = total_days + 1

{year.to_i, month.to_i, day.to_i, day_year.to_i}
{year, month, day, ordinal_day_in_year}
end

protected def self.zone_offset_at(seconds, location)
Expand Down

0 comments on commit b89ad33

Please sign in to comment.