# Time with arbitrary offset

**URL:** <https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457>\
**Category:** Development\
**Created:** [April 25, 2009, 3:11pm UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457 "2009-04-25T15:11:15Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [April 25, 2009, 3:11pm UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/1 "2009-04-25T15:11:15Z")

</div>

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, “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);

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [April 27, 2009, 12:31am UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/2 "2009-04-27T00:31:13Z")

</div>

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. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [April 27, 2009, 5:57am UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/3 "2009-04-27T05:57:33Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Yukihiro M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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$r4_0F$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

> **[Civil calendar](https://en.wikipedia.org/wiki/Civil_calendar)**
>
> The civil calendar is the calendar, or possibly one of several calendars, used within a country for civil, official, or administrative purposes. The civil calendar is almost always used for general purposes by people and private organizations.
> The most widespread civil calendar and de facto international standard is the Gregorian calendar. Although that calendar was first declared by Pope Gregory XIII to be used in Catholic countries in 1582, it has since been adopted, as a matter of convenience...

> 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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [April 27, 2009, 6:43pm UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/4 "2009-04-27T18:43:44Z")

</div>

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. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
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$r4_0F$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 /:|)

```

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [April 28, 2009, 10:31am UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/5 "2009-04-28T10:31:01Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Yukihiro M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [May 12, 2009, 8:44am UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/6 "2009-05-12T08:44:31Z")

</div>

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. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
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 /:|)

```

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [May 2, 2009, 11:20am UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/7 "2009-05-02T11:20:52Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Tanaka A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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, “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;

```

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [May 12, 2009, 9:16am UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/8 "2009-05-12T09:16:58Z")

</div>

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. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [May 12, 2009, 9:10am UTC](https://www.ruby-forum.com/t/time-with-arbitrary-offset/166457/9 "2009-05-12T09:10:30Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Yukihiro M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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
