[前][次][番号順一覧][スレッド一覧]

ruby-changes:19096

From: tadf <ko1@a...>
Date: Sun, 20 Mar 2011 22:08:21 +0900 (JST)
Subject: [ruby-changes:19096] Ruby:r31135 (trunk): * ext/date/date_core.c: replacement of implementation of

tadf	2011-03-20 21:44:47 +0900 (Sun, 20 Mar 2011)

  New Revision: 31135

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=31135

  Log:
    * ext/date/date_core.c: replacement of implementation of
      strftime. It has some limitations that is same as Time's
      one.  [experimental]
    * ext/date/date_strftime.c: new.
    * ext/date/lib/date/format.c: removed ruby version of strftime.

  Added files:
    trunk/ext/date/date_strftime.c
    trunk/ext/date/timev.h
  Modified files:
    trunk/ChangeLog
    trunk/ext/date/date_core.c
    trunk/ext/date/lib/date/format.rb
    trunk/test/date/test_date_strftime.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 31134)
+++ ChangeLog	(revision 31135)
@@ -1,3 +1,11 @@
+Sun Mar 20 21:34:49 2011  Tadayoshi Funaba  <tadf@d...>
+
+	* ext/date/date_core.c: replacement of implementation of
+	  strftime. It has some limitations that is same as Time's
+	  one.  [experimental]
+	* ext/date/date_strftime.c: new.
+	* ext/date/lib/date/format.c: removed ruby version of strftime.
+
 Sun Mar 20 12:43:12 2011  Tanaka Akira  <akr@f...>
 
 	* ext/openssl/ossl_x509store.c: parenthesize macro arguments.
Index: ext/date/date_core.c
===================================================================
--- ext/date/date_core.c	(revision 31134)
+++ ext/date/date_core.c	(revision 31135)
@@ -1464,6 +1464,17 @@
     }
 }
 
