time: add missing timezone offset methods and tests

implement gmt_offset, utc_offset, and gmtoff methods as aliases to
complete the ruby time api. all three methods return timezone offset
in seconds, with utc times returning 0 and local times returning the
appropriate offset value.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-08-06 12:50:15 +09:00
parent a3efb96240
commit 16d3f31f82
2 changed files with 54 additions and 8 deletions
+36 -6
View File
@@ -1583,6 +1583,39 @@ time_saturday(mrb_state *mrb, mrb_value self)
return time_wday_p(mrb, self, 6);
}
/*
* ISO 15.2.19.7.12
* ISO 15.2.19.7.14
* ISO 15.2.19.7.29
*/
/*
* call-seq:
* time.gmt_offset -> integer
* time.utc_offset -> integer
* time.gmtoff -> integer
*
* Returns the offset in seconds between the timezone of time and UTC.
*
* Time.local(2000, 1, 1).gmt_offset #=> 32400 (JST: UTC+9)
* Time.utc(2000, 1, 1).utc_offset #=> 0 (UTC)
* Time.local(2000, 7, 1).gmtoff #=> 32400 (or 28800 if DST)
*/
static mrb_value
time_utc_offset(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm = time_get_ptr(mrb, self);
if (tm->timezone == MRB_TIMEZONE_UTC) {
return mrb_fixnum_value(0); /* UTC is always offset 0 */
}
/* For local times, calculate offset = local_time_t - utc_time_t */
time_t utc_time_t = timegm(&tm->datetime); /* Convert datetime as UTC */
mrb_int offset_seconds = (mrb_int)(tm->sec - utc_time_t);
return mrb_fixnum_value(offset_seconds);
}
void
mrb_mruby_time_gem_init(mrb_state* mrb)
{
@@ -1656,12 +1689,9 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
mrb_define_method_id(mrb, tc, MRB_SYM_Q(friday), time_friday, MRB_ARGS_NONE());
mrb_define_method_id(mrb, tc, MRB_SYM_Q(saturday), time_saturday, MRB_ARGS_NONE());
/*
methods not available:
gmt_offset(15.2.19.7.12)
gmtoff(15.2.19.7.14)
utc_offset(15.2.19.7.29)
*/
mrb_define_method_id(mrb, tc, MRB_SYM(gmt_offset), time_utc_offset, MRB_ARGS_NONE()); /* 15.2.19.7.12 */
mrb_define_method_id(mrb, tc, MRB_SYM(gmtoff), time_utc_offset, MRB_ARGS_NONE()); /* 15.2.19.7.14 */
mrb_define_method_id(mrb, tc, MRB_SYM(utc_offset), time_utc_offset, MRB_ARGS_NONE()); /* 15.2.19.7.29 */
}
void
+18 -2
View File
@@ -219,8 +219,24 @@ assert('Time#utc?', '15.2.19.7.28') do
assert_predicate(Time.at(1300000000).utc, :utc?)
end
# ATM not implemented
# assert('Time#utc_offset', '15.2.19.7.29') do
assert('Time#utc_offset, #gmt_offset, #gmtoff', '15.2.19.7.12, 15.2.19.7.14, 15.2.19.7.29') do
# UTC times should have zero offset
utc_time = Time.utc(2000, 1, 1)
assert_equal(0, utc_time.utc_offset)
# Local times should return integer offsets in seconds
local_time = Time.local(2000, 1, 1)
assert_kind_of(Integer, local_time.utc_offset)
# Test that offset values make sense (should be multiple of 900 seconds = 15 minutes)
assert_equal(0, local_time.utc_offset % 900)
# All three methods should be aliases returning identical values
assert_equal(utc_time.utc_offset, utc_time.gmt_offset)
assert_equal(utc_time.utc_offset, utc_time.gmtoff)
assert_equal(local_time.utc_offset, local_time.gmt_offset)
assert_equal(local_time.utc_offset, local_time.gmtoff)
end
assert('Time#wday', '15.2.19.7.30') do
assert_equal(0, Time.gm(2012, 12, 23).wday)