[#38323] [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — oshida@...

押田です。

22 messages 2009/04/24
[#38331] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — Hidetoshi NAGAI <nagai@...> 2009/04/26

永井@知能.九工大です.

[#38339] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — oshida@... 2009/04/27

押田です。

[#38340] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — Hidetoshi NAGAI <nagai@...> 2009/04/27

永井@知能.九工大です.

[#38697] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — Hidetoshi NAGAI <nagai@...> 2009/06/21

永井@知能.九工大です.

[#38711] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — oshida@... 2009/06/24

押田です。

[#38723] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — Hidetoshi NAGAI <nagai@...> 2009/07/01

永井@知能.九工大です.

[#38743] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — oshida@... 2009/07/07

押田です。

[#38747] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — Hidetoshi NAGAI <nagai@...> 2009/07/08

永井@知能.九工大です.

[#38748] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — oshida@... 2009/07/08

押田です。

[#38749] Re: [1.8.7][1.9.1][tk] 自前実装の拡張 widget を使いたい場合 — Hidetoshi NAGAI <nagai@...> 2009/07/08

永井@知能.九工大です.

[ruby-dev:38326] Time with arbitrary offset

From: Tanaka Akira <akr@...>
Date: 2009-04-25 13:10:05 UTC
List: ruby-dev #38326
Time で、オブジェクト毎に任意の時差を指定できるようにするの
はどうでしょうか。

たとえば、ひとつの用法として、[ruby-core:23036] でも述べまし
たが、web application で時刻をユーザに表示する際、ユーザの指
定したタイムゾーンがあれば、Time オブジェクトにその時差を設
定して表示に使う、というものが考えられます。

また、文字列で受け取った時刻を、その時差を保存したまま Time
オブジェクトにすることもできるようになります。

とりあえず API としては
* 日付と時差から Time オブジェクトを生成するメソッドと、
* 時差を変えた Time オブジェクトを生成するメソッド
が必要ではないかと思います。

というわけで、それぞれに対応する
* Time.civil(year, mon, day, hour, min, sec, utc_offset) -> time
* Time#getlocal(utc_offset) -> time
というのを追加するのはどうでしょうか。
(year 以外の引数は省略可能です)

civil というのは、DateTime.civil からとりました。

Time.local あたりを拡張することも考えたんですが、ParseDate
対応 [ruby-core:761] との兼ね合いでうまいやりかたを思いつき
ませんでした。仮にそちらでうまいやりかたを思いついて作ること
になったとしても、DateTime と多態なメソッドがあるのは良いこ
とだと思います。
(なお、DateTime.civil はもうひとつ start という引数があり
ますが、Time では改暦は扱わないので、Time.civil にはありませ
ん。)

% 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);
 
-- 
[田中 哲][たなか あきら][Tanaka Akira]

In This Thread

Prev Next