+static const int yeartab[2][13] = {
+    { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
+    { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
+};
+
+static int
+civil_to_yday(int y, int m, int d)
+{
+    return yeartab[leap_p(y) ? 1 : 0][m] + d;
+}
+
 /*
  * call-seq:
  *    d.yday
@@ -1481,9 +1492,8 @@
     if (!light_mode_p(dat))
 	return iforward0("yday_r");
     {
-	get_d_jd(dat);
-	jd_to_ordinal(dat->l.jd, dat->l.sg, &ry, &rd);
-	return INT2FIX(rd);
+	get_d_civil(dat);
+	return INT2FIX(civil_to_yday(dat->l.year, dat->l.mon, dat->l.mday));
     }
 }
 
@@ -2322,8 +2332,160 @@
     }
 }
 
+#include <errno.h>
+#include "timev.h"
+
+size_t
+date_strftime(char *s, size_t maxsize, const char *format,
+	      const struct vtm *vtm, VALUE timev, int gmt);
+
+#define SMALLBUF 100
+static size_t
+date_strftime_alloc(char **buf, const char *format,
+		    struct vtm *vtm, VALUE timev)
+{
+    size_t size, len, flen;
+
+    (*buf)[0] = '\0';
+    flen = strlen(format);
+    if (flen == 0) {
+	return 0;
+    }
+    errno = 0;
+    len = date_strftime(*buf, SMALLBUF, format, vtm, timev, 0);
+    if (len != 0 || (**buf == '\0' && errno != ERANGE)) return len;
+    for (size=1024; ; size*=2) {
+	*buf = xmalloc(size);
+	(*buf)[0] = '\0';
+	len = date_strftime(*buf, size, format, vtm, timev, 0);
+	/*
+	 * buflen can be zero EITHER because there's not enough
+	 * room in the string, or because the control command
+	 * goes to the empty string. Make a reasonable guess that
+	 * if the buffer is 1024 times bigger than the length of the
+	 * format string, it's not failing for lack of room.
+	 */
+	if (len > 0 || size >= 1024 * flen) break;
+	xfree(*buf);
+    }
+    return len;
+}
+
+static void
+d_lite_set_vtm_and_timev(VALUE self, struct vtm *vtm, VALUE *timev)
+{
+    get_d1(self);
+
+    if (!light_mode_p(dat)) {
+	vtm->year = iforward0("year_r");
+	vtm->mon = FIX2INT(iforward0("mon_r"));
+	vtm->mday = FIX2INT(iforward0("mday_r"));
+	vtm->hour = FIX2INT(iforward0("hour_r"));
+	vtm->min = FIX2INT(iforward0("min_r"));
+	vtm->sec = FIX2INT(iforward0("sec_r"));
+	vtm->subsecx = iforward0("sec_fraction_r");
+	vtm->utc_offset = INT2FIX(0);
+	vtm->wday = FIX2INT(iforward0("wday_r"));
+	vtm->yday = FIX2INT(iforward0("yday_r"));
+	vtm->isdst = 0;
+	vtm->zone = RSTRING_PTR(iforward0("zone_r"));
+	*timev = f_mul(f_sub(dat->r.ajd,
+			     rb_rational_new2(INT2FIX(4881175), INT2FIX(2))),
+		       INT2FIX(86400));
+    }
+    else {
+	get_d_jd(dat);
+	get_d_civil(dat);
+
+	vtm->year = LONG2NUM(dat->l.year);
+	vtm->mon = dat->l.mon;
+	vtm->mday = dat->l.mday;
+	vtm->hour = 0;
+	vtm->min = 0;
+	vtm->sec = 0;
+	vtm->subsecx = INT2FIX(0);
+	vtm->utc_offset = INT2FIX(0);
+	vtm->wday = jd_to_wday(dat->l.jd);
+	vtm->yday = civil_to_yday(dat->l.year, dat->l.mon, dat->l.mday);
+	vtm->isdst = 0;
+	vtm->zone = "+00:00";
+	*timev = f_mul(INT2FIX(dat->l.jd - 2440588),
+		       INT2FIX(86400));
+    }
+}
+
+static VALUE
+date_strftime_internal(int argc, VALUE *argv, VALUE self,
+		       const char *default_fmt,
+		       void (*func)(VALUE, struct vtm *, VALUE *))
+{
+    get_d1(self);
+    {
+	VALUE vfmt;
+	const char *fmt;
+	long len;
+	char buffer[SMALLBUF], *buf = buffer;
+	struct vtm vtm;
+	VALUE timev;
+	VALUE str;
+
+	rb_scan_args(argc, argv, "01", &vfmt);
+
+	if (argc < 1)
+	    vfmt = rb_usascii_str_new2(default_fmt);
+	else {
+	    StringValue(vfmt);
+	    if (!rb_enc_str_asciicompat_p(vfmt)) {
+		rb_raise(rb_eArgError,
+			 "format should have ASCII compatible encoding");
+	    }
+	}
+	fmt = RSTRING_PTR(vfmt);
+	len = RSTRING_LEN(vfmt);
+	(*func)(self, &vtm, &timev);
+	if (memchr(fmt, '\0', len)) {
+	    /* Ruby string may contain \0's. */
+	    const char *p = fmt, *pe = fmt + len;
+
+	    str = rb_str_new(0, 0);
+	    while (p < pe) {
+		len = date_strftime_alloc(&buf, p, &vtm, timev);
+		rb_str_cat(str, buf, len);
+		p += strlen(p);
+		if (buf != buffer) {
+		    xfree(buf);
+		    buf = buffer;
+		}
+		for (fmt = p; p < pe && !*p; ++p);
+		if (p > fmt) rb_str_cat(str, fmt, p - fmt);
+	    }
+	    return str;
+	}
+	else
+	    len = date_strftime_alloc(&buf, fmt, &vtm, timev);
+
+	str = rb_str_new(buf, len);
+	if (buf != buffer) xfree(buf);
+	rb_enc_copy(str, vfmt);
+	return str;
+    }
+}
+
 /*
  * call-seq:
+ *    d.strftime([format="%F"])
+ *
+ * Return a formatted string.
+ */
+static VALUE
+d_lite_strftime(int argc, VALUE *argv, VALUE self)
+{
+    return date_strftime_internal(argc, argv, self,
+				  "%F", d_lite_set_vtm_and_timev);
+}
+
+/*
+ * call-seq:
  *    d.marshal_dump
  *
  * Dump to Marshal format.
@@ -3074,10 +3236,8 @@
     if (!light_mode_p(dat))
 	return iforward0("yday_r");
     {
-	get_dt_jd(dat);
-	get_dt_df(dat);
-	jd_to_ordinal(local_jd(dat), dat->l.sg, &ry, &rd);
-	return INT2FIX(rd);
+	get_dt_civil(dat);
+	return INT2FIX(civil_to_yday(dat->l.year, dat->l.mon, dat->l.mday));
     }
 }
 
@@ -3806,8 +3966,66 @@
     }
 }
 
+static void
+dt_lite_set_vtm_and_timev(VALUE self, struct vtm *vtm, VALUE *timev)
+{
+    get_dt1(self);
+
+    if (!light_mode_p(dat)) {
+	vtm->year = iforward0("year_r");
+	vtm->mon = FIX2INT(iforward0("mon_r"));
+	vtm->mday = FIX2INT(iforward0("mday_r"));
+	vtm->hour = FIX2INT(iforward0("hour_r"));
+	vtm->min = FIX2INT(iforward0("min_r"));
+	vtm->sec = FIX2INT(iforward0("sec_r"));
+	vtm->subsecx = iforward0("sec_fraction_r");
+	vtm->utc_offset = INT2FIX(0);
+	vtm->wday = FIX2INT(iforward0("wday_r"));
+	vtm->yday = FIX2INT(iforward0("yday_r"));
+	vtm->isdst = 0;
+	vtm->zone = RSTRING_PTR(iforward0("zone_r"));
+	*timev = f_mul(f_sub(dat->r.ajd,
+			     rb_rational_new2(INT2FIX(4881175), INT2FIX(2))),
+		       INT2FIX(86400));
+    }
+    else {
+	get_dt_jd(dat);
+	get_dt_civil(dat);
+	get_dt_time(dat);
+
+	vtm->year = LONG2NUM(dat->l.year);
+	vtm->mon = dat->l.mon;
+	vtm->mday = dat->l.mday;
+	vtm->hour = dat->l.hour;
+	vtm->min = dat->l.min;
+	vtm->sec = dat->l.sec;
+	vtm->subsecx = LONG2NUM(dat->l.sf);
+	vtm->utc_offset = INT2FIX(dat->l.of);
+	vtm->wday = jd_to_wday(local_jd(dat));
+	vtm->yday = civil_to_yday(dat->l.year, dat->l.mon, dat->l.mday);
+	vtm->isdst = 0;
+	vtm->zone = RSTRING_PTR(dt_lite_zone(self));
+	*timev = f_mul(f_sub(dt_lite_ajd(self),
+			     rb_rational_new2(INT2FIX(4881175), INT2FIX(2))),
+		       INT2FIX(86400));
+    }
+}
+
 /*
  * call-seq:
+ *    dt.strftime([format="%FT%T%:z"])
+ *
+ * Return a formatted string.
+ */
+static VALUE
+dt_lite_strftime(int argc, VALUE *argv, VALUE self)
+{
+    return date_strftime_internal(argc, argv, self,
+				  "%FT%T%:z", dt_lite_set_vtm_and_timev);
+}
+
+/*
+ * call-seq:
  *    dt.marshal_dump
  *
  * Dump to Marshal format.
@@ -4214,6 +4432,7 @@
 
     rb_define_method(cDate, "to_s", d_lite_to_s, 0);
     rb_define_method(cDate, "inspect", d_lite_inspect, 0);
+    rb_define_method(cDate, "strftime", d_lite_strftime, -1);
 
     rb_define_method(cDate, "marshal_dump", d_lite_marshal_dump, 0);
     rb_define_method(cDate, "marshal_load", d_lite_marshal_load, 1);
@@ -4288,6 +4507,7 @@
 
     rb_define_method(cDateTime, "to_s", dt_lite_to_s, 0);
     rb_define_method(cDateTime, "inspect", dt_lite_inspect, 0);
+    rb_define_method(cDateTime, "strftime", dt_lite_strftime, -1);
 
     rb_define_method(cDateTime, "marshal_dump", dt_lite_marshal_dump, 0);
     rb_define_method(cDateTime, "marshal_load", dt_lite_marshal_load, 1);
Index: ext/date/lib/date/format.rb
===================================================================
--- ext/date/lib/date/format.rb	(revision 31134)
+++ ext/date/lib/date/format.rb	(revision 31135)
@@ -131,208 +131,6 @@
 
   end
 
-  def emit(e, f) # :nodoc:
-    case e
-    when Numeric
-      sign = %w(+ + -)[e <=> 0]
-      e = e.abs
-    end
-
-    s = e.to_s
-
-    if f[:s] && f[:p] == '0'
-      f[:w] -= 1
-    end
-
-    if f[:s] && f[:p] == "\s"
-      s[0,0] = sign
-    end
-
-    if f[:p] != '-'
-      s = s.rjust(f[:w], f[:p])
-    end
-
-    if f[:s] && f[:p] != "\s"
-      s[0,0] = sign
-    end
-
-    s = s.upcase if f[:u]
-    s = s.downcase if f[:d]
-    s
-  end
-
-  def emit_w(e, w, f) # :nodoc:
-    f[:w] = [f[:w], w].compact.max
-    emit(e, f)
-  end
-
-  def emit_n(e, w, f) # :nodoc:
-    f[:p] ||= '0'
-    emit_w(e, w, f)
-  end
-
-  def emit_sn(e, w, f) # :nodoc:
-    if e < 0
-      w += 1
-      f[:s] = true
-    end
-    emit_n(e, w, f)
-  end
-
-  def emit_z(e, w, f) # :nodoc:
-    w += 1
-    f[:s] = true
-    emit_n(e, w, f)
-  end
-
-  def emit_a(e, w, f) # :nodoc:
-    f[:p] ||= "\s"
-    emit_w(e, w, f)
-  end
-
-  def emit_ad(e, w, f) # :nodoc:
-    if f[:x]
-      f[:u] = true
-      f[:d] = false
-    end
-    emit_a(e, w, f)
-  end
-
-  def emit_au(e, w, f) # :nodoc:
-    if f[:x]
-      f[:u] = false
-      f[:d] = true
-    end
-    emit_a(e, w, f)
-  end
-
-  private :emit, :emit_w, :emit_n, :emit_sn, :emit_z,
-	  :emit_a, :emit_ad, :emit_au
-
-  def strftime(fmt='%F')
-    fmt.gsub(/%([-_0^#]+)?(\d+)?([EO]?(?::{1,3}z|.))/m) do
-      f = {}
-      m = $&
-      s, w, c = $1, $2, $3
-      if s
-	s.scan(/./) do |k|
-	  case k
-	  when '-'; f[:p] = '-'
-	  when '_'; f[:p] = "\s"
-	  when '0'; f[:p] = '0'
-	  when '^'; f[:u] = true
-	  when '#'; f[:x] = true
-	  end
-	end
-      end
-      if w
-	f[:w] = w.to_i
-      end
-      case c
-      when 'A'; emit_ad(DAYNAMES[wday], 0, f)
-      when 'a'; emit_ad(ABBR_DAYNAMES[wday], 0, f)
-      when 'B'; emit_ad(MONTHNAMES[mon], 0, f)
-      when 'b'; emit_ad(ABBR_MONTHNAMES[mon], 0, f)
-      when 'C', 'EC'; emit_sn((year / 100).floor, 2, f)
-      when 'c', 'Ec'; emit_a(strftime('%a %b %e %H:%M:%S %Y'), 0, f)
-      when 'D'; emit_a(strftime('%m/%d/%y'), 0, f)
-      when 'd', 'Od'; emit_n(mday, 2, f)
-      when 'e', 'Oe'; emit_a(mday, 2, f)
-      when 'F'
-	if m == '%F'
-	  format('%.4d-%02d-%02d', year, mon, mday) # 4p
-	else
-	  emit_a(strftime('%Y-%m-%d'), 0, f)
-	end
-      when 'G'; emit_sn(cwyear, 4, f)
-      when 'g'; emit_n(cwyear % 100, 2, f)
-      when 'H', 'OH'; emit_n(hour, 2, f)
-      when 'h'; emit_ad(strftime('%b'), 0, f)
-      when 'I', 'OI'; emit_n((hour % 12).nonzero? || 12, 2, f)
-      when 'j'; emit_n(yday, 3, f)
-      when 'k'; emit_a(hour, 2, f)
-      when 'L'
-	f[:p] = nil
-	w = f[:w] || 3
-	u = 10**w
-	emit_n((sec_fraction * u).floor, w, f)
-      when 'l'; emit_a((hour % 12).nonzero? || 12, 2, f)
-      when 'M', 'OM'; emit_n(min, 2, f)
-      when 'm', 'Om'; emit_n(mon, 2, f)
-      when 'N'
-	f[:p] = nil
-	w = f[:w] || 9
-	u = 10**w
-	emit_n((sec_fraction * u).floor, w, f)
-      when 'n'; emit_a("\n", 0, f)
-      when 'P'; emit_ad(strftime('%p').downcase, 0, f)
-      when 'p'; emit_au(if hour < 12 then 'AM' else 'PM' end, 0, f)
-      when 'Q'
-	s = ((ajd - UNIX_EPOCH_IN_AJD) / MILLISECONDS_IN_DAY).round
-	emit_sn(s, 1, f)
-      when 'R'; emit_a(strftime('%H:%M'), 0, f)
-      when 'r'; emit_a(strftime('%I:%M:%S %p'), 0, f)
-      when 'S', 'OS'; emit_n(sec, 2, f)
-      when 's'
-	s = ((ajd - UNIX_EPOCH_IN_AJD) / SECONDS_IN_DAY).round
-	emit_sn(s, 1, f)
-      when 'T'
-	if m == '%T'
-	  format('%02d:%02d:%02d', hour, min, sec) # 4p
-	else
-	  emit_a(strftime('%H:%M:%S'), 0, f)
-	end
-      when 't'; emit_a("\t", 0, f)
-      when 'U', 'W', 'OU', 'OW'
-	emit_n(if c[-1,1] == 'U' then wnum0 else wnum1 end, 2, f)
-      when 'u', 'Ou'; emit_n(cwday, 1, f)
-      when 'V', 'OV'; emit_n(cweek, 2, f)
-      when 'v'; emit_a(strftime('%e-%b-%Y'), 0, f)
-      when 'w', 'Ow'; emit_n(wday, 1, f)
-      when 'X', 'EX'; emit_a(strftime('%H:%M:%S'), 0, f)
-      when 'x', 'Ex'; emit_a(strftime('%m/%d/%y'), 0, f)
-      when 'Y', 'EY'; emit_sn(year, 4, f)
-      when 'y', 'Ey', 'Oy'; emit_n(year % 100, 2, f)
-      when 'Z'; emit_au(strftime('%:z'), 0, f)
-      when /\A(:{0,3})z/
-	t = $1.size
-	sign = if offset < 0 then -1 else +1 end
-	fr = offset.abs
-	ss = fr.div(SECONDS_IN_DAY) # 4p
-	hh, ss = ss.divmod(3600)
-	mm, ss = ss.divmod(60)
-	if t == 3
-	  if    ss.nonzero? then t =  2
-	  elsif mm.nonzero? then t =  1
-	  else                   t = -1
-	  end
-	end
-	case t
-	when -1
-	  tail = []
-	  sep = ''
-	when 0
-	  f[:w] -= 2 if f[:w]
-	  tail = ['%02d' % mm]
-	  sep = ''
-	when 1
-	  f[:w] -= 3 if f[:w]
-	  tail = ['%02d' % mm]
-	  sep = ':'
-	when 2
-	  f[:w] -= 6 if f[:w]
-	  tail = ['%02d' % mm, '%02d' % ss]
-	  sep = ':'
-	end
-	([emit_z(sign * hh, 2, f)] + tail).join(sep)
-      when '%'; emit_a('%', 0, f)
-      when '+'; emit_a(strftime('%a %b %e %H:%M:%S %Z %Y'), 0, f)
-      else
-	m
-      end
-    end
-  end
-
 # alias_method :format, :strftime
 
   def asctime() strftime('%c') end
Index: ext/date/timev.h
===================================================================
--- ext/date/timev.h	(revision 0)
+++ ext/date/timev.h	(revision 31135)
@@ -0,0 +1,21 @@
+#ifndef RUBY_TIMEV_H
+#define RUBY_TIMEV_H
+
+struct vtm {
+    VALUE year; /* 2000 for example.  Integer. */
+    int mon; /* 1..12 */
+    int mday; /* 1..31 */
+    int hour; /* 0..23 */
+    int min; /* 0..59 */
+    int sec; /* 0..60 */
+    VALUE subsecx; /* 0 <= subsecx < TIME_SCALE.  possibly Rational. */
+    VALUE utc_offset; /* -3600 as -01:00 for example.  possibly Rational. */
+    int wday; /* 0:Sunday, 1:Monday, ..., 6:Saturday */
+    int yday; /* 1..366 */
+    int isdst; /* 0:StandardTime 1:DayLightSavingTime */
+    const char *zone; /* "JST", "EST", "EDT", etc. */
+};
+
+#define TIME_SCALE 1000000000
+
+#endif

