Permalink
Browse files

added time format options

  • Loading branch information...
1 parent 0e7afa0 commit 5618854bc0a6ec6ba238473f6fe52b68ce111e86 Peter Ohler committed Jul 9, 2012
Showing with 144 additions and 30 deletions.
  1. +2 −8 README.md
  2. +17 −16 build_test.sh
  3. +54 −1 ext/oj/dump.c
  4. +45 −1 ext/oj/oj.c
  5. +7 −0 ext/oj/oj.h
  6. +1 −1 lib/oj/version.rb
  7. +18 −3 test/tests.rb
View
@@ -32,15 +32,9 @@ A fast JSON parser and Object marshaller as a Ruby gem.
## <a name="release">Release Notes</a>
-### Release 1.2.13
+### Release 1.3.0
- - Fixed double free bug in Oj::Doc that showed up for larger documents.
-
-### Release 1.2.12
-
- - Fixed GC bug in Oj::Doc, the fast parser.
-
- - Serialization of Exceptions in Ruby 1.8.7 now includes message and backtrace.
+ - Added an option to control the time format output when in :compat mode.
## <a name="description">Description</a>
View
@@ -1,5 +1,22 @@
#!/bin/sh
+
+PATH=/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin:$PATH
+echo "\n********************************************************************************"
+echo "Building OS X Ruby\n"
+cd ext/oj
+ruby extconf.rb
+make
+
+echo "\nRunning tests for OS X Ruby"
+cd ../../test
+./tests.rb
+./test_mimic.rb
+./test_fast.rb
+cd ..
+
+echo "\n"
+
for ruby in \
1.8.7-p358\
1.9.2-p290\
@@ -26,19 +43,3 @@ do
echo "\n"
done
-
-PATH=/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin:$PATH
-echo "\n********************************************************************************"
-echo "Building OS X Ruby\n"
-cd ext/oj
-ruby extconf.rb
-make
-
-echo "\nRunning tests for OS X Ruby"
-cd ../../test
-./tests.rb
-./test_mimic.rb
-./test_fast.rb
-cd ..
-
-echo "\n"
View
@@ -82,6 +82,8 @@ static int hash_cb_compat(VALUE key, VALUE value, Out out);
static int hash_cb_object(VALUE key, VALUE value, Out out);
static void dump_hash(VALUE obj, int depth, int mode, Out out);
static void dump_time(VALUE obj, Out out);
+static void dump_ruby_time(VALUE obj, Out out);
+static void dump_xml_time(VALUE obj, Out out);
static void dump_data_comp(VALUE obj, Out out);
static void dump_data_obj(VALUE obj, int depth, Out out);
static void dump_obj_comp(VALUE obj, int depth, Out out);
@@ -945,12 +947,63 @@ dump_time(VALUE obj, Out out) {
*out->cur = '\0';
}
+static void
+dump_ruby_time(VALUE obj, Out out) {
+ VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
+
+ dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
+}
+
+static void
+dump_xml_time(VALUE obj, Out out) {
+ char buf[64];
+ struct tm *tm;
+#if HAS_RB_TIME_TIMESPEC
+ struct timespec ts = rb_time_timespec(obj);
+ time_t sec = ts.tv_sec;
+ long nsec = ts.tv_nsec;
+#else
+ time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
+#if HAS_NANO_TIME
+ long nsec = NUM2LONG(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
+#else
+ long nsec = NUM2LONG(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
+#endif
+#endif
+ int tzhour, tzmin;
+ char tzsign = '+';
+
+ if (out->end - out->cur <= 36) {
+ grow(out, 36);
+ }
+ // 2012-01-05T23:58:07.123456000+09:00
+ tm = localtime(&sec);
+ if (0 > tm->tm_gmtoff) {
+ tzsign = '-';
+ tzhour = (int)(tm->tm_gmtoff / -3600);
+ tzmin = (int)(tm->tm_gmtoff / -60) - (tzhour * 60);
+ } else {
+ tzhour = (int)(tm->tm_gmtoff / 3600);
+ tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
+ }
+ sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
+ tzsign, tzhour, tzmin);
+ dump_cstr(buf, 35, 0, 0, out);
+}
+
static void
dump_data_comp(VALUE obj, Out out) {
VALUE clas = rb_obj_class(obj);
if (rb_cTime == clas) {
- dump_time(obj, out);
+ switch (out->opts->time_format) {
+ case RubyTime: dump_ruby_time(obj, out); break;
+ case XmlTime: dump_xml_time(obj, out); break;
+ case UnixTime:
+ default: dump_time(obj, out); break;
+ }
} else {
VALUE rstr;
View
@@ -29,7 +29,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <pthread.h> // TBD LOCK
+#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
@@ -84,8 +84,12 @@ static VALUE max_stack_sym;
static VALUE mode_sym;
static VALUE null_sym;
static VALUE object_sym;
+static VALUE ruby_sym;
static VALUE strict_sym;
static VALUE symbol_keys_sym;
+static VALUE time_format_sym;
+static VALUE unix_sym;
+static VALUE xmlschema_sym;
static VALUE array_nl_sym;
static VALUE create_additions_sym;
@@ -114,6 +118,7 @@ struct _Options oj_default_options = {
No, // sym_key
No, // ascii_only
ObjectMode, // mode
+ UnixTime, // time_format
json_class, // create_id
65536, // max_stack
0, // dump_opts
@@ -143,6 +148,7 @@ oj_get_odd(VALUE clas) {
* - auto_define: [true|false|nil] automatically define classes if they do not exist
* - symbol_keys: [true|false|nil] use symbols instead of strings for hash keys
* - mode: [:object|:strict|:compat|:null] load and dump modes to use for JSON
+ * - time_format: [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
* - create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
* - max_stack: [Fixnum|nil] maximum json size to allocate on the stack, default is 65536
* @return [Hash] all current option settings.
@@ -164,6 +170,12 @@ get_def_opts(VALUE self) {
case ObjectMode:
default: rb_hash_aset(opts, mode_sym, object_sym); break;
}
+ switch (oj_default_options.time_format) {
+ case XmlTime: rb_hash_aset(opts, time_format_sym, xmlschema_sym); break;
+ case RubyTime: rb_hash_aset(opts, time_format_sym, ruby_sym); break;
+ case UnixTime:
+ default: rb_hash_aset(opts, time_format_sym, unix_sym); break;
+ }
rb_hash_aset(opts, create_id_sym, (0 == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
return opts;
@@ -186,6 +198,10 @@ get_def_opts(VALUE self) {
* and to_json() methods and encodes variables using code internal to
* the Oj gem. The :null mode ignores non-supported Objects and
* replaces them with a null.
+ * @param [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
+ * :unix decimal number denoting the number of seconds since 1/1/1970,
+ * :xmlschema date-time format taken from XML Schema as a String,
+ * :ruby Time.to_s formatted String
* @param [String|nil] :create_id create id for json compatible object encoding
* @param [Fixnum|nil] :max_stack maximum size to allocate on the stack for a JSON String
* @return [nil]
@@ -235,6 +251,19 @@ set_def_opts(VALUE self, VALUE opts) {
rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.\n");
}
+ v = rb_hash_lookup(opts, time_format_sym);
+ if (Qnil == v) {
+ // ignore
+ } else if (unix_sym == v) {
+ oj_default_options.time_format = UnixTime;
+ } else if (xmlschema_sym == v) {
+ oj_default_options.time_format = XmlTime;
+ } else if (ruby_sym == v) {
+ oj_default_options.time_format = RubyTime;
+ } else {
+ rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.\n");
+ }
+
if (Qtrue == rb_funcall(opts, rb_intern("has_key?"), 1, create_id_sym)) {
if (0 != oj_default_options.create_id) {
if (json_class != oj_default_options.create_id) {
@@ -301,6 +330,17 @@ parse_options(VALUE ropts, Options copts) {
rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.\n");
}
}
+ if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
+ if (unix_sym == v) {
+ copts->time_format = UnixTime;
+ } else if (xmlschema_sym == v) {
+ copts->time_format = XmlTime;
+ } else if (ruby_sym == v) {
+ copts->time_format = RubyTime;
+ } else {
+ rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.\n");
+ }
+ }
for (o = ynos; 0 != o->attr; o++) {
if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
if (Qtrue == v) {
@@ -850,8 +890,12 @@ void Init_oj() {
mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
null_sym = ID2SYM(rb_intern("null")); rb_gc_register_address(&null_sym);
object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
+ ruby_sym = ID2SYM(rb_intern("ruby")); rb_gc_register_address(&ruby_sym);
strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
symbol_keys_sym = ID2SYM(rb_intern("symbol_keys")); rb_gc_register_address(&symbol_keys_sym);
+ time_format_sym = ID2SYM(rb_intern("time_format")); rb_gc_register_address(&time_format_sym);
+ unix_sym = ID2SYM(rb_intern("unix")); rb_gc_register_address(&unix_sym);
+ xmlschema_sym = ID2SYM(rb_intern("xmlschema")); rb_gc_register_address(&xmlschema_sym);
oj_slash_string = rb_str_new2("/"); rb_gc_register_address(&oj_slash_string);
View
@@ -80,6 +80,12 @@ typedef enum {
CompatMode = 'c'
} Mode;
+typedef enum {
+ UnixTime = 'u',
+ XmlTime = 'x',
+ RubyTime = 'r'
+} TimeFormat;
+
typedef struct _DumpOpts {
const char *indent;
const char *before_sep;
@@ -100,6 +106,7 @@ typedef struct _Options {
char sym_key; // YesNo
char ascii_only; // YesNo
char mode; // Mode
+ char time_format; // TimeFormat
const char *create_id; // 0 or string
size_t max_stack; // max size to allocate on the stack
DumpOpts dump_opts;
View
@@ -1,5 +1,5 @@
module Oj
# Current version of the module.
- VERSION = '1.2.13'
+ VERSION = '1.3.0'
end
View
@@ -109,6 +109,7 @@ def test0_get_options
:symbol_keys=>false,
:ascii_only=>false,
:mode=>:object,
+ :time_format=>:unix,
:max_stack=>65536,
:create_id=>'json_class'}, opts)
end
@@ -121,6 +122,7 @@ def test0_set_options
:symbol_keys=>false,
:ascii_only=>false,
:mode=>:object,
+ :time_format=>:unix,
:max_stack=>65536,
:create_id=>'json_class'}
o2 = {
@@ -130,6 +132,7 @@ def test0_set_options
:symbol_keys=>true,
:ascii_only=>true,
:mode=>:compat,
+ :time_format=>:ruby,
:max_stack=>4000,
:create_id=>nil}
o3 = { :indent => 4 }
@@ -250,10 +253,22 @@ def test_time_null
json = Oj.dump(t, :mode => :null)
assert_equal('null', json)
end
- def test_time_compat
- t = Time.local(2012, 1, 5, 23, 58, 7)
+ def test_unix_time_compat
+ t = Time.xmlschema("2012-01-05T23:58:07.123456000+09:00")
+ #t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
json = Oj.dump(t, :mode => :compat)
- assert_equal(%{1325775487.000000000}, json)
+ assert_equal(%{1325775487.123456000}, json)
+ end
+ def test_ruby_time_compat
+ t = Time.xmlschema("2012-01-05T23:58:07.123456000+09:00")
+ json = Oj.dump(t, :mode => :compat, :time_format => :ruby)
+ #assert_equal(%{"2012-01-05 23:58:07 +0900"}, json)
+ assert_equal(%{"#{t.to_s}"}, json)
+ end
+ def test_xml_time_compat
+ t = Time.xmlschema("2012-01-05T23:58:07.123456000+09:00")
+ json = Oj.dump(t, :mode => :compat, :time_format => :xmlschema)
+ assert_equal(%{"2012-01-05T23:58:07.123456000+09:00"}, json)
end
def test_time_object
t = Time.now()

0 comments on commit 5618854

Please sign in to comment.