From 9b938e0378c53316a6f818afbf845cc5373c70da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 21 Aug 2018 12:25:33 +0200 Subject: [PATCH 1/3] Refactor and document Time.absolute_days --- src/time.cr | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/time.cr b/src/time.cr index 49be144463aa..7bb2e495de5f 100644 --- a/src/time.cr +++ b/src/time.cr @@ -1413,17 +1413,25 @@ struct Time end {% end %} - protected def self.absolute_days(year, month, day) - days = leap_year?(year) ? DAYS_MONTH_LEAP : DAYS_MONTH - - temp = 0 - m = 1 - while m < month - temp += days[m] - m += 1 + # 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 + + 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 From 0f4309afb3553d928bc1ad27540f40c92c1c7889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 21 Aug 2018 14:05:38 +0200 Subject: [PATCH 2/3] Refactor and document Time.year_month_day_year --- spec/std/time/time_spec.cr | 13 ++++++++++ src/time.cr | 51 +++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/spec/std/time/time_spec.cr b/spec/std/time/time_spec.cr index f7c8ef67daf6..c5e5b2ea3e75 100644 --- a/spec/std/time/time_spec.cr +++ b/spec/std/time/time_spec.cr @@ -495,6 +495,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 diff --git a/src/time.cr b/src/time.cr index 7bb2e495de5f..f7042ec6d6cd 100644 --- a/src/time.cr +++ b/src/time.cr @@ -1442,48 +1442,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 - - num4 = totaldays / DAYS_PER_4_YEARS - totaldays -= num4 * DAYS_PER_4_YEARS + total_days -= num100 * DAYS_PER_100_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) From fa4aa636f6a9a256c8954d5432821054a64950cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 21 Aug 2018 14:07:46 +0200 Subject: [PATCH 3/3] Remove unncessary validation from Time.days_in_month The value range is already checked in `leap_year?` --- src/time.cr | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/time.cr b/src/time.cr index f7042ec6d6cd..d4736622a2cf 100644 --- a/src/time.cr +++ b/src/time.cr @@ -1000,10 +1000,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