Property changes on: ext/date/timev.h
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: ext/date/date_strftime.c
===================================================================
--- ext/date/date_strftime.c	(revision 0)
+++ ext/date/date_strftime.c	(revision 31135)
@@ -0,0 +1,1252 @@
+/* -*- c-file-style: "linux" -*- */
+
+/*
+ * strftime.c
+ *
+ * Public-domain implementation of ANSI C library routine.
+ *
+ * It's written in old-style C for maximal portability.
+ * However, since I'm used to prototypes, I've included them too.
+ *
+ * If you want stuff in the System V ascftime routine, add the SYSV_EXT define.
+ * For extensions from SunOS, add SUNOS_EXT.
+ * For stuff needed to implement the P1003.2 date command, add POSIX2_DATE.
+ * For VMS dates, add VMS_EXT.
+ * For a an RFC822 time format, add MAILHEADER_EXT.
+ * For ISO week years, add ISO_DATE_EXT.
+ * For complete POSIX semantics, add POSIX_SEMANTICS.
+ *
+ * The code for %c, %x, and %X now follows the 1003.2 specification for
+ * the POSIX locale.
+ * This version ignores LOCALE information.
+ * It also doesn't worry about multi-byte characters.
+ * So there.
+ *
+ * This file is also shipped with GAWK (GNU Awk), gawk specific bits of
+ * code are included if GAWK is defined.
+ *
+ * Arnold Robbins
+ * January, February, March, 1991
+ * Updated March, April 1992
+ * Updated April, 1993
+ * Updated February, 1994
+ * Updated May, 1994
+ * Updated January, 1995
+ * Updated September, 1995
+ * Updated January, 1996
+ *
+ * Fixes from ado@e...
+ * February 1991, May 1992
+ * Fixes from Tor Lillqvist tml@t...
+ * May, 1993
+ * Further fixes from ado@e...
+ * February 1994
+ * %z code from chip@c...
+ * Applied September 1995
+ * %V code fixed (again) and %G, %g added,
+ * January 1996
+ */
+
+#include "ruby/ruby.h"
+#include "timev.h"
+
+#ifndef GAWK
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <errno.h>
+#endif
+#if defined(TM_IN_SYS_TIME) || !defined(GAWK)
+#include <sys/types.h>
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#endif
+#include <math.h>
+
+/* defaults: season to taste */
+#define SYSV_EXT	1	/* stuff in System V ascftime routine */
+#define SUNOS_EXT	1	/* stuff in SunOS strftime routine */
+#define POSIX2_DATE	1	/* stuff in Posix 1003.2 date command */
+#define VMS_EXT		1	/* include %v for VMS date format */
+#define MAILHEADER_EXT	1	/* add %z for HHMM format */
+#define ISO_DATE_EXT	1	/* %G and %g for year of ISO week */
+
+#if defined(ISO_DATE_EXT)
+#if ! defined(POSIX2_DATE)
+#define POSIX2_DATE	1
+#endif
+#endif
+
+#if defined(POSIX2_DATE)
+#if ! defined(SYSV_EXT)
+#define SYSV_EXT	1
+#endif
+#if ! defined(SUNOS_EXT)
+#define SUNOS_EXT	1
+#endif
+#endif
+
+#if defined(POSIX2_DATE)
+#define adddecl(stuff)	stuff
+#else
+#define adddecl(stuff)
+#endif
+
+#undef strchr	/* avoid AIX weirdness */
+
+#if !defined __STDC__ && !defined _WIN32
+#define const	/**/
+static int weeknumber();
+adddecl(static int iso8601wknum();)
+static int weeknumber_v();
+adddecl(static int iso8601wknum_v();)
+#else
+static int weeknumber(const struct tm *timeptr, int firstweekday);
+adddecl(static int iso8601wknum(const struct tm *timeptr);)
+static int weeknumber_v(const struct vtm *vtm, int firstweekday);
+adddecl(static int iso8601wknum_v(const struct vtm *vtm);)
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#else
+extern void *malloc();
+extern void *realloc();
+extern char *getenv();
+extern char *strchr();
+#endif
+
+#define range(low, item, hi)	max((low), min((item), (hi)))
+
+#undef min	/* just in case */
+
+/* min --- return minimum of two numbers */
+
+#ifndef __STDC__
+static inline int
+min(a, b)
+int a, b;
+#else
+static inline int
+min(int a, int b)
+#endif
+{
+	return (a < b ? a : b);
+}
+
+#undef max	/* also, just in case */
+
+/* max --- return maximum of two numbers */
+
+#ifndef __STDC__
+static inline int
+max(a, b)
+int a, b;
+#else
+static inline int
+max(int a, int b)
+#endif
+{
+	return (a > b ? a : b);
+}
+
+#ifdef NO_STRING_LITERAL_CONCATENATION
+#error No string literal concatenation
+#endif
+
+#define add(x,y) (rb_funcall((x), '+', 1, (y)))
+#define sub(x,y) (rb_funcall((x), '-', 1, (y)))
+#define mul(x,y) (rb_funcall((x), '*', 1, (y)))
+#define quo(x,y) (rb_funcall((x), rb_intern("quo"), 1, (y)))
+#define div(x,y) (rb_funcall((x), rb_intern("div"), 1, (y)))
+#define mod(x,y) (rb_funcall((x), '%', 1, (y)))
+
+/* strftime --- produce formatted time */
+
+static size_t
+date_strftime_with_timespec(char *s, size_t maxsize, const char *format, const struct vtm *vtm, VALUE timev, struct timespec *ts, int gmt)
+{
+	char *endp = s + maxsize;
+	char *start = s;
+	const char *sp, *tp;
+	auto char tbuf[100];
+	long off;
+	ptrdiff_t i;
+	int w;
+	long y;
+	int precision, flags, colons;
+	char padding;
+	enum {LEFT, CHCASE, LOWER, UPPER, LOCALE_O, LOCALE_E};
+#define BIT_OF(n) (1U<<(n))
+
+	/* various tables, useful in North America */
+	static const char days_l[][10] = {
+		"Sunday", "Monday", "Tuesday", "Wednesday",
+		"Thursday", "Friday", "Saturday",
+	};
+	static const char months_l[][10] = {
+		"January", "February", "March", "April",
+		"May", "June", "July", "August", "September",
+		"October", "November", "December",
+	};
+	static const char ampm[][3] = { "AM", "PM", };
+
+	if (s == NULL || format == NULL || vtm == NULL || maxsize == 0)
+		return 0;
+
+	/* quick check if we even need to bother */
+	if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) {
+	err:
+		errno = ERANGE;
+		return 0;
+	}
+
+	for (; *format && s < endp - 1; format++) {
+#define FLAG_FOUND() do { \
+			if (precision > 0 || flags & (BIT_OF(LOCALE_E)|BIT_OF(LOCALE_O))) \
+				goto unknown; \
+		} while (0)
+#define NEEDS(n) do if (s + (n) >= endp - 1) goto err; while (0)
+#define FILL_PADDING(i) do { \
+	if (!(flags & BIT_OF(LEFT)) && precision > (i)) { \
+		NEEDS(precision); \
+		memset(s, padding ? padding : ' ', precision - (i)); \
+		s += precision - (i); \
+	} \
+	else { \
+		NEEDS(i); \
+	} \
+} while (0);
+#define FMT(def_pad, def_prec, fmt, val) \
+		do { \
+			int l; \
+			if (precision <= 0) precision = (def_prec); \
+			if (flags & BIT_OF(LEFT)) precision = 1; \
+			l = snprintf(s, endp - s, \
+				     ((padding == '0' || (!padding && (def_pad) == '0')) ? "%0*"fmt : "%*"fmt), \
+				     precision, (val)); \
+			if (l < 0) goto err; \
+			s += l; \
+		} while (0)
+#define STRFTIME(fmt) \
+		do { \
+			i = date_strftime_with_timespec(s, endp - s, (fmt), vtm, timev, ts, gmt); \
+			if (!i) return 0; \
+			if (precision > i) {\
+				if (start + maxsize < s + precision) { \
+					errno = ERANGE; \
+					return 0; \
+				} \
+				memmove(s + precision - i, s, i);\
+				memset(s, padding ? padding : ' ', precision - i); \
+				s += precision;	\
+	                }\
+			else s += i; \
+		} while (0)
+#define FMTV(def_pad, def_prec, fmt, val) \
+                do { \
+                        VALUE tmp = (val); \
+                        if (FIXNUM_P(tmp)) { \
+                                FMT((def_pad), (def_prec), "l"fmt, FIX2LONG(tmp)); \
+                        } \
+                        else { \
+                                VALUE args[2], result; \
+                                size_t l; \
+                                if (precision <= 0) precision = (def_prec); \
+                                if (flags & BIT_OF(LEFT)) precision = 1; \
+                                args[0] = INT2FIX(precision); \
+                                args[1] = (val); \
+                                if (padding == '0' || (!padding && (def_pad) == '0')) \
+                                        result = rb_str_format(2, args, rb_str_new2("%0*"fmt)); \
+                                else \
+                                        result = rb_str_format(2, args, rb_str_new2("%*"fmt)); \
+                                l = strlcpy(s, StringValueCStr(result), endp-s); \
+                                if ((size_t)(endp-s) <= l) \
+                                        goto err; \
+                                s += l; \
+                        } \
+                } while (0)
+#define SKIP_MODIFIER_E \
+		if (flags & BIT_OF(LOCALE_E)) {	\
+			format--; \
+			goto unknown; \
+		}
+#define SKIP_MODIFIER_O \
+		if (flags & BIT_OF(LOCALE_O)) { \
+			format--; \
+			goto unknown; \
+		}
+#define SKIP_MODIFIER_EO \
+		{ SKIP_MODIFIER_E; SKIP_MODIFIER_O }
+
+		if (*format != '%') {
+			*s++ = *format;
+			continue;
+		}
+		tp = tbuf;
+		sp = format;
+		precision = -1;
+		flags = 0;
+		padding = 0;
+                colons = 0;
+	again:
+		switch (*++format) {
+		case '\0':
+			format--;
+			goto unknown;
+
+		case '%':
+			SKIP_MODIFIER_EO;
+			FILL_PADDING(1);
+			*s++ = '%';
+			continue;
+
+		case 'a':	/* abbreviated weekday name */
+			SKIP_MODIFIER_EO;
+			if (flags & BIT_OF(CHCASE)) {
+				flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE));
+				flags |= BIT_OF(UPPER);
+			}
+			if (vtm->wday < 0 || vtm->wday > 6)
+				i = 1, tp = "?";
+			else
+				i = 3, tp = days_l[vtm->wday];
+			break;
+
+		case 'A':	/* full weekday name */
+			SKIP_MODIFIER_EO;
+			if (flags & BIT_OF(CHCASE)) {
+				flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE));
+				flags |= BIT_OF(UPPER);
+			}
+			if (vtm->wday < 0 || vtm->wday > 6)
+				i = 1, tp = "?";
+			else
+				i = strlen(tp = days_l[vtm->wday]);
+			break;
+
+#ifdef SYSV_EXT
+		case 'h':	/* abbreviated month name */
+#endif
+		case 'b':	/* abbreviated month name */
+			SKIP_MODIFIER_EO;
+			if (flags & BIT_OF(CHCASE)) {
+				flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE));
+				flags |= BIT_OF(UPPER);
+			}
+			if (vtm->mon < 1 || vtm->mon > 12)
+				i = 1, tp = "?";
+			else
+				i = 3, tp = months_l[vtm->mon-1];
+			break;
+
+		case 'B':	/* full month name */
+			SKIP_MODIFIER_EO;
+			if (flags & BIT_OF(CHCASE)) {
+				flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE));
+				flags |= BIT_OF(UPPER);
+			}
+			if (vtm->mon < 1 || vtm->mon > 12)
+				i = 1, tp = "?";
+			else
+				i = strlen(tp = months_l[vtm->mon-1]);
+			break;
+
+		case 'c':	/* appropriate date and time representation */
+			SKIP_MODIFIER_O;
+			STRFTIME("%a %b %e %H:%M:%S %Y");
+			continue;
+
+		case 'd':	/* day of the month, 01 - 31 */
+			SKIP_MODIFIER_E;
+			i = range(1, vtm->mday, 31);
+			FMT('0', 2, "d", (int)i);
+			continue;
+
+		case 'H':	/* hour, 24-hour clock, 00 - 23 */
+			SKIP_MODIFIER_E;
+			i = range(0, vtm->hour, 23);
+			FMT('0', 2, "d", (int)i);
+			continue;
+
+		case 'I':	/* hour, 12-hour clock, 01 - 12 */
+			SKIP_MODIFIER_E;
+			i = range(0, vtm->hour, 23);
+			if (i == 0)
+				i = 12;
+			else if (i > 12)
+				i -= 12;
+			FMT('0', 2, "d", (int)i);
+			continue;
+
+		case 'j':	/* day of the year, 001 - 366 */
+			SKIP_MODIFIER_EO;
+			FMT('0', 3, "d", vtm->yday);
+			continue;
+
+		case 'm':	/* month, 01 - 12 */
+			SKIP_MODIFIER_E;
+			i = range(1, vtm->mon, 12);
+			FMT('0', 2, "d", (int)i);
+			continue;
+
+		case 'M':	/* minute, 00 - 59 */
+			SKIP_MODIFIER_E;
+			i = range(0, vtm->min, 59);
+			FMT('0', 2, "d", (int)i);
+			continue;
+
+		case 'p':	/* AM or PM based on 12-hour clock */
+		case 'P':	/* am or pm based on 12-hour clock */
+			SKIP_MODIFIER_EO;
+			if ((*format == 'p' && (flags & BIT_OF(CHCASE))) ||
+			    (*format == 'P' && !(flags & (BIT_OF(CHCASE)|BIT_OF(UPPER))))) {
+				flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE));
+				flags |= BIT_OF(LOWER);
+			}
+			i = range(0, vtm->hour, 23);
+			if (i < 12)
+				tp = ampm[0];
+			else
+				tp = ampm[1];
+			i = 2;
+			break;
+
+		case 's':
+			SKIP_MODIFIER_EO;
+                        if (ts) {
+                                time_t sec = ts->tv_sec;
+                                if (~(time_t)0 <= 0)
+                                    FMT('0', 1, PRI_TIMET_PREFIX"d", sec);
+                                else
+                                    FMT('0', 1, PRI_TIMET_PREFIX"u", sec);
+                        }
+                        else {
+                                VALUE sec = div(timev, INT2FIX(1));
+                                FMTV('0', 1, "d", sec);
+                        }
+                        continue;
+
+		case 'Q':
+			SKIP_MODIFIER_EO;
+			{
+				VALUE sec = div(timev,
+						rb_rational_new2(INT2FIX(1),
+								 INT2FIX(1000)));
+				FMTV('0', 1, "d", sec);
+			}
+                        continue;
+
+		case 'S':	/* second, 00 - 60 */
+			SKIP_MODIFIER_E;
+			i = range(0, vtm->sec, 60);
+			FMT('0', 2, "d", (int)i);
+			continue;
+
+		case 'U':	/* week of year, Sunday is first day of week */
+			SKIP_MODIFIER_E;
+			FMT('0', 2, "d", weeknumber_v(vtm, 0));
+			continue;
+
+		case 'w':	/* weekday, Sunday == 0, 0 - 6 */
+			SKIP_MODIFIER_E;
+			i = range(0, vtm->wday, 6);
+			FMT('0', 1, "d", (int)i);
+			continue;
+
+		case 'W':	/* week of year, Monday is first day of week */
+			SKIP_MODIFIER_E;
+			FMT('0', 2, "d", weeknumber_v(vtm, 1));
+			continue;
+
+		case 'x':	/* appropriate date representation */
+			SKIP_MODIFIER_O;
+			STRFTIME("%m/%d/%y");
+			continue;
+
+		case 'X':	/* appropriate time representation */
+			SKIP_MODIFIER_O;
+			STRFTIME("%H:%M:%S");
+			continue;
+
+		case 'y':	/* year without a century, 00 - 99 */
+			i = NUM2INT(mod(vtm->year, INT2FIX(100)));
+			FMT('0', 2, "d", (int)i);
+			continue;
+
+		case 'Y':	/* year with century */
+			SKIP_MODIFIER_O;
+                        if (FIXNUM_P(vtm->year)) {
+                            long y = FIX2LONG(vtm->year);
+                            FMT('0', 0 <= y ? 4 : 5, "ld", y);
+                        }
+                        else {
+                            FMTV('0', 4, "d", vtm->year);
+                        }
+			continue;
+
+#ifdef MAILHEADER_EXT
+		case 'z':	/* time zone offset east of GMT e.g. -0600 */
+			SKIP_MODIFIER_EO;
+			{
+				int aoff, hl, hw;
+
+				if (gmt) {
+					off = 0;
+				}
+				else {
+					off = NUM2LONG(rb_funcall(vtm->utc_offset, rb_intern("round"), 0));
+				}
+
+				aoff = off;
+				if (aoff < 0)
+					aoff = -off;
+
+				if ((aoff / 3600) < 10)
+					hl = 1;
+				else
+					hl = 2;
+				hw = 2;
+				if (flags & BIT_OF(LEFT) && hl == 1)
+					hw = 1;
+
+				switch (colons) {
+				case 0: /* %z -> +hhmm */
+					precision = precision <= (3 + hw) ? hw : precision-3;
+					NEEDS(precision + 3);
+					break;
+
+				case 1: /* %:z -> +hh:mm */
+					precision = precision <= (4 + hw) ? hw : precision-4;
+					NEEDS(precision + 4);
+					break;
+
+				case 2: /* %::z -> +hh:mm:ss */
+					precision = precision <= (7 + hw) ? hw : precision-7;
+					NEEDS(precision + 7);
+					break;
+
+				case 3: /* %:::z -> +hh[:mm[:ss]] */
+				{
+					if (aoff % 3600 == 0) {
+						precision = precision <= (1 + hw) ? hw : precision-1;
+						NEEDS(precision + 3);
+					}
+					else if (aoff % 60 == 0) {
+						precision = precision <= (4 + hw) ? hw : precision-4;
+						NEEDS(precision + 4);
+					}
+					else {
+						precision = precision <= (7 + hw) ? hw : precision-7;
+						NEEDS(precision + 7);
+					}
+				}
+				break;
+
+				default:
+					format--;
+					goto unknown;
+				}
+				if (padding == ' ' && precision > hl) {
+					i = snprintf(s, endp - s, "%*s", precision - hl, "");
+					precision = hl;
+					if (i < 0) goto err;
+					s += i;
+				}
+				if (off < 0) {
+					off = -off;
+					*s++ = '-';
+				} else {
+					*s++ = '+';
+				}
+				i = snprintf(s, endp - s, "%.*ld", precision, off / 3600);
+				if (i < 0) goto err;
+				s += i;
+				off = off % 3600;
+				if (colons == 3 && off == 0)
+					continue;
+				if (1 <= colons)
+					*s++ = ':';
+				i = snprintf(s, endp - s, "%02d", (int)(off / 60));
+				if (i < 0) goto err;
+				s += i;
+				off = off % 60;
+				if (colons == 3 && off == 0)
+					continue;
+				if (2 <= colons) {
+					*s++ = ':';
+					i = snprintf(s, endp - s, "%02d", (int)off);
+					if (i < 0) goto err;
+					s += i;
+				}
+			}
+			continue;
+#endif /* MAILHEADER_EXT */
+
+		case 'Z':	/* time zone name or abbreviation */
+			SKIP_MODIFIER_EO;
+			if (flags & BIT_OF(CHCASE)) {
+				flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE));
+				flags |= BIT_OF(LOWER);
+			}
+			if (gmt) {
+				i = 3;
+				tp = "UTC";
+				break;
+			}
+                        if (vtm->zone == NULL)
+                            tp = "";
+                        else
+                            tp = vtm->zone;
+			i = strlen(tp);
+			break;
+
+#ifdef SYSV_EXT
+		case 'n':	/* same as \n */
+			SKIP_MODIFIER_EO;
+			FILL_PADDING(1);
+			*s++ = '\n';
+			continue;
+
+		case 't':	/* same as \t */
+			SKIP_MODIFIER_EO;
+			FILL_PADDING(1);
+			*s++ = '\t';
+			continue;
+
+		case 'D':	/* date as %m/%d/%y */
+			SKIP_MODIFIER_EO;
+			STRFTIME("%m/%d/%y");
+			continue;
+
+		case 'e':	/* day of month, blank padded */
+			SKIP_MODIFIER_E;
+			FMT(' ', 2, "d", range(1, vtm->mday, 31));
+			continue;
+
+		case 'r':	/* time as %I:%M:%S %p */
+			SKIP_MODIFIER_EO;
+			STRFTIME("%I:%M:%S %p");
+			continue;
+
+		case 'R':	/* time as %H:%M */
+			SKIP_MODIFIER_EO;
+			STRFTIME("%H:%M");
+			continue;
+
+		case 'T':	/* time as %H:%M:%S */
+			SKIP_MODIFIER_EO;
+			STRFTIME("%H:%M:%S");
+			continue;
+#endif
+
+#ifdef SUNOS_EXT
+		case 'k':	/* hour, 24-hour clock, blank pad */
+			SKIP_MODIFIER_EO;
+			i = range(0, vtm->hour, 23);
+			FMT(' ', 2, "d", (int)i);
+			continue;
+
+		case 'l':	/* hour, 12-hour clock, 1 - 12, blank pad */
+			SKIP_MODIFIER_EO;
+			i = range(0, vtm->hour, 23);
+			if (i == 0)
+				i = 12;
+			else if (i > 12)
+				i -= 12;
+			FMT(' ', 2, "d", (int)i);
+			continue;
+#endif
+
+
+#ifdef VMS_EXT
+		case 'v':	/* date as dd-bbb-YYYY */
+			SKIP_MODIFIER_EO;
+			STRFTIME("%e-%^b-%4Y");
+			continue;
+#endif
+
+
+#ifdef POSIX2_DATE
+		case 'C':
+			SKIP_MODIFIER_O;
+                        FMTV('0', 2, "d", div(vtm->year, INT2FIX(100)));
+			continue;
+
+		case 'E':
+			/* POSIX locale extensions, ignored for now */
+			flags |= BIT_OF(LOCALE_E);
+			goto again;
+		case 'O':
+			/* POSIX locale extensions, ignored for now */
+			flags |= BIT_OF(LOCALE_O);
+			goto again;
+
+		case 'V':	/* week of year according ISO 8601 */
+			SKIP_MODIFIER_E;
+			FMT('0', 2, "d", iso8601wknum_v(vtm));
+			continue;
+
+		case 'u':
+		/* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
+			SKIP_MODIFIER_E;
+			FMT('0', 1, "d", vtm->wday == 0 ? 7 : vtm->wday);
+			continue;
+#endif	/* POSIX2_DATE */
+
+#ifdef ISO_DATE_EXT
+		case 'G':
+		case 'g':
+			/*
+			 * Year of ISO week.
+			 *
+			 * If it's December but the ISO week number is one,
+			 * that week is in next year.
+			 * If it's January but the ISO week number is 52 or
+			 * 53, that week is in last year.
+			 * Otherwise, it's this year.
+			 */
+			SKIP_MODIFIER_EO;
+                        {
+                                VALUE yv = vtm->year;
+                                w = iso8601wknum_v(vtm);
+                                if (vtm->mon == 12 && w == 1)
+                                        yv = add(yv, INT2FIX(1));
+                                else if (vtm->mon == 1 && w >= 52)
+                                        yv = sub(yv, INT2FIX(1));
+
+                                if (*format == 'G') {
+                                        if (FIXNUM_P(yv)) {
+                                                long y = FIX2LONG(yv);
+                                                FMT('0', 0 <= y ? 4 : 5, "ld", y);
+                                        }
+                                        else {
+                                                FMTV('0', 4, "d", yv);
+                                        }
+                                }
+                                else {
+                                        yv = mod(yv, INT2FIX(100));
+                                        y = FIX2LONG(yv);
+                                        FMT('0', 2, "ld", y);
+                                }
+                                continue;
+                        }
+
+#endif /* ISO_DATE_EXT */
+
+
+		case 'L':
+			SKIP_MODIFIER_EO;
+			w = 3;
+			goto subsec;
+
+		case 'N':
+			SKIP_MODIFIER_EO;
+			/*
+			 * fractional second digits. default is 9 digits
+			 * (nanosecond).
+			 *
+			 * %3N  millisecond (3 digits)
+			 * %6N  microsecond (6 digits)
+			 * %9N  nanosecond (9 digits)
+			 */
+			w = 9;
+		subsec:
+                        if (precision <= 0) {
+                            precision = w;
+                        }
+                        NEEDS(precision);
+
+                        if (ts) {
+                                long subsec = ts->tv_nsec;
+                                if (9 < precision) {
+                                        snprintf(s, endp - s, "%09ld", subsec);
+                                        memset(s+9, '0', precision-9);
+                                        s += precision;
+                                }
+                                else {
+                                        int i;
+                                        for (i = 0; i < 9-precision; i++)
+                                                subsec /= 10;
+                                        snprintf(s, endp - s, "%0*ld", precision, subsec);
+                                        s += precision;
+                                }
+                        }
+                        else {
+                                VALUE subsec = mod(timev, INT2FIX(1));
+                                int ww;
+                                long n;
+
+                                ww = precision;
+                                while (9 <= ww) {
+                                        subsec = mul(subsec, INT2FIX(1000000000));
+                                        ww -= 9;
+                                }
+                                n = 1;
+                                for (; 0 < ww; ww--)
+                                        n *= 10;
+                                if (n != 1)
+                                        subsec = mul(subsec, INT2FIX(n));
+                                subsec = div(subsec, INT2FIX(1));
+
+                                if (FIXNUM_P(subsec)) {
+                                        int l;
+                                        l = snprintf(s, endp - s, "%0*ld", precision, FIX2LONG(subsec));
+                                        s += precision;
+                                }
+                                else {
+                                        VALUE args[2], result;
+                                        size_t l;
+                                        args[0] = INT2FIX(precision);
+                                        args[1] = subsec;
+                                        result = rb_str_format(2, args, rb_str_new2("%0*d"));
+                                        l = strlcpy(s, StringValueCStr(result), endp-s);
+                                        s += precision;
+                                }
+			}
+			continue;
+
+		case 'F':	/*  Equivalent to %Y-%m-%d */
+			SKIP_MODIFIER_EO;
+			STRFTIME("%Y-%m-%d");
+			continue;
+		case '+':
+			SKIP_MODIFIER_EO;
+			STRFTIME("%a %b %e %H:%M:%S %Z %Y");
+			continue;
+
+		case '-':
+			FLAG_FOUND();
+			flags |= BIT_OF(LEFT);
+			padding = precision = 0;
+			goto again;
+
+		case '^':
+			FLAG_FOUND();
+			flags |= BIT_OF(UPPER);
+			goto again;
+
+		case '#':
+			FLAG_FOUND();
+			flags |= BIT_OF(CHCASE);
+			goto again;
+
+		case '_':
+			FLAG_FOUND();
+			padding = ' ';
+			goto again;
+
+		case ':':
+                        colons++;
+			goto again;
+
+		case '0':
+			padding = '0';
+		case '1':  case '2': case '3': case '4':
+		case '5': case '6':  case '7': case '8': case '9':
+			{
+				char *e;
+				precision = (int)strtoul(format, &e, 10);
+				format = e - 1;
+				goto again;
+			}
+
+		default:
+		unknown:
+			i = format - sp + 1;
+			tp = sp;
+			precision = -1;
+			flags = 0;
+			padding = 0;
+                        colons = 0;
+			break;
+		}
+		if (i) {
+			FILL_PADDING(i);
+			memcpy(s, tp, i);
+			switch (flags & (BIT_OF(UPPER)|BIT_OF(LOWER))) {
+			case BIT_OF(UPPER):
+				do {
+					if (ISLOWER(*s)) *s = TOUPPER(*s);
+				} while (s++, --i);
+				break;
+			case BIT_OF(LOWER):
+				do {
+					if (ISUPPER(*s)) *s = TOLOWER(*s);
+				} while (s++, --i);
+				break;
+			default:
+				s += i;
+				break;
+			}
+		}
+	}
+	if (s >= endp) {
+		goto err;
+	}
+	if (*format == '\0') {
+		*s = '\0';
+		return (s - start);
+	} else
+		return 0;
+}
+
+size_t
+date_strftime(char *s, size_t maxsize, const char *format, const struct vtm *vtm, VALUE timev, int gmt)
+{
+    return date_strftime_with_timespec(s, maxsize, format, vtm, timev, NULL, gmt);
+}
+
+size_t
+date_strftime_timespec(char *s, size_t maxsize, const char *format, const struct vtm *vtm, struct timespec *ts, int gmt)
+{
+    return date_strftime_with_timespec(s, maxsize, format, vtm, Qnil, ts, gmt);
+}
+
+/* isleap --- is a year a leap year? */
+
+#ifndef __STDC__
+static int
+isleap(year)
+long year;
+#else
+static int
+isleap(long year)
+#endif
+{
+	return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+}
+
+
+static void
+vtm2tm_noyear(const struct vtm *vtm, struct tm *result)
+{
+    struct tm tm;
+
+    /* for isleap() in iso8601wknum.  +100 is -1900 (mod 400). */
+    tm.tm_year = FIX2INT(mod(vtm->year, INT2FIX(400))) + 100;
+
+    tm.tm_mon = vtm->mon-1;
+    tm.tm_mday = vtm->mday;
+    tm.tm_hour = vtm->hour;
+    tm.tm_min = vtm->min;
+    tm.tm_sec = vtm->sec;
+    tm.tm_wday = vtm->wday;
+    tm.tm_yday = vtm->yday-1;
+    tm.tm_isdst = vtm->isdst;
+#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
+    tm.tm_gmtoff = NUM2LONG(vtm->utc_offset);
+#endif
+#if defined(HAVE_TM_ZONE)
+    tm.tm_zone = (char *)vtm->zone;
+#endif
+    *result = tm;
+}
+
+#ifdef POSIX2_DATE
+/* iso8601wknum --- compute week number according to ISO 8601 */
+
+#ifndef __STDC__
+static int
+iso8601wknum(timeptr)
+const struct tm *timeptr;
+#else
+static int
+iso8601wknum(const struct tm *timeptr)
+#endif
+{
+	/*
+	 * From 1003.2:
+	 *	If the week (Monday to Sunday) containing January 1
+	 *	has four or more days in the new year, then it is week 1;
+	 *	otherwise it is the highest numbered week of the previous
+	 *	year (52 or 53), and the next week is week 1.
+	 *
+	 * ADR: This means if Jan 1 was Monday through Thursday,
+	 *	it was week 1, otherwise week 52 or 53.
+	 *
+	 * XPG4 erroneously included POSIX.2 rationale text in the
+	 * main body of the standard. Thus it requires week 53.
+	 */
+
+	int weeknum, jan1day;
+
+	/* get week number, Monday as first day of the week */
+	weeknum = weeknumber(timeptr, 1);
+
+	/*
+	 * With thanks and tip of the hatlo to tml@t...
+	 *
+	 * What day of the week does January 1 fall on?
+	 * We know that
+	 *	(timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
+	 *		(timeptr->tm_wday - jan1.tm_wday) MOD 7
+	 * and that
+	 * 	jan1.tm_yday == 0
+	 * and that
+	 * 	timeptr->tm_wday MOD 7 == timeptr->tm_wday
+	 * from which it follows that. . .
+	 */
+	jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
+	if (jan1day < 0)
+		jan1day += 7;
+
+	/*
+	 * If Jan 1 was a Monday through Thursday, it was in
+	 * week 1.  Otherwise it was last year's highest week, which is
+	 * this year's week 0.
+	 *
+	 * What does that mean?
+	 * If Jan 1 was Monday, the week number is exactly right, it can
+	 *	never be 0.
+	 * If it was Tuesday through Thursday, the weeknumber is one
+	 *	less than it should be, so we add one.
+	 * Otherwise, Friday, Saturday or Sunday, the week number is
+	 * OK, but if it is 0, it needs to be 52 or 53.
+	 */
+	switch (jan1day) {
+	case 1:		/* Monday */
+		break;
+	case 2:		/* Tuesday */
+	case 3:		/* Wednesday */
+	case 4:		/* Thursday */
+		weeknum++;
+		break;
+	case 5:		/* Friday */
+	case 6:		/* Saturday */
+	case 0:		/* Sunday */
+		if (weeknum == 0) {
+#ifdef USE_BROKEN_XPG4
+			/* XPG4 (as of March 1994) says 53 unconditionally */
+			weeknum = 53;
+#else
+			/* get week number of last week of last year */
+			struct tm dec31ly;	/* 12/31 last year */
+			dec31ly = *timeptr;
+			dec31ly.tm_year--;
+			dec31ly.tm_mon = 11;
+			dec31ly.tm_mday = 31;
+			dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
+			dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900L);
+			weeknum = iso8601wknum(& dec31ly);
+#endif
+		}
+		break;
+	}
+
+	if (timeptr->tm_mon == 11) {
+		/*
+		 * The last week of the year
+		 * can be in week 1 of next year.
+		 * Sigh.
+		 *
+		 * This can only happen if
+		 *	M   T  W
+		 *	29  30 31
+		 *	30  31
+		 *	31
+		 */
+		int wday, mday;
+
+		wday = timeptr->tm_wday;
+		mday = timeptr->tm_mday;
+		if (   (wday == 1 && (mday >= 29 && mday <= 31))
+		    || (wday == 2 && (mday == 30 || mday == 31))
+		    || (wday == 3 &&  mday == 31))
+			weeknum = 1;
+	}
+
+	return weeknum;
+}
+
+static int
+iso8601wknum_v(const struct vtm *vtm)
+{
+        struct tm tm;
+        vtm2tm_noyear(vtm, &tm);
+        return iso8601wknum(&tm);
+}
+
+#endif
+
+/* weeknumber --- figure how many weeks into the year */
+
+/* With thanks and tip of the hatlo to ado@e... */
+
+#ifndef __STDC__
+static int
+weeknumber(timeptr, firstweekday)
+const struct tm *timeptr;
+int firstweekday;
+#else
+static int
+weeknumber(const struct tm *timeptr, int firstweekday)
+#endif
+{
+	int wday = timeptr->tm_wday;
+	int ret;
+
+	if (firstweekday == 1) {
+		if (wday == 0)	/* sunday */
+			wday = 6;
+		else
+			wday--;
+	}
+	ret = ((timeptr->tm_yday + 7 - wday) / 7);
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+static int
+weeknumber_v(const struct vtm *vtm, int firstweekday)
+{
+        struct tm tm;
+        vtm2tm_noyear(vtm, &tm);
+        return weeknumber(&tm, firstweekday);
+}
+
+#if 0
+/* ADR --- I'm loathe to mess with ado's code ... */
+
+Date:         Wed, 24 Apr 91 20:54:08 MDT
+From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
+To: arnold@a...
+
+Hi Arnold,
+in a process of fixing of strftime() in libraries on Atari ST I grabbed
+some pieces of code from your own strftime.  When doing that it came
+to mind that your weeknumber() function compiles a little bit nicer
+in the following form:
+/*
+ * firstweekday is 0 if starting in Sunday, non-zero if in Monday
+ */
+{
+    return (timeptr->tm_yday - timeptr->tm_wday +
+	    (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
+}
+How nicer it depends on a compiler, of course, but always a tiny bit.
+
+   Cheers,
+   Michal
+   ntomczak@v...
+#endif
+
+#ifdef	TEST_STRFTIME
+
+/*
+ * NAME:
+ *	tst
+ *
+ * SYNOPSIS:
+ *	tst
+ *
+ * DESCRIPTION:
+ *	"tst" is a test driver for the function "strftime".
+ *
+ * OPTIONS:
+ *	None.
+ *
+ * AUTHOR:
+ *	Karl Vogel
+ *	Control Data Systems, Inc.
+ *	vogelke@c...
+ *
+ * BUGS:
+ *	None noticed yet.
+ *
+ * COMPILE:
+ *	cc -o tst -DTEST_STRFTIME strftime.c
+ */
+
+/* ADR: I reformatted this to my liking, and deleted some unneeded code. */
+
+#ifndef NULL
+#include	<stdio.h>
+#endif
+#include	<sys/time.h>
+#include	<string.h>
+
+#define		MAXTIME		132
+
+/*
+ * Array of time formats.
+ */
+
+static char *array[] =
+{
+	"(%%A)      full weekday name, var length (Sunday..Saturday)  %A",
+	"(%%B)       full month name, var length (January..December)  %B",
+	"(%%C)                                               Century  %C",
+	"(%%D)                                       date (%%m/%%d/%%y)  %D",
+	"(%%E)                           Locale extensions (ignored)  %E",
+	"(%%H)                          hour (24-hour clock, 00..23)  %H",
+	"(%%I)                          hour (12-hour clock, 01..12)  %I",
+	"(%%M)                                       minute (00..59)  %M",
+	"(%%O)                           Locale extensions (ignored)  %O",
+	"(%%R)                                 time, 24-hour (%%H:%%M)  %R",
+	"(%%S)                                       second (00..60)  %S",
+	"(%%T)                              time, 24-hour (%%H:%%M:%%S)  %T",
+	"(%%U)    week of year, Sunday as first day of week (00..53)  %U",
+	"(%%V)                    week of year according to ISO 8601  %V",
+	"(%%W)    week of year, Monday as first day of week (00..53)  %W",
+	"(%%X)     appropriate locale time representation (%H:%M:%S)  %X",
+	"(%%Y)                           year with century (1970...)  %Y",
+	"(%%Z) timezone (EDT), or blank if timezone not determinable  %Z",
+	"(%%a)          locale's abbreviated weekday name (Sun..Sat)  %a",
+	"(%%b)            locale's abbreviated month name (Jan..Dec)  %b",
+	"(%%c)           full date (Sat Nov  4 12:02:33 1989)%n%t%t%t  %c",
+	"(%%d)                             day of the month (01..31)  %d",
+	"(%%e)               day of the month, blank-padded ( 1..31)  %e",
+	"(%%h)                                should be same as (%%b)  %h",
+	"(%%j)                            day of the year (001..366)  %j",
+	"(%%k)               hour, 24-hour clock, blank pad ( 0..23)  %k",
+	"(%%l)               hour, 12-hour clock, blank pad ( 1..12)  %l",
+	"(%%m)                                        month (01..12)  %m",
+	"(%%p)              locale's AM or PM based on 12-hour clock  %p",
+	"(%%r)                   time, 12-hour (same as %%I:%%M:%%S %%p)  %r",
+	"(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7]   %u",
+	"(%%v)                                VMS date (dd-bbb-YYYY)  %v",
+	"(%%w)                       day of week (0..6, Sunday == 0)  %w",
+	"(%%x)                appropriate locale date representation  %x",
+	"(%%y)                      last two digits of year (00..99)  %y",
+	"(%%z)      timezone offset east of GMT as HHMM (e.g. -0500)  %z",
+	(char *) NULL
+};
+
+/* main routine. */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+	long time();
+
+	char *next;
+	char string[MAXTIME];
+
+	int k;
+	int length;
+
+	struct tm *tm;
+
+	long clock;
+
+	/* Call the function. */
+
+	clock = time((long *) 0);
+	tm = localtime(&clock);
+
+	for (k = 0; next = array[k]; k++) {
+		length = strftime(string, MAXTIME, next, tm);
+		printf("%s\n", string);
+	}
+
+	exit(0);
+}
+#endif	/* TEST_STRFTIME */

Property changes on: ext/date/date_strftime.c
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Index: test/date/test_date_strftime.rb
===================================================================
--- test/date/test_date_strftime.rb	(revision 31134)
+++ test/date/test_date_strftime.rb	(revision 31135)
@@ -47,7 +47,7 @@
     '%t'=>["\t",{}],
     '%u'=>['6',{:cwday=>6}],
     '%V'=>['05',{:cweek=>5}],
-    '%v'=>[' 3-Feb-2001',{:mday=>3,:mon=>2,:year=>2001}],
+    '%v'=>[' 3-FEB-2001',{:mday=>3,:mon=>2,:year=>2001}],
     '%z'=>['+0000',{:zone=>'+0000',:offset=>0}],
     '%+'=>['Sat Feb  3 00:00:00 +00:00 2001',
       {:wday=>6,:mon=>2,:mday=>3,

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]