# Time with arbitrary offset

Time e\$B\$G!"%*%V%8%’%/%HKh\$KG\$0U\$N;~:9\$r;XDj\$G\$-\$k\$h\$&\$K\$9\$k\$Ne(B
e\$B\$O\$I\$&\$G\$7\$g\$&\$+!#e(B

e\$B\$?\$H\$(\$P!"\$R\$H\$D\$NMQK!\$H\$7\$F!“e(B[ruby-core:23036] e\$B\$G\$b=R\$Y\$^\$7e(B
e\$B\$?\$,!“e(Bweb application e\$B\$G;~9o\$r%f!<%6\$KI=<(\$9\$k:]!”%f!<%6\$N;Xe(B
e\$BDj\$7\$?%?%\$%`%>!<%s\$,\$”\$l\$P!“e(BTime e\$B%*%V%8%’%/%H\$K\$=\$N;~:[email protected]_e(B
e\$BDj\$7\$FI=<(\$K;H\$&!”\$H\$\$\$&\$b\$N\$,9M\$(\$i\$l\$^\$9!#e(B

e\$B\$^\$?!“J8;zNs\$G<u\$1<h\$C\$?;~9o\$r!”\$=\$N;~:9\$rJ]B8\$7\$?\$^\$^e(B Time
e\$B%*%V%8%’%/%H\$K\$9\$k\$3\$H\$b\$G\$-\$k\$h\$&\$K\$J\$j\$^\$9!#e(B

e\$B\$H\$j\$"\$(\$:e(B API e\$B\$H\$7\$F\$Oe(B

• e\$BF|IU\$H;~:9\$+\$ie(B Time e\$B%*%V%8%’%/%[email protected]@.\$9\$k%a%=%C%I\$H!"e(B
• e\$B;~:9\$rJQ\$(\$?e(B Time e\$B%*%V%8%’%/%[email protected]@.\$9\$k%a%=%C%Ie(B
e\$B\$,I,MW\$G\$O\$J\$\$\$+\$H;W\$\$\$^\$9!#e(B

e\$B\$H\$\$\$&\$o\$1\$G!"\$=\$l\$>\$l\$KBP1~\$9\$ke(B

• Time.civil(year, mon, day, hour, min, sec, utc_offset) -> time
• Time#getlocal(utc_offset) -> time
e\$B\$H\$\$\$&\$N\$rDI2C\$9\$k\$N\$O\$I\$&\$G\$7\$g\$&\$+!#e(B
(year e\$B0J30\$N0z?t\$O>JN,2DG=\$G\$9e(B)

civil e\$B\$H\$\$\$&\$N\$O!"e(BDateTime.civil e\$B\$+\$i\$H\$j\$^\$7\$?!#e(B

Time.local e\$B\$"\$?\$j\$r3HD%\$9\$k\$3\$H\$b9M\$(\$?\$s\$G\$9\$,!"e(BParseDate
e\$BBP1~e(B [ruby-core:761] e\$B\$H\$N7s\$M9g\$\$\$G\$&\$^\$\$\$d\$j\$+\$?\$r;W\$\$\$D\$-e(B
e\$B\$^\$;\$s\$G\$7\$?!#2>\$K\$=\$A\$i\$G\$&\$^\$\$\$d\$j\$+\$?\$r;W\$\$\$D\$\$\$F:n\$k\$3\$He(B
e\$B\$K\$J\$C\$?\$H\$7\$F\$b!“e(BDateTime e\$B\$HB?BV\$J%a%=%C%I\$,\$”\$k\$N\$ONI\$\$\$3e(B
[email protected]\$H;W\$\$\$^\$9!#e(B
(e\$B\$J\$*!“e(BDateTime.civil e\$B\$O\$b\$&\$R\$H\$De(B start
e\$B\$H\$\$\$&0z?t\$,\$”\$je(B
e\$B\$^\$9\$,!"e(BTime e\$B\$G\$O2~Nq\$O07\$o\$J\$\$\$N\$G!“e(BTime.civil
e\$B\$K\$O\$”\$j\$^\$;e(B
e\$B\$s!#e(B)

# % svn diff --diff-cmd diff -x ‘-u -p’ Index: time.c

— time.c (revision 23276)
+++ time.c (working copy)
@@ -59,6 +59,7 @@ static const char *find_time_t(struct tm
static struct vtm *localtimev(VALUE timev, struct vtm *result);

static int leap_year_p(long y);
+#define leap_year_v_p(y) leap_year_p(NUM2LONG(mod(v, INT2FIX(400))))

#define NDIV(x,y) (-(-((x)+1)/(y))-1)
#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
@@ -1002,6 +1003,12 @@ struct time_object {
#define TIME_LOCALTIME_P(tobj) ((tobj)->gmt == 0)
#define TIME_SET_LOCALTIME(tobj) ((tobj)->gmt = 0)

+#define TIME_FIXOFF_P(tobj) ((tobj)->gmt == 2)
+#define TIME_SET_FIXOFF(tobj, off) \

• ((tobj)->gmt = 2, \
• ``````(tobj)->vtm.utc_offset = (off), \
``````
• ``````(tobj)->vtm.zone = NULL)
``````

#define TIME_COPY_GMT(tobj1, tobj2) ((tobj1)->gmt = (tobj2)->gmt)

static VALUE time_get_tm(VALUE, struct time_object *);
@@ -1452,6 +1459,13 @@ month_arg(VALUE arg)
}

static void
+validate_utc_offset(VALUE utc_offset)
+{

• if (le(utc_offset, INT2FIX(-86400)) || ge(utc_offset,
INT2FIX(86400)))
• rb_raise(rb_eArgError, “utc_offset out of range”);
+}

+static void
validate_vtm(struct vtm *vtm)
{
if ( vtm->mon < 1 || vtm->mon > 12
@@ -1460,7 +1474,8 @@ validate_vtm(struct vtm *vtm)
|| (vtm->hour == 24 && (vtm->min > 0 || vtm->sec > 0))
|| vtm->min < 0 || vtm->min > 59
|| vtm->sec < 0 || vtm->sec > 60

• || lt(vtm->subsec, INT2FIX(0)) || ge(vtm->subsec, INT2FIX(1)))
• ``````   || lt(vtm->subsec, INT2FIX(0)) || ge(vtm->subsec, INT2FIX(1))
``````
• ``````   || (!NIL_P(vtm->utc_offset) &&
``````

(validate_utc_offset(vtm->utc_offset), 0)))
rb_raise(rb_eArgError, “argument out of range”);
}

@@ -1945,6 +1960,240 @@ time_s_mktime(int argc, VALUE *argv, VAL
return time_utc_or_local(argc, argv, Qfalse, klass);
}

+static VALUE
+time_set_utc_offset(VALUE time, VALUE off)
+{

• struct time_object *tobj;
• off = num_exact(off);
• time_modify(time);
• GetTimeval(time, tobj);
• tobj->tm_got = 0;
• TIME_SET_FIXOFF(tobj, off);
• return time;
+}

+static void
+{

• int sign;
• VALUE subsec, v;
• int sec, min, hour;
• int day;
• vtm->utc_offset = sub(vtm->utc_offset, off);
• if (RTEST(lt(off, INT2FIX(0)))) {
• ``````   sign = -1;
``````
• ``````   off = neg(off);
``````
• }
• else {
• ``````   sign = 1;
``````
• }
• divmodv(off, INT2FIX(1), &off, &subsec);
• divmodv(off, INT2FIX(60), &off, &v);
• sec = NUM2INT(v);
• divmodv(off, INT2FIX(60), &off, &v);
• min = NUM2INT(v);
• divmodv(off, INT2FIX(24), &off, &v);
• hour = NUM2INT(v);
• if (sign < 0) {
• ``````   subsec = neg(subsec);
``````
• ``````   sec = -sec;
``````
• ``````   min = -min;
``````
• ``````   hour = -hour;
``````
• }
• day = 0;
• if (!rb_equal(subsec, INT2FIX(0))) {
• ``````   vtm->subsec = add(vtm->subsec, subsec);
``````
• ``````   if (lt(vtm->subsec, INT2FIX(0))) {
``````
• ``````       vtm->subsec = add(vtm->subsec, INT2FIX(1));
``````
• ``````       sec -= 1;
``````
• ``````   }
``````
• ``````   if (le(INT2FIX(1), vtm->subsec)) {
``````
• ``````       vtm->subsec = sub(vtm->subsec, INT2FIX(1));
``````
• ``````       sec += 1;
``````
• ``````   }
``````
• ``````   goto not_zero_sec;
``````
• }
• if (sec) {
• `````` not_zero_sec:
``````
• ``````   /* If sec + subsec == 0, don't change vtm->sec.
``````
• ``````    * It may be 60 which is a leap second. */
``````
• ``````   vtm->sec += sec;
``````
• ``````   if (vtm->sec < 0) {
``````
• ``````       vtm->sec += 60;
``````
• ``````       min -= 1;
``````
• ``````   }
``````
• ``````   if (60 <= vtm->sec) {
``````
• ``````       vtm->sec -= 60;
``````
• ``````       min += 1;
``````
• ``````   }
``````
• }
• if (min) {
• ``````   vtm->min += min;
``````
• ``````   if (vtm->min < 0) {
``````
• ``````       vtm->min += 60;
``````
• ``````       hour -= 1;
``````
• ``````   }
``````
• ``````   if (60 <= vtm->min) {
``````
• ``````       vtm->min -= 60;
``````
• ``````       hour += 1;
``````
• ``````   }
``````
• }
• if (hour) {
• ``````   vtm->hour += hour;
``````
• ``````   if (vtm->hour < 0) {
``````
• ``````       vtm->hour += 24;
``````
• ``````       day = -1;
``````
• ``````   }
``````
• ``````   if (24 <= vtm->hour) {
``````
• ``````       vtm->hour -= 24;
``````
• ``````       day = 1;
``````
• ``````   }
``````
• }
• if (day) {
• ``````   if (day < 0) {
``````
• ``````       if (vtm->mon == 1 && vtm->mday == 1) {
``````
• ``````           vtm->mday = 31;
``````
• ``````           vtm->mon = 12; /* December */
``````
• ``````           vtm->year = sub(vtm->year, INT2FIX(1));
``````
• ``````           vtm->yday = leap_year_v_p(vtm->year) ? 365 : 364;
``````
• ``````       }
``````
• ``````       else if (vtm->mday == 1) {
``````
• ``````           const int *days_in_month = leap_year_v_p(vtm->year) ?
``````
• ``````                                      leap_year_days_in_month :
``````
• ``````                                      common_year_days_in_month;
``````
• ``````           vtm->mon--;
``````
• ``````           vtm->mday = days_in_month[vtm->mon-1];
``````
• ``````           vtm->yday--;
``````
• ``````       }
``````
• ``````       else {
``````
• ``````           vtm->mday--;
``````
• ``````           vtm->yday--;
``````
• ``````       }
``````
• ``````       vtm->wday = (vtm->wday + 6) % 7;
``````
• ``````   }
``````
• ``````   else {
``````
• ``````       int leap = leap_year_v_p(vtm->year);
``````
• ``````       if (vtm->mon == 12 && vtm->mday == 31) {
``````
• ``````           vtm->year = add(vtm->year, INT2FIX(1));
``````
• ``````           vtm->mon = 1; /* January */
``````
• ``````           vtm->mday = 1;
``````
• ``````           vtm->yday = 1;
``````
• ``````       }
``````
• ``````       else if (vtm->mday == (leap ? leap_year_days_in_month :
``````

common_year_days_in_month)[vtm->mon-1]) {

• ``````           vtm->mon++;
``````
• ``````           vtm->mday = 1;
``````
• ``````           vtm->yday++;
``````
• ``````       }
``````
• ``````       else {
``````
• ``````           vtm->mday++;
``````
• ``````           vtm->yday++;
``````
• ``````       }
``````
• ``````       vtm->wday = (vtm->wday + 1) % 7;
``````
• ``````   }
``````
• }
+}

+static VALUE
+utc_offset_arg(VALUE arg)
+{

• VALUE tmp;
• if (!NIL_P(tmp = rb_check_string_type(arg))) {
• ``````   int n;
``````
• ``````   char *s = RSTRING_PTR(tmp);
``````
• ``````   if (!rb_enc_str_asciicompat_p(tmp) ||
``````
• ``````       RSTRING_LEN(tmp) != 6 ||
``````
• ``````       (s[0] != '+' && s[0] != '-') ||
``````
• ``````       !ISDIGIT(s[1]) ||
``````
• ``````       !ISDIGIT(s[2]) ||
``````
• ``````       s[3] != ':' ||
``````
• ``````       !ISDIGIT(s[4]) ||
``````
• ``````       !ISDIGIT(s[5]))
``````
• ``````       rb_raise(rb_eArgError, "\"+HH:MM\" or \"-HH:MM\" expected
``````

for utc_offset");

• ``````   n = strtol(s+1, NULL, 10) * 3600;
``````
• ``````   n += strtol(s+4, NULL, 10) * 60;
``````
• ``````   if (s[0] == '-')
``````
• ``````       n = -n;
``````
• ``````   return INT2FIX(n);
``````
• }
• else {
• ``````   return num_exact(arg);
``````
• }
+}

+static VALUE
+time_s_civil(int argc, VALUE *argv, VALUE klass)
+{

• struct vtm vtm;
• VALUE time;
• VALUE v[7];
• int i;
• vtm.wday = -1;
• vtm.yday = 0;
• vtm.zone = “”;
• /* year mon mday hour min sec
off */
• rb_scan_args(argc, argv, “16”,
&v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6]);
• vtm.year = obj2vint(v[0]);
• vtm.mon = NIL_P(v[1]) ? 1 : month_arg(v[1]);
• vtm.mday = NIL_P(v[2]) ? 1 : obj2long(v[2]);
• vtm.hour = NIL_P(v[3]) ? 0 : obj2long(v[3]);
• vtm.min = NIL_P(v[4]) ? 0 : obj2long(v[4]);
• vtm.sec = 0;
• vtm.subsec = INT2FIX(0);
• if (!NIL_P(v[5])) {
• ``````   VALUE sec = num_exact(v[5]);
``````
• ``````   VALUE subsec;
``````
• ``````   divmodv(sec, INT2FIX(1), &sec, &subsec);
``````
• ``````   vtm.sec = NUM2INT(sec);
``````
• ``````   vtm.subsec = subsec;
``````
• }
• vtm.isdst = -1;
• vtm.utc_offset = Qnil;
• if (!NIL_P(v[6])) {
• ``````   VALUE arg = v[6];
``````
• ``````   VALUE tmp;
``````
• ``````   if (arg == ID2SYM(rb_intern("dst")))
``````
• ``````       vtm.isdst = 1;
``````
• ``````   else if (arg == ID2SYM(rb_intern("std")))
``````
• ``````       vtm.isdst = 0;
``````
• ``````   else
``````
• ``````       vtm.utc_offset = utc_offset_arg(arg);
``````
• }
• validate_vtm(&vtm);
• if (!NIL_P(vtm.utc_offset)) {
• ``````   VALUE off = vtm.utc_offset;
``````
• ``````   vtm_add_offset(&vtm, neg(off));
``````
• ``````   vtm.utc_offset = Qnil;
``````
• ``````   time = time_new_timev(klass, timegmv(&vtm));
``````
• ``````   return time_set_utc_offset(time, off);
``````
• }
• else {
• ``````   time = time_new_timev(klass, timelocalv(&vtm));
``````
• ``````   return time_localtime(time);
``````
• }
+}

/*

• call-seq:
• ``````time.to_i   => int
``````

@@ -2281,6 +2530,38 @@ time_gmtime(VALUE time)
return time;
}

+static VALUE
+time_fixoff(VALUE time)
+{

• struct time_object *tobj;
• struct vtm vtm;
• VALUE off;
• GetTimeval(time, tobj);
• if (TIME_FIXOFF_P(tobj)) {
• ``````  if (tobj->tm_got)
``````
• ``````      return time;
``````
• }
• else {
• ``````  time_modify(time);
``````
• }
• if (TIME_FIXOFF_P(tobj))
• ``````   off = tobj->vtm.utc_offset;
``````
• else
• ``````   off = INT2FIX(0);
``````
• if (!gmtimev(tobj->timev, &vtm))
• ``````  rb_raise(rb_eArgError, "gmtime error");
``````
• tobj->vtm = vtm;
• tobj->tm_got = 1;
• TIME_SET_FIXOFF(tobj, off);
• return time;
+}

/*

• call-seq:
• ``````time.getlocal => new_time
``````

@@ -2296,8 +2577,20 @@ time_gmtime(VALUE time)
*/

static VALUE
-time_getlocaltime(VALUE time)
+time_getlocaltime(int argc, VALUE *argv, VALUE time)
{

• VALUE off;
• rb_scan_args(argc, argv, “01”, &off);
• if (!NIL_P(off)) {
• ``````   off = utc_offset_arg(off);
``````
• ``````   validate_utc_offset(off);
``````
• ``````   time = time_dup(time);
``````
• ``````   time_set_utc_offset(time, off);
``````
• ``````   return time_fixoff(time);
``````
• }
• return time_localtime(time_dup(time));
}

@@ -2326,6 +2619,7 @@ static VALUE
time_get_tm(VALUE time, struct time_object *tobj)
{
if (TIME_UTC_P(tobj)) return time_gmtime(time);

• if (TIME_FIXOFF_P(tobj)) return time_fixoff(time);
return time_localtime(time);
}

@@ -2829,6 +3123,8 @@ time_zone(VALUE time)
if (TIME_UTC_P(tobj)) {
return rb_str_new2(“UTC”);
}

• if (tobj->vtm.zone == NULL)
• ``````   return Qnil;
``````
return rb_str_new2(tobj->vtm.zone);
}

@@ -3316,6 +3612,7 @@ Init_Time(void)
rb_define_singleton_method(rb_cTime, “gm”, time_s_mkutc, -1);
rb_define_singleton_method(rb_cTime, “local”, time_s_mktime, -1);
rb_define_singleton_method(rb_cTime, “mktime”, time_s_mktime, -1);

• rb_define_singleton_method(rb_cTime, “civil”, time_s_civil, -1);

rb_define_method(rb_cTime, “to_i”, time_to_i, 0);
rb_define_method(rb_cTime, “to_f”, time_to_f, 0);
@@ -3328,7 +3625,7 @@ Init_Time(void)
rb_define_method(rb_cTime, “localtime”, time_localtime, 0);
rb_define_method(rb_cTime, “gmtime”, time_gmtime, 0);
rb_define_method(rb_cTime, “utc”, time_gmtime, 0);

• rb_define_method(rb_cTime, “getlocal”, time_getlocaltime, 0);
• rb_define_method(rb_cTime, “getlocal”, time_getlocaltime, -1);
rb_define_method(rb_cTime, “getgm”, time_getgmtime, 0);
rb_define_method(rb_cTime, “getutc”, time_getgmtime, 0);

e\$B\$^\$D\$b\$He(B e\$B\$f\$-\$R\$m\$G\$9e(B

In message “Re: [ruby-dev:38326] Time with arbitrary offset”
on Sat, 25 Apr 2009 22:10:05 +0900, Tanaka A. [email protected]
writes:

|Time e\$B\$G!"%*%V%8%’%/%HKh\$KG\$0U\$N;~:9\$r;XDj\$G\$-\$k\$h\$&\$K\$9\$k\$Ne(B
|e\$B\$O\$I\$&\$G\$7\$g\$&\$+!#e(B

e\$B86B’E*\$K;[email protected]\$G\$9!#e(B

|e\$B\$H\$\$\$&\$o\$1\$G!"\$=\$l\$>\$l\$KBP1~\$9\$ke(B
|* Time.civil(year, mon, day, hour, min, sec, utc_offset) -> time
|* Time#getlocal(utc_offset) -> time
|e\$B\$H\$\$\$&\$N\$rDI2C\$9\$k\$N\$O\$I\$&\$G\$7\$g\$&\$+!#e(B
|(year e\$B0J30\$N0z?t\$O>JN,2DG=\$G\$9e(B)
|
|civil e\$B\$H\$\$\$&\$N\$O!"e(BDateTime.civil e\$B\$+\$i\$H\$j\$^\$7\$?!#e(B

[email protected]\$7!“e(Bcivile\$B\$H\$\$\$&%a%=%C%IL>\$K\$OH?BP\$G\$9!#e(BDatee\$B%/%i%9\$K\$O!”\$^e(B
Datee\$B\$H\$\$\$&35G0\$,\$"\$C\$FBP1~\$9\$ke(Bcivile\$B%ae(B
e\$B%=%C%I\$,\$"\$k\$HM}2r\$7\$F\$\$\$^\$9\$,!";d\$NCN\$k8B\$je(Bcivil timee\$B\$H\$\$\$&e(B
e\$B35G0\$O\$J\$\$\$7!“2>\$K\$”\$C\$?\$H\$7\$F\$b\$=\$l\$O!VG\$0U\$N;~:9!W\$H\$O4X78e(B
[email protected]\$H;W\$\$\$^\$9!#e(B

e\$B\$5\$i\$Ke(Bgetlocale\$B%a%=%C%I\$N?6\$kIq\$\$\$b\$h\$/\$o\$+\$j\$^\$;\$s\$G\$7\$?!#F1e(B
e\$B\$8;~4V\$G;~:9\$N0c\$&%*%V%8%’%/%H\$rJV\$9\$C\$F\$3\$H!)e(B e\$B\$=\$l\$b\$A\$g\$Ce(B
e\$B\$HL>A0\$+\$iM=A[\$5\$l\$k5sF0\$H\$O0c\$&\$+\$b\$7\$l\$^\$;\$s!#e(B

e\$B\$"\$H\$O!“e(Bstrftimee\$B\$Ne(B%Ze\$B\$r\$I\$&\$9\$k\$+\$J\$s\$F\$3\$H\$b5\$\$K\$J\$i\$J\$\$\$G\$be(B
e\$B\$J\$\$\$G\$9\$,!”[email protected]\$1\$N\$3\$H\$G\$9\$M!#e(B

In article [email protected],
Yukihiro M. [email protected] writes:

[email protected]\$7!“e(Bcivile\$B\$H\$\$\$&%a%=%C%IL>\$K\$OH?BP\$G\$9!#e(BDatee\$B%/%i%9\$K\$O!”\$^e(B
e\$B%=%C%I\$,\$"\$k\$HM}2r\$7\$F\$\$\$^\$9\$,!";d\$NCN\$k8B\$je(Bcivil timee\$B\$H\$\$\$&e(B
e\$B35G0\$O\$J\$\$\$7!“2>\$K\$”\$C\$?\$H\$7\$F\$b\$=\$l\$O!VG\$0U\$N;~:9!W\$H\$O4X78e(B
[email protected]\$H;W\$\$\$^\$9!#e(B

e\$B\$G\$O!"e(B
Time.new(year, mon, day, hour, min, sec, utc_offset)
e\$B\$J\$i\$I\$&\$G\$7\$g\$&\$+e(B?

e\$B8=:_\$Ne(B Time.new (e\$B\$H\$\$\$&\$+e(B Time#initialize)
e\$B\$O0z?t\$r\$H\$i\$J\$\$e(B
e\$B\$N\$G!"[email protected]\$r5/\$3\$5\$:\$K3HD%\$9\$k\$3\$H\$,\$G\$-\$^\$9!#e(B

e\$B\$r40F\$9\$k\$H\$\$\$&\$N\$O\$h\$/0UL#\$,\$o\$+e(B
e\$B\$j\$^\$;\$s!#>/\$J\$/\$H\$b!"5
85A0e(B1e\$BG/\$G\$”[email protected]>Nqe(B0e\$BG/\$O\$=\$N\$^e(B
e\$B\$^DL\$k\$h\$&\$G\$9!#e(B

% ruby -rdate -e ‘p Date.civil(0,1,1)’
#<Date: 0000-01-01 (3442115/2,0,2299161)>

e\$B\$J\$*!";d\$Oe(B civil e\$B\$H\$\$\$&8l\$r\$3\$3\$GMQ\$\$\$kM}M3\$O\$h\$/\$o\$+\$C\$F\$\$e(B
e\$B\$J\$\$\$s\$G\$9\$,!“8!:w\$9\$k\$He(B Civil calendar e\$B\$H\$\$\$&\$N\$,\$”\$k\$N\$G!“e(B
e\$B\$3\$l\$J\$N\$+\$J\$!!”\$HA[A|\$7\$F\$\$\$^\$9!#e(B
http://en.wikipedia.org/wiki/Civil_calendar

e\$B\$5\$i\$Ke(Bgetlocale\$B%a%=%C%I\$N?6\$kIq\$\$\$b\$h\$/\$o\$+\$j\$^\$;\$s\$G\$7\$?!#F1e(B
e\$B\$8;~4V\$G;~:9\$N0c\$&%*%V%8%’%/%H\$rJV\$9\$C\$F\$3\$H!)e(B e\$B\$=\$l\$b\$A\$g\$Ce(B
e\$B\$HL>A0\$+\$iM=A[\$5\$l\$k5sF0\$H\$O0c\$&\$+\$b\$7\$l\$^\$;\$s!#e(B

time.getlocal e\$B\$OF1\$8;~9o\$GCOJ};~\$N%%V%8%’%/%H\$rJV\$9\$N\$G!“e(B
time.getlocal(”+09:00") e\$B\$J\$I\$H;XDj\$9\$l\$P!"F1\$8;~9o\$G;XDj\$7\$?e(B
e\$B;~:9\$N%
%V%8%’%/%H\$rJV\$9\$N\$O\$=\$l\$J\$j\$K<+A3\$J3HD%[email protected]\$H;W\$C\$F\$\$e(B
e\$B\$^\$9!#e(B

% TZ=EST5EDT ./ruby -e ’
t = Time.utc(2000,1,1)
p t
p t.getlocal
p t.getlocal("+09:00")
p t.getlocal(3600*9)

2000-01-01 00:00:00 UTC
1999-12-31 19:00:00 -0500
2000-01-01 09:00:00 +0900
2000-01-01 09:00:00 +0900

e\$B\$3\$&\$G\$J\$\$\$H\$9\$k\$H!"\$I\$&\$\$\$&M=A[\$r\$9\$k\$H;W\$C\$?\$s\$G\$9\$+e(B?

e\$B\$"\$H\$O!“e(Bstrftimee\$B\$Ne(B%Ze\$B\$r\$I\$&\$9\$k\$+\$J\$s\$F\$3\$H\$b5\$\$K\$J\$i\$J\$\$\$G\$be(B
e\$B\$J\$\$\$G\$9\$,!”[email protected]\$1\$N\$3\$H\$G\$9\$M!#e(B

e\$B\$"\$!!"\$=\$l\$OAw\$C\$?8e\$K5\$\$,\$D\$\$\$F!"<j85\$G\$O\$H\$j\$"\$(\$:6uJ8;zNse(B
e\$B\$K\$J\$k\$h\$&\$K\$7\$F\$"\$j\$^\$9!#e(B

e\$B\$^\$D\$b\$He(B e\$B\$f\$-\$R\$m\$G\$9e(B

In message “Re: [ruby-dev:38341] Re: Time with arbitrary offset”
on Mon, 27 Apr 2009 12:56:58 +0900, Tanaka A. [email protected]
writes:

|e\$B\$G\$O!"e(B
|Time.new(year, mon, day, hour, min, sec, utc_offset)
|e\$B\$J\$i\$I\$&\$G\$7\$g\$&\$+e(B?
|
|e\$B8=:_\$Ne(B Time.new (e\$B\$H\$\$\$&\$+e(B Time#initialize) e\$B\$O0z?t\$r\$H\$i\$J\$\$e(B
|e\$B\$N\$G!"[email protected]\$r5/\$3\$5\$:\$K3HD%\$9\$k\$3\$H\$,\$G\$-\$^\$9!#e(B

e\$B\$3\$C\$A\$K\$O;[email protected]\$G\$9!#e(B

|e\$B\$j\$^\$;\$s!#>/\$J\$/\$H\$b!"5
85A0e(B1e\$BG/\$G\$”[email protected]>Nqe(B0e\$BG/\$O\$=\$N\$^e(B
|e\$B\$^DL\$k\$h\$&\$G\$9!#e(B

e\$B;d\$b\$h\$/\$OJ,\$+\$C\$F\$J\$\$\$N\$G\$9\$,!"E7J8\$H\$+\$G;H\$o\$l\$ke(B(e\$B\$i\$7\$\$e(B)e\$B%fe(B
e\$B%j%&%9Nq\$KBP\$7\$Fe(BCivile\$BNq\$J\$s\$8\$c\$J\$\$\$+\$H?dB,\$7\$^\$9!#\$\$\$:\$l\$Ke(B
e\$B\$7\$F\$b!"e(BTimee\$B%/%i%9\$H\$OL51o\$N35G0\$G\$9\$M!#e(B

|> e\$B\$5\$i\$Ke(Bgetlocale\$B%a%=%C%I\$N?6\$kIq\$\$\$b\$h\$/\$o\$+\$j\$^\$;\$s\$G\$7\$?!#F1e(B
|> e\$B\$8;~4V\$G;~:9\$N0c\$&%%V%8%’%/%H\$rJV\$9\$C\$F\$3\$H!)e(B e\$B\$=\$l\$b\$A\$g\$Ce(B
|> e\$B\$HL>A0\$+\$iM=A[\$5\$l\$k5sF0\$H\$O0c\$&\$+\$b\$7\$l\$^\$;\$s!#e(B
|
|time.getlocal e\$B\$OF1\$8;~9o\$GCOJ};~\$N%
%V%8%’%/%H\$rJV\$9\$N\$G!“e(B
|time.getlocal(”+09:00") e\$B\$J\$I\$H;XDj\$9\$l\$P!"F1\$8;~9o\$G;XDj\$7\$?e(B
|e\$B;~:9\$N%*%V%8%’%/%H\$rJV\$9\$N\$O\$=\$l\$J\$j\$K<+A3\$J3HD%[email protected]\$H;W\$C\$F\$\$e(B
|e\$B\$^\$9!#e(B

|e\$B\$3\$&\$G\$J\$\$\$H\$9\$k\$H!"\$I\$&\$\$\$&M=A[\$r\$9\$k\$H;W\$C\$?\$s\$G\$9\$+e(B?

Timee\$B%/%i%9\$N4{B8\$NItJ,\$O!“e(BUNIXe\$B\$,Ds<(\$7\$F\$\$\$?%b%G%k!”\$D\$^\$je(B
UTCe\$B\$+COJ};~\$N\$\$\$:\$l\$+\$H\$\$\$&%b%G%k\$K6/\$/1F6A\$r<u\$1\$F\$\$\$^\$9\$7!“e(B
e\$B;H\$&?Me(B(e\$B\$3\$N>l9g\$O;de(B)e\$B\$b\$=\$N%b%G%k\$K\$J\$l\$-\$C\$F\$\$\$^\$9!#4{B8\$Ne(B
getlocale\$B\$OCOJ};~\$rJV\$9\$o\$1\$G\$9\$,!”\$=\$3\$G0z?t\$rEO\$7\$?\$i!“e(BUTCe\$B\$Ge(B
e\$B\$be(B(e\$B8E\$\$0UL#\$G\$Ne(B)e\$BCOJ};[email protected]\$&\$N\$O!”\$A\$g\$C\$He(B
e\$BDq93\$,\$"\$j\$^\$9!#!VM=A[\$5\$l\$k5sF0\$&\$s\$L\$s!W\$O4*0c\$\$\$K\$h\$k\$b\$Ne(B
[email protected]\$C\$?\$N\$GK:\$l\$F\$/[email protected]\$5\$\$!#e(B

e\$BMW\$9\$k\$K!"?7\$?\$KF3F~\$5\$l\$?\$b\$N\$,e(BUTCe\$B\$G\$bCOJ};~\$G\$b\$J\$\$!V%%U%;%Ce(B
e\$B%H\$,;XDj\$5\$l\$?;~9o!W\$H\$H\$i\$(\$k\$+!"COJ};~\$K!V%
%U%;%C%H!W\$H\$\$e(B
e\$B\$&[email protected]\$,A}\$(\$?\$H\$H\$i\$(\$k\$+!"\$H\$\$\$&[email protected]\$J\$s\$G\$7\$g\$&\$+!#EDe(B
e\$BCf\$5\$s\$O8e<T\$NN)>l\$N\$h\$&\$G\$9\$,!“e(Bstrftimee\$B\$r;O\$a\$H\$7\$F0q\$NF;\$Ne(B
e\$B\$h\$&\$J5\$\$,\$7\$^\$9!#e(Btimee\$B4X78\$N4X?t\$r\$9\$Y\$F:F<BAu\$7\$J\$1\$l\$P\$J\$ie(B
e\$B\$J\$\$\$h\$&\$J5\$\$,\$9\$k\$N\$G\$9\$,!”\$"\$kDxEY\$O\$b\$&:F<[email protected](B
e\$B\$7<B\$O<j4V\$O\$=\$l\$[\$I\$G\$b\$J\$\$\$N\$+\$J!#e(B

``                            e\$B\$^\$D\$b\$He(B e\$B\$f\$-\$R\$me(B /:|)``

In article [email protected],
Yukihiro M. [email protected] writes:

|Time.new(year, mon, day, hour, min, sec, utc_offset)
|e\$B\$J\$i\$I\$&\$G\$7\$g\$&\$+e(B?

e\$B\$3\$C\$A\$K\$O;[email protected]\$G\$9!#e(B

e\$B\$G\$O\$=\$l\$G!#e(B

Timee\$B%/%i%9\$N4{B8\$NItJ,\$O!“e(BUNIXe\$B\$,Ds<(\$7\$F\$\$\$?%b%G%k!”\$D\$^\$je(B
UTCe\$B\$+COJ};~\$N\$\$\$:\$l\$+\$H\$\$\$&%b%G%k\$K6/\$/1F6A\$r<u\$1\$F\$\$\$^\$9\$7!"e(B
e\$B;H\$&?Me(B(e\$B\$3\$N>l9g\$O;de(B)e\$B\$b\$=\$N%b%G%k\$K\$J\$l\$-\$C\$F\$\$\$^\$9!#4{B8\$Ne(B

e\$B\$=\$&\$\$\$&%b%G%k\$re(B UNIX
e\$B\$,Ds<(\$7\$F\$\$\$k\$H\$\$\$&\$N\$OK\Ev\$G\$7\$g\$&\$+e(B?

UNIX e\$B\$,Ds6!\$7\$F\$\$\$k%G!<%?9=B\$\$Oe(B time_t e\$B\$He(B struct tm
e\$B\$G\$9\$,!“e(B
e\$B\$3\$l\$Oe(B UTC e\$B\$+COJ};~\$N\$\$\$:\$l\$+\$H\$\$\$&%b%G%k\$G\$O\$”\$j\$^\$;\$s!#\$Ie(B
e\$B\$A\$i\$K\$be(B UTC e\$B\$+COJ};~\$+\$H\$\$\$&>pJs\$O4^\$^\$l\$F\$\$\$^\$;\$s!#e(B

time_t e\$B\$+\$ie(B struct tm e\$B\$XJQ49\$9\$k4X?t\$H\$7\$Fe(B gmtime e\$B\$He(B
localtime e\$B\$,MQ0U\$5\$l\$F\$\$\$k\$H\$\$\$&[email protected]\$Oe(B UTC
e\$B\$HCOJ};~\$N\$I\$A\$i\$+!“e(B
e\$B\$H\$\$\$&46\$O\$”\$j\$^\$9\$,!"\$Y\$D\$KFCDj\$N%%U%;%C%H\$rMQ\$\$\$FJQ49\$9\$ke(B
e\$B4X?t\$rMQ0U\$9\$k\$3\$H\$,Fq\$7\$\$\$o\$1\$G\$O\$"\$j\$^\$;\$s!#<B:]!“e(Bgmtime
e\$B\$r;H\$C\$Fe(B struct tm [email protected]\$?8e!”%
%U%;%C%H\$rB-\$;\$P\$=\$l\$G<B8=\$Ge(B
e\$B\$-\$^\$9!#e(B

e\$B\$^\$?!"e(Bstruct tm e\$B\$Ke(B tm_gmtoff
e\$B\$,Dj5A\$5\$l\$k\$h\$&\$K\$J\$C\$?\$N\$O!“e(B
e\$BG\$0U\$N;~:9\$r07\$&I[@P\$H\$b9M\$(\$i\$l\$^\$9!#\$^\$!!”\$3\$l\$OI8=`\$G\$O\$"e(B
e\$B\$j\$^\$;\$s\$,!#e(B

getlocale\$B\$OCOJ};~\$rJV\$9\$o\$1\$G\$9\$,!"\$=\$3\$G0z?t\$rEO\$7\$?\$i!“e(BUTCe\$B\$Ge(B
e\$B\$be(B(e\$B8E\$\$0UL#\$G\$Ne(B)e\$BCOJ};[email protected]\$&\$N\$O!”\$A\$g\$C\$He(B
e\$BDq93\$,\$"\$j\$^\$9!#!VM=A[\$5\$l\$k5sF0\$&\$s\$L\$s!W\$O4*0c\$\$\$K\$h\$k\$b\$Ne(B
[email protected]\$C\$?\$N\$GK:\$l\$F\$/[email protected]\$5\$\$!#e(B

e\$BMW\$9\$k\$K!"?7\$?\$KF3F~\$5\$l\$?\$b\$N\$,e(BUTCe\$B\$G\$bCOJ};~\$G\$b\$J\$\$!V%%U%;%Ce(B
e\$B%H\$,;XDj\$5\$l\$?;~9o!W\$H\$H\$i\$(\$k\$+!"COJ};~\$K!V%
%U%;%C%H!W\$H\$\$e(B
e\$B\$&[email protected]\$,A}\$(\$?\$H\$H\$i\$(\$k\$+!"\$H\$\$\$&[email protected]\$J\$s\$G\$7\$g\$&\$+!#EDe(B

UTC e\$B\$+\$i\$N%*%U%;%C%H\$H\$\$\$&\$N\$OCOJ};~\$N0l<o\$G\$"\$k!"\$H\$\$\$&\$N\$,e(B
e\$B;d\$N9M\$(\$G\$9!#\$?\$H\$(\$P!"F|K\I8=`;~\$O!"e(B+09:00 e\$B\$H\$\$\$&0lDj\$N%e(B
e\$B%U%;%C%H\$r\$b\$DCOJ};~\$H9M\$(\$i\$l\$^\$9!#\$^\$?!"?M9)E
\$JDj5A\$,e(B
/usr/share/zoneinfo/Etc e\$B\$Ke(B GMT[±]HH e\$B\$H\$\$\$&7A\$GMQ0U\$5\$l\$F\$\$e(B
e\$B\$?\$j\$b\$7\$^\$9!#e(B

% ls /usr/share/zoneinfo/Etc
GMT GMT+11 GMT+4 GMT+8 GMT-10 GMT-14 GMT-5 GMT-9 UTC
GMT+0 GMT+12 GMT+5 GMT+9 GMT-11 GMT-2 GMT-6 GMT0
Universal
GMT+1 GMT+2 GMT+6 GMT-0 GMT-12 GMT-3 GMT-7 Greenwich Zulu
GMT+10 GMT+3 GMT+7 GMT-1 GMT-13 GMT-4 GMT-8 UCT

e\$B\$G\$9\$+\$i!"e(Bgetlocal e\$B\$N0z?t\$Oe(B
(e\$B8GDj%%U%;%C%H\$N\$b\$N\$K8BDj\$5\$le(B
e\$B\$^\$9\$,e(B) e\$B\$I\$NCOJ};~\$r;HMQ\$9\$k\$+\$H\$\$\$&;XDj\$G\$"\$j!";XDj\$5\$l\$F\$\$e(B
e\$B\$J\$+\$C\$?\$ie(B OS e\$B\$,Ds6!\$7\$F\$\$\$kCOJ};~\$r;H\$&!"\$H\$\$\$&\$N\$,;d\$N8+J}e(B
e\$B\$G\$9!#e(Bgetlocal e\$B\$,COJ};[email protected]_Dj\$7\$?%
%V%8%’%/%[email protected]\$He(B
e\$B\$\$\$&35G0\$OJQ\$o\$j\$^\$;\$s!#e(B

e\$BCf\$5\$s\$O8e<T\$NN)>l\$N\$h\$&\$G\$9\$,!“e(Bstrftimee\$B\$r;O\$a\$H\$7\$F0q\$NF;\$Ne(B
e\$B\$h\$&\$J5\$\$,\$7\$^\$9!#e(Btimee\$B4X78\$N4X?t\$r\$9\$Y\$F:F<BAu\$7\$J\$1\$l\$P\$J\$ie(B
e\$B\$J\$\$\$h\$&\$J5\$\$,\$9\$k\$N\$G\$9\$,!”\$"\$kDxEY\$O\$b\$&:F<[email protected](B
e\$B\$7<B\$O<j4V\$O\$=\$l\$[\$I\$G\$b\$J\$\$\$N\$+\$J!#e(B

e\$BKAF,\$K=R\$Y\$?DL\$j!"%G!<%?9=B\$\$Oe(B UTC e\$B\$+COJ};~\$N\$\$\$:\$l\$+\$H\$\$\$&e(B
e\$B%b%G%k\$G\$O\$"\$j\$^\$;\$s\$N\$G%W%m%0%i%`\$N9=B\$<+BN\$rJQ\$(\$k\$H\$\$\$&OCe(B
e\$B\$K\$O\$J\$j\$^\$;\$s!#e(B

[email protected]!“e(Bstruct tm e\$B\$Oe(B tm_gmtoff
e\$B\$r;}\$C\$F\$\$\$J\$\$\$3\$H\$b\$”\$k\$N\$G;He(B
e\$B\$(\$^\$;\$s\$,!“I=8=HO0O\$r9-\$2\$?;[email protected]\$Ge(B (tm_year e\$B\$,e(B overflow
e\$B\$7\$Je(B
e\$B\$\$\$h\$&\$Ke(B) e\$BFH<+\$N9=B\$BN\$r;H\$&\$h\$&\$KJQ\$(\$F\$”\$k\$N\$G!"\$9\$G\$K\$=\$Ne(B
e\$B\$X\$s\$N:n6H\$O:Q\$s\$G\$\$\$^\$9!#\$?\$H\$(\$P!"e(Brb_strftime e\$B\$Oe(B struct tm
e\$B\$G\$O\$J\$/!"FH<+\$N9=B\$BN\$r<u\$1<h\$k4X?t\$K\$J\$C\$F\$\$\$^\$9!#e(B

e\$B\$=\$N\$"\$?\$j\$r0q\$NF;\$H8F\$V\$J\$i!"\$=\$l\$O\$b\$&DL\$jH4\$1\$F\$7\$^\$C\$F\$\$e(B
e\$B\$k\$H\$\$\$C\$F\$\$\$\$\$G\$7\$g\$&!#e(B

e\$B\$^\$D\$b\$He(B e\$B\$f\$-\$R\$m\$G\$9e(B

e\$BCY\$/\$J\$j\$^\$7\$?!#e(B

In message “Re: [ruby-dev:38366] Re: Time with arbitrary offset”
on Sat, 2 May 2009 18:20:07 +0900, Tanaka A. [email protected]
writes:

|> e\$B\$=\$N\$"\$?\$j\$r0q\$NF;\$H8F\$V\$J\$i!"\$=\$l\$O\$b\$&DL\$jH4\$1\$F\$7\$^\$C\$F\$\$e(B
|> e\$B\$k\$H\$\$\$C\$F\$\$\$\$\$G\$7\$g\$&!#e(B
|
|strftime e\$B\$b\$H\$/\$KLdBj\$J\$/F0\$-\$^\$9!#e(B

e\$B\$=\$&\$G\$9\$+!#\$=\$&\$9\$k\$HDq93\$9\$kM}M3\$O\$b\$&\$"\$^\$j\$J\$\$\$h\$&\$G\$9\$M!#e(B
Timee\$B%/%i%9\$Ne(BAPIe\$B\$r2~\$a\$F0lDL\$jD/\$a\$^\$7\$?\$,!"e(BUTCe\$B\$H!V8=:[email protected]_Dj\$5e(B
e\$B\$l\$F\$\$\$k%m!<%+%k%?%\$%`[email protected]\$1\$K8BDj\$9\$Y\$-=EMW\$JM}M3\$Oe(B
e\$B8+Ev\$?\$j\$^\$;\$s\$G\$7\$?\$7!#e(B

[email protected]!“e(BTimee\$B%/%i%9\$K\$Oe(Blocaltimee\$B%a%=%C%I\$,\$”\$j!“e(Btimee\$B%%V%8%’%/e(B
e\$B%H\$re(B(UTCe\$B\$G\$O\$J\$\$e(B)e\$B%m!<%+%k%?%\$%`\$KJQ49\$7\$^\$9!#e(Bgetlocale\$B\$,;~:9e(B
e\$B\$r<(\$9%
%W%7%g%J%k0z?t\$r\$H\$k\$N\$G\$”\$l\$P!"\$3\$A\$i\$bF1MM\$N0z?t\$re(B
[email protected]\$H;W\$\$\$^\$9!#e(B

e\$B\$H\$\$\$&>r7o\$GJQ99\$K;[email protected]\$7\$^\$9!#e(B

e\$B\$D\$\$\$G\$K\$\$\$&\$H!"\$3\$Ne(Blocaltimee\$B%a%=%C%I\$O85\$N%%V%8%’%/%H\$N>uBVe(B
e\$B\$rJQ\$(\$k\$N\$G\$O\$J\$/!"%m!<%+%k%?%\$%`[email protected]_Dj\$7\$??7\$7\$\$e(Btimee\$B%
%V%8%'e(B
e\$B%/%H\$rJV\$9\$Y\$-\$J\$N\$G\$9\$,e(B(gmtimee\$B%a%=%C%I\$bF1MMe(B)e\$B!"[email protected]\$K\$D\$\$e(B
e\$B\$F\$O:#\$5\$iJQ99\$G\$-\$J\$\$\$h\$&\$J5\$\$,\$7\$^\$9!#e(B

``                            e\$B\$^\$D\$b\$He(B e\$B\$f\$-\$R\$me(B /:|)``

In article [email protected],
Tanaka A. [email protected] writes:

|Time.new(year, mon, day, hour, min, sec, utc_offset)
|e\$B\$J\$i\$I\$&\$G\$7\$g\$&\$+e(B?

e\$B\$3\$C\$A\$K\$O;[email protected]\$G\$9!#e(B

e\$B\$G\$O\$=\$l\$G!#e(B

e\$B\$=\$N\$h\$&\$K\$7\$?%Q%C%A\$r:n\$C\$?\$N\$GIU\$1\$F\$*\$-\$^\$9!#e(B

e\$BCf\$5\$s\$O8e<T\$NN)>l\$N\$h\$&\$G\$9\$,!“e(Bstrftimee\$B\$r;O\$a\$H\$7\$F0q\$NF;\$Ne(B
e\$B\$h\$&\$J5\$\$,\$7\$^\$9!#e(Btimee\$B4X78\$N4X?t\$r\$9\$Y\$F:F<BAu\$7\$J\$1\$l\$P\$J\$ie(B
e\$B\$J\$\$\$h\$&\$J5\$\$,\$9\$k\$N\$G\$9\$,!”\$"\$kDxEY\$O\$b\$&:F<[email protected](B
e\$B\$7<B\$O<j4V\$O\$=\$l\$[\$I\$G\$b\$J\$\$\$N\$+\$J!#e(B

e\$B\$=\$N\$"\$?\$j\$r0q\$NF;\$H8F\$V\$J\$i!"\$=\$l\$O\$b\$&DL\$jH4\$1\$F\$7\$^\$C\$F\$\$e(B
e\$B\$k\$H\$\$\$C\$F\$\$\$\$\$G\$7\$g\$&!#e(B

strftime e\$B\$b\$H\$/\$KLdBj\$J\$/F0\$-\$^\$9!#e(B

% ./ruby -e ’
t = Time.new(2000,1,1, 0,0,0, “-05:00”)
puts t.strftime("%Y-%m-%d %H:%M:%S %z")

t2 = t.getlocal("-01:00")
puts t2.strftime("%Y-%m-%d %H:%M:%S %z")

2000-01-01 00:00:00 -0500
2000-01-01 04:00:00 -0100

e\$B\$I\$&\$G\$7\$g\$&\$+!#e(B

# % svn diff --diff-cmd diff -x ‘-u -p’ Index: time.c

— time.c (revision 23325)
+++ time.c (working copy)
@@ -48,6 +48,11 @@ typedef unsigned LONG_LONG unsigned_time
VALUE rb_cTime;
static VALUE time_utc_offset _((VALUE));

+static long obj2long(VALUE obj);
+static VALUE obj2vint(VALUE obj);
+static int month_arg(VALUE arg);
+static void validate_vtm(struct vtm *vtm);
+
static VALUE time_gmtime(VALUE);
static VALUE time_localtime(VALUE);

@@ -59,6 +64,7 @@ static const char *find_time_t(struct tm
static struct vtm *localtimev(VALUE timev, struct vtm *result);

static int leap_year_p(long y);
+#define leap_year_v_p(y) leap_year_p(NUM2LONG(mod(v, INT2FIX(400))))

#define NDIV(x,y) (-(-((x)+1)/(y))-1)
#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
@@ -1002,6 +1008,12 @@ struct time_object {
#define TIME_LOCALTIME_P(tobj) ((tobj)->gmt == 0)
#define TIME_SET_LOCALTIME(tobj) ((tobj)->gmt = 0)

+#define TIME_FIXOFF_P(tobj) ((tobj)->gmt == 2)
+#define TIME_SET_FIXOFF(tobj, off) \

• ((tobj)->gmt = 2, \
• ``````(tobj)->vtm.utc_offset = (off), \
``````
• ``````(tobj)->vtm.zone = NULL)
``````

#define TIME_COPY_GMT(tobj1, tobj2) ((tobj1)->gmt = (tobj2)->gmt)

static VALUE time_get_tm(VALUE, struct time_object *);
@@ -1082,25 +1094,8 @@ timev2timespec(VALUE timev)

• initialized to the current system time.
*/

-/*

• call-seq:
• ``````Time.new -> time
``````
• Returns a `Time` object initialized to the current
system
• time. Note: The object created will be created using the
• resolution available on your system clock, and so may include
• fractional seconds.
• ``````a = Time.new      #=> 2007-11-19 07:50:02 -0600
``````
• ``````b = Time.new      #=> 2007-11-19 07:50:02 -0600
``````
• ``````a == b            #=> false
``````
• ``````"%.6f" % a.to_f   #=> "1195480202.282373"
``````
• ``````"%.6f" % b.to_f   #=> "1195480202.283415"
``````
• */

static VALUE
-time_init(VALUE time)
+time_init_0(VALUE time)
{
struct time_object *tobj;
struct timespec ts;
@@ -1128,6 +1123,271 @@ time_init(VALUE time)
return time;
}

+static VALUE
+time_set_utc_offset(VALUE time, VALUE off)
+{

• struct time_object *tobj;
• off = num_exact(off);
• time_modify(time);
• GetTimeval(time, tobj);
• tobj->tm_got = 0;
• TIME_SET_FIXOFF(tobj, off);
• return time;
+}

+static void
+{

• int sign;
• VALUE subsec, v;
• int sec, min, hour;
• int day;
• vtm->utc_offset = sub(vtm->utc_offset, off);
• if (RTEST(lt(off, INT2FIX(0)))) {
• ``````   sign = -1;
``````
• ``````   off = neg(off);
``````
• }
• else {
• ``````   sign = 1;
``````
• }
• divmodv(off, INT2FIX(1), &off, &subsec);
• divmodv(off, INT2FIX(60), &off, &v);
• sec = NUM2INT(v);
• divmodv(off, INT2FIX(60), &off, &v);
• min = NUM2INT(v);
• divmodv(off, INT2FIX(24), &off, &v);
• hour = NUM2INT(v);
• if (sign < 0) {
• ``````   subsec = neg(subsec);
``````
• ``````   sec = -sec;
``````
• ``````   min = -min;
``````
• ``````   hour = -hour;
``````
• }
• day = 0;
• if (!rb_equal(subsec, INT2FIX(0))) {
• ``````   vtm->subsec = add(vtm->subsec, subsec);
``````
• ``````   if (lt(vtm->subsec, INT2FIX(0))) {
``````
• ``````       vtm->subsec = add(vtm->subsec, INT2FIX(1));
``````
• ``````       sec -= 1;
``````
• ``````   }
``````
• ``````   if (le(INT2FIX(1), vtm->subsec)) {
``````
• ``````       vtm->subsec = sub(vtm->subsec, INT2FIX(1));
``````
• ``````       sec += 1;
``````
• ``````   }
``````
• ``````   goto not_zero_sec;
``````
• }
• if (sec) {
• `````` not_zero_sec:
``````
• ``````   /* If sec + subsec == 0, don't change vtm->sec.
``````
• ``````    * It may be 60 which is a leap second. */
``````
• ``````   vtm->sec += sec;
``````
• ``````   if (vtm->sec < 0) {
``````
• ``````       vtm->sec += 60;
``````
• ``````       min -= 1;
``````
• ``````   }
``````
• ``````   if (60 <= vtm->sec) {
``````
• ``````       vtm->sec -= 60;
``````
• ``````       min += 1;
``````
• ``````   }
``````
• }
• if (min) {
• ``````   vtm->min += min;
``````
• ``````   if (vtm->min < 0) {
``````
• ``````       vtm->min += 60;
``````
• ``````       hour -= 1;
``````
• ``````   }
``````
• ``````   if (60 <= vtm->min) {
``````
• ``````       vtm->min -= 60;
``````
• ``````       hour += 1;
``````
• ``````   }
``````
• }
• if (hour) {
• ``````   vtm->hour += hour;
``````
• ``````   if (vtm->hour < 0) {
``````
• ``````       vtm->hour += 24;
``````
• ``````       day = -1;
``````
• ``````   }
``````
• ``````   if (24 <= vtm->hour) {
``````
• ``````       vtm->hour -= 24;
``````
• ``````       day = 1;
``````
• ``````   }
``````
• }
• if (day) {
• ``````   if (day < 0) {
``````
• ``````       if (vtm->mon == 1 && vtm->mday == 1) {
``````
• ``````           vtm->mday = 31;
``````
• ``````           vtm->mon = 12; /* December */
``````
• ``````           vtm->year = sub(vtm->year, INT2FIX(1));
``````
• ``````           vtm->yday = leap_year_v_p(vtm->year) ? 365 : 364;
``````
• ``````       }
``````
• ``````       else if (vtm->mday == 1) {
``````
• ``````           const int *days_in_month = leap_year_v_p(vtm->year) ?
``````
• ``````                                      leap_year_days_in_month :
``````
• ``````                                      common_year_days_in_month;
``````
• ``````           vtm->mon--;
``````
• ``````           vtm->mday = days_in_month[vtm->mon-1];
``````
• ``````           vtm->yday--;
``````
• ``````       }
``````
• ``````       else {
``````
• ``````           vtm->mday--;
``````
• ``````           vtm->yday--;
``````
• ``````       }
``````
• ``````       vtm->wday = (vtm->wday + 6) % 7;
``````
• ``````   }
``````
• ``````   else {
``````
• ``````       int leap = leap_year_v_p(vtm->year);
``````
• ``````       if (vtm->mon == 12 && vtm->mday == 31) {
``````
• ``````           vtm->year = add(vtm->year, INT2FIX(1));
``````
• ``````           vtm->mon = 1; /* January */
``````
• ``````           vtm->mday = 1;
``````
• ``````           vtm->yday = 1;
``````
• ``````       }
``````
• ``````       else if (vtm->mday == (leap ? leap_year_days_in_month :
``````

common_year_days_in_month)[vtm->mon-1]) {

• ``````           vtm->mon++;
``````
• ``````           vtm->mday = 1;
``````
• ``````           vtm->yday++;
``````
• ``````       }
``````
• ``````       else {
``````
• ``````           vtm->mday++;
``````
• ``````           vtm->yday++;
``````
• ``````       }
``````
• ``````       vtm->wday = (vtm->wday + 1) % 7;
``````
• ``````   }
``````
• }
+}

+static VALUE
+utc_offset_arg(VALUE arg)
+{

• VALUE tmp;
• if (!NIL_P(tmp = rb_check_string_type(arg))) {
• ``````   int n;
``````
• ``````   char *s = RSTRING_PTR(tmp);
``````
• ``````   if (!rb_enc_str_asciicompat_p(tmp) ||
``````
• ``````       RSTRING_LEN(tmp) != 6 ||
``````
• ``````       (s[0] != '+' && s[0] != '-') ||
``````
• ``````       !ISDIGIT(s[1]) ||
``````
• ``````       !ISDIGIT(s[2]) ||
``````
• ``````       s[3] != ':' ||
``````
• ``````       !ISDIGIT(s[4]) ||
``````
• ``````       !ISDIGIT(s[5]))
``````
• ``````       rb_raise(rb_eArgError, "\"+HH:MM\" or \"-HH:MM\" expected
``````

for utc_offset");

• ``````   n = strtol(s+1, NULL, 10) * 3600;
``````
• ``````   n += strtol(s+4, NULL, 10) * 60;
``````
• ``````   if (s[0] == '-')
``````
• ``````       n = -n;
``````
• ``````   return INT2FIX(n);
``````
• }
• else {
• ``````   return num_exact(arg);
``````
• }
+}

+static VALUE
+time_init_1(int argc, VALUE *argv, VALUE time)
+{

• struct vtm vtm;
• VALUE v[7];
• int i;
• struct time_object *tobj;
• vtm.wday = -1;
• vtm.yday = 0;
• vtm.zone = “”;
• /* year mon mday hour min sec
off */
• rb_scan_args(argc, argv, “16”,
&v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6]);
• vtm.year = obj2vint(v[0]);
• vtm.mon = NIL_P(v[1]) ? 1 : month_arg(v[1]);
• vtm.mday = NIL_P(v[2]) ? 1 : obj2long(v[2]);
• vtm.hour = NIL_P(v[3]) ? 0 : obj2long(v[3]);
• vtm.min = NIL_P(v[4]) ? 0 : obj2long(v[4]);
• vtm.sec = 0;
• vtm.subsec = INT2FIX(0);
• if (!NIL_P(v[5])) {
• ``````   VALUE sec = num_exact(v[5]);
``````
• ``````   VALUE subsec;
``````
• ``````   divmodv(sec, INT2FIX(1), &sec, &subsec);
``````
• ``````   vtm.sec = NUM2INT(sec);
``````
• ``````   vtm.subsec = subsec;
``````
• }
• vtm.isdst = -1;
• vtm.utc_offset = Qnil;
• if (!NIL_P(v[6])) {
• ``````   VALUE arg = v[6];
``````
• ``````   VALUE tmp;
``````
• ``````   if (arg == ID2SYM(rb_intern("dst")))
``````
• ``````       vtm.isdst = 1;
``````
• ``````   else if (arg == ID2SYM(rb_intern("std")))
``````
• ``````       vtm.isdst = 0;
``````
• ``````   else
``````
• ``````       vtm.utc_offset = utc_offset_arg(arg);
``````
• }
• validate_vtm(&vtm);
• time_modify(time);
• GetTimeval(time, tobj);
• tobj->tm_got=0;
• tobj->timev = INT2FIX(0);
• if (!NIL_P(vtm.utc_offset)) {
• ``````   VALUE off = vtm.utc_offset;
``````
• ``````   vtm_add_offset(&vtm, neg(off));
``````
• ``````   vtm.utc_offset = Qnil;
``````
• ``````   tobj->timev = timegmv(&vtm);
``````
• ``````   return time_set_utc_offset(time, off);
``````
• }
• else {
• ``````   tobj->timev = timelocalv(&vtm);
``````
• ``````   return time_localtime(time);
``````
• }
+}

+/*

• call-seq:
• ``````Time.new -> time
``````
• Returns a `Time` object initialized to the current
system
• time. Note: The object created will be created using the
• resolution available on your system clock, and so may include
• fractional seconds.
• ``````a = Time.new      #=> 2007-11-19 07:50:02 -0600
``````
• ``````b = Time.new      #=> 2007-11-19 07:50:02 -0600
``````
• ``````a == b            #=> false
``````
• ``````"%.6f" % a.to_f   #=> "1195480202.282373"
``````
• ``````"%.6f" % b.to_f   #=> "1195480202.283415"
``````
• */

+static VALUE
+time_init(int argc, VALUE *argv, VALUE time)
+{

• if (argc == 0)
• ``````   return time_init_0(time);
``````
• else
• ``````   return time_init_1(argc, argv, time);
``````

+}
+
static void
time_overflow_p(time_t *secp, long *nsecp)
{
@@ -1452,6 +1712,13 @@ month_arg(VALUE arg)
}

static void
+validate_utc_offset(VALUE utc_offset)
+{

• if (le(utc_offset, INT2FIX(-86400)) || ge(utc_offset,
INT2FIX(86400)))
• rb_raise(rb_eArgError, “utc_offset out of range”);
+}

+static void
validate_vtm(struct vtm *vtm)
{
if ( vtm->mon < 1 || vtm->mon > 12
@@ -1460,7 +1727,8 @@ validate_vtm(struct vtm *vtm)
|| (vtm->hour == 24 && (vtm->min > 0 || vtm->sec > 0))
|| vtm->min < 0 || vtm->min > 59
|| vtm->sec < 0 || vtm->sec > 60

• || lt(vtm->subsec, INT2FIX(0)) || ge(vtm->subsec, INT2FIX(1)))
• ``````   || lt(vtm->subsec, INT2FIX(0)) || ge(vtm->subsec, INT2FIX(1))
``````
• ``````   || (!NIL_P(vtm->utc_offset) &&
``````

(validate_utc_offset(vtm->utc_offset), 0)))
rb_raise(rb_eArgError, “argument out of range”);
}

@@ -2280,6 +2548,38 @@ time_gmtime(VALUE time)
return time;
}

+static VALUE
+time_fixoff(VALUE time)
+{

• struct time_object *tobj;
• struct vtm vtm;
• VALUE off;
• GetTimeval(time, tobj);
• if (TIME_FIXOFF_P(tobj)) {
• ``````  if (tobj->tm_got)
``````
• ``````      return time;
``````
• }
• else {
• ``````  time_modify(time);
``````
• }
• if (TIME_FIXOFF_P(tobj))
• ``````   off = tobj->vtm.utc_offset;
``````
• else
• ``````   off = INT2FIX(0);
``````
• if (!gmtimev(tobj->timev, &vtm))
• ``````  rb_raise(rb_eArgError, "gmtime error");
``````
• tobj->vtm = vtm;
• tobj->tm_got = 1;
• TIME_SET_FIXOFF(tobj, off);
• return time;
+}

/*

• call-seq:
• ``````time.getlocal => new_time
``````

@@ -2295,8 +2595,20 @@ time_gmtime(VALUE time)
*/

static VALUE
-time_getlocaltime(VALUE time)
+time_getlocaltime(int argc, VALUE *argv, VALUE time)
{

• VALUE off;
• rb_scan_args(argc, argv, “01”, &off);
• if (!NIL_P(off)) {
• ``````   off = utc_offset_arg(off);
``````
• ``````   validate_utc_offset(off);
``````
• ``````   time = time_dup(time);
``````
• ``````   time_set_utc_offset(time, off);
``````
• ``````   return time_fixoff(time);
``````
• }
• return time_localtime(time_dup(time));
}

@@ -2325,6 +2637,7 @@ static VALUE
time_get_tm(VALUE time, struct time_object *tobj)
{
if (TIME_UTC_P(tobj)) return time_gmtime(time);

• if (TIME_FIXOFF_P(tobj)) return time_fixoff(time);
return time_localtime(time);
}

@@ -2828,6 +3141,8 @@ time_zone(VALUE time)
if (TIME_UTC_P(tobj)) {
return rb_str_new2(“UTC”);
}

• if (tobj->vtm.zone == NULL)
• ``````   return Qnil;
``````
return rb_str_new2(tobj->vtm.zone);
}

@@ -3321,13 +3636,13 @@ Init_Time(void)
rb_define_method(rb_cTime, “<=>”, time_cmp, 1);
rb_define_method(rb_cTime, “eql?”, time_eql, 1);
rb_define_method(rb_cTime, “hash”, time_hash, 0);

• rb_define_method(rb_cTime, “initialize”, time_init, 0);
• rb_define_method(rb_cTime, “initialize”, time_init, -1);
rb_define_method(rb_cTime, “initialize_copy”, time_init_copy, 1);

rb_define_method(rb_cTime, “localtime”, time_localtime, 0);
rb_define_method(rb_cTime, “gmtime”, time_gmtime, 0);
rb_define_method(rb_cTime, “utc”, time_gmtime, 0);

• rb_define_method(rb_cTime, “getlocal”, time_getlocaltime, 0);
• rb_define_method(rb_cTime, “getlocal”, time_getlocaltime, -1);
rb_define_method(rb_cTime, “getgm”, time_getgmtime, 0);
rb_define_method(rb_cTime, “getutc”, time_getgmtime, 0);

# Index: strftime.c

— strftime.c (revision 23325)
+++ strftime.c (working copy)
@@ -610,7 +610,10 @@ rb_strftime(char s, size_t maxsize, con
#endif /
HAVE_TM_ZONE /
#endif /
HAVE_TZNAME /
#endif /
0 */

• `````` tp = vtm->zone;
``````
• ``````                   if (vtm->zone == NULL)
``````
• ``````                       tp = "";
``````
• ``````                   else
``````
• ``````                       tp = vtm->zone;
i = strlen(tp);
break;``````

e\$B\$^\$D\$b\$He(B e\$B\$f\$-\$R\$m\$G\$9e(B

In message “Re: [ruby-dev:38437] Re: Time with arbitrary offset”
on Tue, 12 May 2009 16:09:30 +0900, Tanaka A. [email protected]
writes:

|e\$B;d\$be(B localtime e\$B\$,GK2uE*[email protected]\$H;W\$C\$F\$\$\$^\$9!#\$=\$7\$F!“JQe(B
|e\$B\$J\$b\$N\$O3HD%\$7\$FMQES\$r9-\$2\$?\$j\$7\$J\$\$!”\$H\$\$\$&9M\$(\$F\$\$\$k\$N\$G!"e(B
|localtime e\$B\$r\$\$\$8\$i\$J\$+\$C\$?\$N\$O<B\$O\$=\$l\$,M}M3\$G\$9!#e(B

e\$B\$J\$k\$[\$I!#\$=\$&\$\$\$&\$3\$H\$G\$7\$?\$+!#e(B

|e\$B\$G\$b!“JQ99\$G\$-\$J\$\$\$H\$\$\$&\$3\$H\$G\$”\$l\$P!"3HD%\$7\$J\$\$\$3\$H\$K\$?\$\$\$7e(B
|e\$B\$?0UL#\$O\$J\$\$\$G\$9\$M!#e(B

e\$B\$G\$9!#\$`\$7\$m;d<+?H\$,4Y\$C\$F\$7\$^\$C\$?8m\$C\$?%\$%a!<%8e(B(e\$B%m!<%+%k%?%\$e(B e\$B%`\$Oe([email protected]_Dj\$5\$l\$?%?%\$%`%>!<%s\$K8BDj\$5\$l\$ke(B)e\$B\$rA}I}\$5\$;\$k\$N\$G!"e(B
e\$BD>\$7\$?J}\$,\$h\$\$\$H;W\$\$\$^\$9!#e(B

In article [email protected],
Yukihiro M. [email protected] writes:

[email protected]!“e(BTimee\$B%/%i%9\$K\$Oe(Blocaltimee\$B%a%=%C%I\$,\$”\$j!“e(Btimee\$B%%V%8%’%/e(B
e\$B%H\$re(B(UTCe\$B\$G\$O\$J\$\$e(B)e\$B%m!<%+%k%?%\$%`\$KJQ49\$7\$^\$9!#e(Bgetlocale\$B\$,;~:9e(B
e\$B\$r<(\$9%
%W%7%g%J%k0z?t\$r\$H\$k\$N\$G\$”\$l\$P!"\$3\$A\$i\$bF1MM\$N0z?t\$re(B
[email protected]\$H;W\$\$\$^\$9!#e(B

[email protected]\$+\$i\$O\$=\$N\$H\$*\$j\$G\$9\$M!#e(B

e\$B\$^\$D\$b\$H\$5\$s\$,[email protected]\$r\$H\$k\$H\$\$\$&\$N\$G\$"\$l\$P\$H\$/\$KH?BP\$9\$k\$3\$He(B
e\$B\$b\$J\$\$\$N\$G\$=\$&\$7\$F\$*\$-\$^\$9!#e(B

e\$B\$D\$\$\$G\$K\$\$\$&\$H!"\$3\$Ne(Blocaltimee\$B%a%=%C%I\$O85\$N%%V%8%’%/%H\$N>uBVe(B
e\$B\$rJQ\$(\$k\$N\$G\$O\$J\$/!"%m!<%+%k%?%\$%`[email protected]_Dj\$7\$??7\$7\$\$e(Btimee\$B%
%V%8%'e(B
e\$B%/%H\$rJV\$9\$Y\$-\$J\$N\$G\$9\$,e(B(gmtimee\$B%a%=%C%I\$bF1MMe(B)e\$B!"[email protected]\$K\$D\$\$e(B
e\$B\$F\$O:#\$5\$iJQ99\$G\$-\$J\$\$\$h\$&\$J5\$\$,\$7\$^\$9!#e(B

e\$B;d\$be(B localtime e\$B\$,GK2uE*[email protected]\$H;W\$C\$F\$\$\$^\$9!#\$=\$7\$F!“JQe(B
e\$B\$J\$b\$N\$O3HD%\$7\$FMQES\$r9-\$2\$?\$j\$7\$J\$\$!”\$H\$\$\$&9M\$(\$F\$\$\$k\$N\$G!"e(B
localtime e\$B\$r\$\$\$8\$i\$J\$+\$C\$?\$N\$O<B\$O\$=\$l\$,M}M3\$G\$9!#e(B

e\$B\$G\$b!“JQ99\$G\$-\$J\$\$\$H\$\$\$&\$3\$H\$G\$”\$l\$P!"3HD%\$7\$J\$\$\$3\$H\$K\$?\$\$\$7e(B
e\$B\$?0UL#\$O\$J\$\$\$G\$9\$M!#e(B

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.