From 0a2cb5093f7fbba370fd9b6caaf308139220b47b Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 19 Apr 2011 00:28:08 +0000 Subject: $time_local and $time_utc are locale-independent This doesn't apply to people that use strftime()-formats, but that's a minority. --- ext/clogger_ext/clogger.c | 50 +++++++++++++++++++++++++++++++++++++++++++++-- lib/clogger.rb | 4 ++-- lib/clogger/pure.rb | 12 ++++++++++++ test/test_clogger.rb | 45 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 99 insertions(+), 12 deletions(-) diff --git a/ext/clogger_ext/clogger.c b/ext/clogger_ext/clogger.c index 416ab30..eb1605b 100644 --- a/ext/clogger_ext/clogger.c +++ b/ext/clogger_ext/clogger.c @@ -92,6 +92,8 @@ enum clogger_special { CL_SP_pid, CL_SP_request_uri, CL_SP_time_iso8601, + CL_SP_time_local, + CL_SP_time_utc }; struct clogger { @@ -445,7 +447,7 @@ static void append_request_length(struct clogger *c) } } -static long gmtoffset(struct tm *tm) +static long local_gmtoffset(struct tm *tm) { time_t t = time(NULL); @@ -463,7 +465,7 @@ static void append_time_iso8601(struct clogger *c) char buf[sizeof("1970-01-01T00:00:00+00:00")]; struct tm tm; int nr; - long gmtoff = gmtoffset(&tm); + long gmtoff = local_gmtoffset(&tm); nr = snprintf(buf, sizeof(buf), "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", @@ -476,6 +478,44 @@ static void append_time_iso8601(struct clogger *c) rb_str_buf_cat(c->log_buf, buf, sizeof(buf) - 1); } +static const char months[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0" + "Jul\0Aug\0Sep\0Oct\0Nov\0Dec"; + +static void append_time_local(struct clogger *c) +{ + char buf[sizeof("01/Jan/1970:00:00:00 +0000")]; + struct tm tm; + int nr; + long gmtoff = local_gmtoffset(&tm); + + nr = snprintf(buf, sizeof(buf), + "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d", + tm.tm_mday, months + (tm.tm_mon * sizeof("Jan")), + tm.tm_year + 1900, tm.tm_hour, + tm.tm_min, tm.tm_sec, + gmtoff < 0 ? '-' : '+', + abs(gmtoff / 60), abs(gmtoff % 60)); + assert(nr == (sizeof(buf) - 1) && "snprintf fail"); + rb_str_buf_cat(c->log_buf, buf, sizeof(buf) - 1); +} + +static void append_time_utc(struct clogger *c) +{ + char buf[sizeof("01/Jan/1970:00:00:00 +0000")]; + struct tm tm; + int nr; + time_t t = time(NULL); + + gmtime_r(&t, &tm); + nr = snprintf(buf, sizeof(buf), + "%02d/%s/%d:%02d:%02d:%02d +0000", + tm.tm_mday, months + (tm.tm_mon * sizeof("Jan")), + tm.tm_year + 1900, tm.tm_hour, + tm.tm_min, tm.tm_sec); + assert(nr == (sizeof(buf) - 1) && "snprintf fail"); + rb_str_buf_cat(c->log_buf, buf, sizeof(buf) - 1); +} + static void append_time(struct clogger *c, enum clogger_opcode op, VALUE fmt, VALUE buf) { @@ -583,6 +623,12 @@ static void special_var(struct clogger *c, enum clogger_special var) break; case CL_SP_time_iso8601: append_time_iso8601(c); + break; + case CL_SP_time_local: + append_time_local(c); + break; + case CL_SP_time_utc: + append_time_utc(c); } } diff --git a/lib/clogger.rb b/lib/clogger.rb index e368115..be1bdce 100644 --- a/lib/clogger.rb +++ b/lib/clogger.rb @@ -19,8 +19,6 @@ class Clogger # support nginx variables that are less customizable than our own ALIASES = { '$request_time' => '$request_time{3}', - '$time_local' => '$time_local{%d/%b/%Y:%H:%M:%S %z}', - '$time_utc' => '$time_utc{%d/%b/%Y:%H:%M:%S %z}', '$msec' => '$time{3}', '$usec' => '$time{6}', '$http_content_length' => '$content_length', @@ -37,6 +35,8 @@ class Clogger :pid => 6, # getpid() :request_uri => 7, :time_iso8601 => 8, + :time_local => 9, + :time_utc => 10, } private diff --git a/lib/clogger/pure.rb b/lib/clogger/pure.rb index a56b982..63907ae 100644 --- a/lib/clogger/pure.rb +++ b/lib/clogger/pure.rb @@ -137,6 +137,18 @@ private $$.to_s when :time_iso8601 Time.now.iso8601 + when :time_local + t = Time.now + off = t.utc_offset + sign = off < 0 ? '-' : '+' + sprintf("%02d/%s/%d:%02d:%02d:%02d #{sign}%02d%02d", + t.mday, Time::RFC2822_MONTH_NAME[t.mon - 1], + t.year, t.hour, t.min, t.sec, *(off.abs / 60).divmod(60)) + when :time_utc + t = Time.now.utc + sprintf("%02d/%s/%d:%02d:%02d:%02d +0000", + t.mday, Time::RFC2822_MONTH_NAME[t.mon - 1], + t.year, t.hour, t.min, t.sec) else raise "EDOOFUS #{special_nr}" end diff --git a/test/test_clogger.rb b/test/test_clogger.rb index 1a555ba..9440d74 100644 --- a/test/test_clogger.rb +++ b/test/test_clogger.rb @@ -18,6 +18,8 @@ class TestClogger < Test::Unit::TestCase include Clogger::Format def setup + @tz = ENV["TZ"] + @nginx_fmt = "%d/%b/%Y:%H:%M:%S %z" @req = { "REQUEST_METHOD" => "GET", "HTTP_VERSION" => "HTTP/1.0", @@ -30,6 +32,10 @@ class TestClogger < Test::Unit::TestCase } end + def teardown + ENV["TZ"] = @tz + end + def test_init_basic Clogger.new(lambda { |env| [ 0, {}, [] ] }) end @@ -162,13 +168,12 @@ class TestClogger < Test::Unit::TestCase '$env{rack.url_scheme}' \ "\n") } - longest_day = Time.at(26265600).strftime('%d/%b/%Y:%H:%M:%S %z') expect = [ [ Clogger::OP_REQUEST, "REMOTE_ADDR" ], [ Clogger::OP_LITERAL, " - " ], [ Clogger::OP_REQUEST, "REMOTE_USER" ], [ Clogger::OP_LITERAL, " [" ], - [ Clogger::OP_TIME_LOCAL, '%d/%b/%Y:%H:%M:%S %z', longest_day ], + [ Clogger::OP_SPECIAL, Clogger::SPECIAL_VARS[:time_local] ], [ Clogger::OP_LITERAL, "] \"" ], [ Clogger::OP_SPECIAL, Clogger::SPECIAL_VARS[:request] ], [ Clogger::OP_LITERAL, "\" "], @@ -714,7 +719,6 @@ class TestClogger < Test::Unit::TestCase end def test_time_iso8601_pst8pdt - orig = ENV["TZ"] ENV["TZ"] = "PST8PDT" s = [] app = lambda { |env| [200, [], [] ] } @@ -722,12 +726,9 @@ class TestClogger < Test::Unit::TestCase status, headers, body = cl.call(@req) t = Time.parse(s[0]) assert_equal t.iso8601, s[0].strip - ensure - ENV["TZ"] = orig end def test_time_iso8601_utc - orig = ENV["TZ"] ENV["TZ"] = "UTC" s = [] app = lambda { |env| [200, [], [] ] } @@ -735,8 +736,36 @@ class TestClogger < Test::Unit::TestCase status, headers, body = cl.call(@req) t = Time.parse(s[0]) assert_equal t.iso8601, s[0].strip - ensure - ENV["TZ"] = orig + end + + def test_time_local + s = [] + app = lambda { |env| [200, [], [] ] } + cl = Clogger.new(app, :logger => s, :format => "$time_local") + status, headers, body = cl.call(@req) + t = DateTime.strptime(s[0].strip, @nginx_fmt) + assert_equal t.strftime(@nginx_fmt), s[0].strip + end + + def test_time_local_pst8pdt + orig = ENV["TZ"] + ENV["TZ"] = "PST8PDT" + s = [] + app = lambda { |env| [200, [], [] ] } + cl = Clogger.new(app, :logger => s, :format => "$time_local") + status, headers, body = cl.call(@req) + t = DateTime.strptime(s[0].strip, @nginx_fmt) + assert_equal t.strftime(@nginx_fmt), s[0].strip + end + + def test_time_local_utc + ENV["TZ"] = "UTC" + s = [] + app = lambda { |env| [200, [], [] ] } + cl = Clogger.new(app, :logger => s, :format => "$time_local") + status, headers, body = cl.call(@req) + t = DateTime.strptime(s[0].strip, @nginx_fmt) + assert_equal t.strftime(@nginx_fmt), s[0].strip end def test_method_missing -- cgit v1.2.3-24-ge0c7