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;~:9$r@_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%’%/%H$r@8@.$9$k%a%=%C%I$H!"e(B
  • e$B;~:9$rJQ$($?e(B Time e$B%*%V%8%’%/%H$r@8@.$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
e$B$H$@$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
+vtm_add_offset(struct vtm *vtm, VALUE off)
+{

  • 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;
  • vtm_add_offset(&tobj->vtm, off);
  • 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;?@.$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

e$B$?$@$7!“e(Bcivile$B$H$$$&%a%=%C%IL>$K$OH?BP$G$9!#e(BDatee$B%/%i%9$K$O!”$^e(B
e$B$:e(BAD/BCe$B$r4*0F$9$ke(BCivil
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
e$B$7$J$$$b$N$@$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$,!”$=$l$OBP1~$9$l$P$h$$$@$1$N$3$H$G$9$M!#e(B

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

e$B$?$@$7!“e(Bcivile$B$H$$$&%a%=%C%IL>$K$OH?BP$G$9!#e(BDatee$B%/%i%9$K$O!”$^e(B
e$B$:e(BAD/BCe$B$r4*0F$9$ke(BCivil 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
e$B$7$J$$$b$N$@$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!"Hs8_49@-$r5/$3$5$:$K3HD%$9$k$3$H$,$G$-$^$9!#e(B

e$B$"$H!“e(BDate e$B%/%i%9$,e(B AD/BC
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$”$k$H$3$m$N@>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

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%$@$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$,!”$=$l$OBP1~$9$l$P$h$$$@$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!"Hs8_49@-$r5/$3$5$:$K3HD%$9$k$3$H$,$G$-$^$9!#e(B

e$B$3$C$A$K$O;?@.$G$9!#e(B

|e$B$"$H!“e(BDate e$B%/%i%9$,e(B AD/BC 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$”$k$H$3$m$N@>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%$@$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};~$G$b$J$$$b$N$rJV$9$H8@$&$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
e$B$@$C$?$N$GK:$l$F$/$@$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$&?7$7$$B0@-$,A}$($?$H$H$i$($k$+!”$H$$$&E@$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<BAu$7$F$k$o$1$@e(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;?@.$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$$$&E@$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 e$B$rF@$?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};~$G$b$J$$$b$N$rJV$9$H8@$&$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
e$B$@$C$?$N$GK:$l$F$/$@$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$&?7$7$$B0@-$,A}$($?$H$H$i$($k$+!”$H$$$&E@$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};~$K@_Dj$7$?%
%V%8%'%/%H$rJV$9$b$N$@$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<BAu$7$F$k$o$1$@e(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

e$B$?$@!“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$?;~E@$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=:_@_Dj$5e(B
e$B$l$F$$$k%m!<%+%k%?%$%`!W$N$U$?$D$@$1$K8BDj$9$Y$-=EMW$JM}M3$Oe(B
e$B8+Ev$?$j$^$;$s$G$7$?$7!#e(B

e$B$?$@!“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
e$B$H$k$Y$-$@$H;W$$$^$9!#e(B

e$B$H$$$&>r7o$GJQ99$K;?@.$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%?%$%`$K@_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!”$3$NE@$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;?@.$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<BAu$7$F$k$o$1$@e(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
+vtm_add_offset(struct vtm *vtm, VALUE off)
+{

  • 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;
  • vtm_add_offset(&tobj->vtm, off);
  • 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*$J$N$OJQ$@$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(BTZe$B$G@_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:

e$B$?$@!“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
e$B$H$k$Y$-$@$H;W$$$^$9!#e(B

e$B0l4S@-$+$i$O$=$N$H$*$j$G$9$M!#e(B

e$B$^$D$b$H$5$s$,0l4S@-$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%?%$%`$K@_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!”$3$NE@$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*$J$N$OJQ$@$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