Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions ext/date/date_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2562,6 +2562,7 @@ valid_nth_kday_p(VALUE y, int m, int n, int k, double sg,
#endif

VALUE date_zone_to_diff(VALUE);
VALUE date_zone_to_diff_frac(VALUE, int *, long *);

static int
offset_to_sec(VALUE vof, int *rof)
Expand Down Expand Up @@ -2633,13 +2634,21 @@ offset_to_sec(VALUE vof, int *rof)
}
case T_STRING:
{
VALUE vs = date_zone_to_diff(vof);
long n;

if (!FIXNUM_P(vs))
return 0;
n = FIX2LONG(vs);
if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS)
int frac, ok = 0;
long whole, n = 0;
VALUE vs = date_zone_to_diff_frac(vof, &frac, &whole);

if (FIXNUM_P(vs)) {
n = FIX2LONG(vs);
if (n >= -DAY_IN_SECONDS && n <= DAY_IN_SECONDS)
ok = 1;
}
/* warn only where dropping the fraction would give an offset
* different from the one this returns today */
if (frac && (ok ? n : 0) != whole)
rb_warning("fraction of hour in a zone offset is deprecated"
" and will be ignored");
if (!ok)
return 0;
*rof = (int)n;
return 1;
Expand Down
20 changes: 19 additions & 1 deletion ext/date/date_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,12 +413,15 @@ shrink_space(char *d, const char *s, long l)
}

VALUE
date_zone_to_diff(VALUE str)
date_zone_to_diff_frac(VALUE str, int *fracp, long *whole)
{
VALUE offset = Qnil;
long l = RSTRING_LEN(str);
const char *s = RSTRING_PTR(str);

*fracp = 0;
*whole = 0;

{
int dst = 0;
int w;
Expand Down Expand Up @@ -533,6 +536,8 @@ date_zone_to_diff(VALUE str)
offset = rb_rational_num(offset);
}
}
*fracp = sec != 0;
*whole = hour * 3600;
goto ok;
}
else if (l > 2) {
Expand All @@ -558,6 +563,19 @@ date_zone_to_diff(VALUE str)
return offset;
}

VALUE
date_zone_to_diff(VALUE str)
{
int frac;
long whole;
VALUE offset = date_zone_to_diff_frac(str, &frac, &whole);

if (frac)
rb_warning("fraction of hour in a zone offset is deprecated"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the following block up to near the top of the file (copied from date_core.c):

#ifndef HAVE_RB_CATEGORY_WARN
#define rb_category_warn(category, fmt) rb_warn(fmt)
#endif

then switch to:

Suggested change
rb_warning("fraction of hour in a zone offset is deprecated"
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "fraction of hour in a zone offset is deprecated"

Similar change for the other warning, except there you would just move the block up in the same file.

" and will be ignored");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
" and will be ignored");
" and will be ignored in a future version");

Similar change for the other warning.

return offset;
}

static int
day_num(VALUE s)
{
Expand Down
40 changes: 40 additions & 0 deletions test/date/test_date_new.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'test/unit'
require 'date'
require 'stringio'

class TestDateNew < Test::Unit::TestCase

Expand Down Expand Up @@ -213,6 +214,45 @@ def test_civil__offset
assert_equal(0, d.offset)
end

def test_civil__fractional_zone_offset_deprecated
# A fraction that makes the offset non-integral is already dropped whole,
# so ignoring the fraction will not change these; only the pre-existing
# "invalid offset" warning may appear.
['+00.123', '-00.123'].each do |z|
out = capture_zone_warning { assert_equal(0, DateTime.civil(2001,2,3, 0,0,0, z).offset) }
assert_match(/invalid offset is ignored/, out)
refute_match(/fraction of hour/, out)
end

# Here the fraction reaches the result, or dropping it would give an
# offset different from the 0 returned today.
out = capture_zone_warning do
assert_equal(Rational(1800, 86400), DateTime.civil(2001,2,3, 0,0,0, '+00.5').offset)
end
assert_match(/fraction of hour/, out)

out = capture_zone_warning do
assert_equal(0, DateTime.civil(2001,2,3, 0,0,0, '+01.123').offset)
end
assert_match(/fraction of hour/, out)

# A zero fraction and the colon forms change nothing.
['+01.0', '+00:30'].each do |z|
out = capture_zone_warning { DateTime.civil(2001,2,3, 0,0,0, z) }
refute_match(/fraction of hour/, out)
end
end

def capture_zone_warning

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for this approach instead of assert_warning?

verbose, $VERBOSE = $VERBOSE, true
err, $stderr = $stderr, StringIO.new
yield
$stderr.string
ensure
$stderr = err
$VERBOSE = verbose
end

def test_civil__reform
d = Date.jd(Date::ENGLAND, Date::ENGLAND)
dt = DateTime.jd(Date::ENGLAND, 0,0,0,0, Date::ENGLAND)
Expand Down
20 changes: 20 additions & 0 deletions test/date/test_date_parse.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@

class TestDateParse < Test::Unit::TestCase

def test__parse_fractional_zone_offset_deprecated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be tests added for Date._strptime, which should have appropriate warnings

# The fractional hour is honoured today and will be ignored; warn only
# where dropping it changes the answer this returns now.
assert_warning(/fraction of hour/) do
assert_equal(Rational(2214, 5), Date._parse('2001-02-03T00:00:00+00.123')[:offset])
end
assert_warning(/fraction of hour/) do
assert_equal(1800, Date._parse('2001-02-03T00:00:00+00.5')[:offset])
end
# A zero fraction changes nothing, so it must stay quiet.
assert_warning('') do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should warn, because while the :offset entry returned may not change, the :zone entry should change to remove the .0

assert_equal(3600, Date._parse('2001-02-03T00:00:00+01.0')[:offset])
end
# The colon forms have no fractional hour at all.
assert_warning('') do
assert_equal(1800, Date._parse('2001-02-03T00:00:00+00:30')[:offset])
assert_equal(86399, Date._parse('2001-02-03T00:00:00+23:59:59')[:offset])
end
end

def test__parse
[
# ctime(3), asctime(3)
Expand Down