Permalink
Browse files

COUCHDB-627 - Support all timezones

Some timezones are incorrectly handled by OTP's calendar module. The
ironic thing is that we only ever need the time in GMT (for HTTP
response headers and the log file).

This patch duplicates httpd_util:rfc1123_date/0 and /1 but uses
universal time everywhere, avoiding the broken conversion code.

Also relates to COUCHDB-1513, a duplicate of COUCHDB-627.
  • Loading branch information...
rnewson committed Aug 15, 2012
1 parent f5341a7 commit b1a049bb7091d3d75092b68bb3645ae909653547
@@ -553,7 +553,7 @@ init_state(Rep) ->
committed_seq = StartSeq,
source_log = SourceLog,
target_log = TargetLog,
- rep_starttime = httpd_util:rfc1123_date(),
+ rep_starttime = couch_util:rfc1123_date(),
src_starttime = get_value(<<"instance_start_time">>, SourceInfo),
tgt_starttime = get_value(<<"instance_start_time">>, TargetInfo),
session_id = couch_uuids:random(),
@@ -706,7 +706,7 @@ do_checkpoint(State) ->
?LOG_INFO("recording a checkpoint for `~s` -> `~s` at source update_seq ~p",
[SourceName, TargetName, NewSeq]),
StartTime = ?l2b(ReplicationStartTime),
- EndTime = ?l2b(httpd_util:rfc1123_date()),
+ EndTime = ?l2b(couch_util:rfc1123_date()),
NewHistoryEntry = {[
{<<"session_id">>, SessionId},
{<<"start_time">>, StartTime},
@@ -44,12 +44,12 @@ handle_welcome_req(Req, _) ->
send_method_not_allowed(Req, "GET,HEAD").
handle_favicon_req(#httpd{method='GET'}=Req, DocumentRoot) ->
- {{Year,Month,Day},Time} = erlang:localtime(),
+ {{Year,Month,Day},Time} = erlang:universatime(),
OneYearFromNow = {{Year+1,Month,Day},Time},
CachingHeaders = [
%favicon should expire a year from now
{"Cache-Control", "public, max-age=31536000"},
- {"Expires", httpd_util:rfc1123_date(OneYearFromNow)}
+ {"Expires", couch_util:rfc1123_date(OneYearFromNow)}
],
couch_httpd:serve_file(Req, "favicon.ico", DocumentRoot, CachingHeaders);
@@ -103,7 +103,7 @@ handle_uuids_req(#httpd{method='GET'}=Req) ->
Etag = couch_httpd:make_etag(UUIDs),
couch_httpd:etag_respond(Req, Etag, fun() ->
CacheBustingHeaders = [
- {"Date", httpd_util:rfc1123_date()},
+ {"Date", couch_util:rfc1123_date()},
{"Cache-Control", "no-cache"},
% Past date, ON PURPOSE!
{"Expires", "Fri, 01 Jan 1990 00:00:00 GMT"},
@@ -159,7 +159,7 @@ log(#state{fd = Fd}, ConsoleMsg, FileMsg) ->
get_log_messages(Pid, Level, Format, Args) ->
ConsoleMsg = unicode:characters_to_binary(io_lib:format(
"[~s] [~p] " ++ Format ++ "~n", [Level, Pid | Args])),
- FileMsg = ["[", httpd_util:rfc1123_date(), "] ", ConsoleMsg],
+ FileMsg = ["[", couch_util:rfc1123_date(), "] ", ConsoleMsg],
{ConsoleMsg, iolist_to_binary(FileMsg)}.
@@ -170,7 +170,7 @@ init([]) ->
{ok, #server{root_dir=RootDir,
dbname_regexp=RegExp,
max_dbs_open=MaxDbsOpen,
- start_time=httpd_util:rfc1123_date()}}.
+ start_time=couch_util:rfc1123_date()}}.
terminate(_Reason, _Srv) ->
lists:foreach(
View
@@ -28,6 +28,7 @@
-export([url_strip_password/1]).
-export([encode_doc_id/1]).
-export([with_db/2]).
+-export([rfc1123_date/0, rfc1123_date/1]).
-include("couch_db.hrl").
@@ -445,3 +446,44 @@ with_db(DbName, Fun) ->
Else ->
throw(Else)
end.
+
+rfc1123_date() ->
+ {{YYYY,MM,DD},{Hour,Min,Sec}} = calendar:universal_time(),
+ DayNumber = calendar:day_of_the_week({YYYY,MM,DD}),
+ lists:flatten(
+ io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
+ [day(DayNumber),DD,month(MM),YYYY,Hour,Min,Sec])).
+
+rfc1123_date(undefined) ->
+ undefined;
+rfc1123_date(UniversalTime) ->
+ {{YYYY,MM,DD},{Hour,Min,Sec}} = UniversalTime,
+ DayNumber = calendar:day_of_the_week({YYYY,MM,DD}),
+ lists:flatten(
+ io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
+ [day(DayNumber),DD,month(MM),YYYY,Hour,Min,Sec])).
+
+%% day
+
+day(1) -> "Mon";
+day(2) -> "Tue";
+day(3) -> "Wed";
+day(4) -> "Thu";
+day(5) -> "Fri";
+day(6) -> "Sat";
+day(7) -> "Sun".
+
+%% month
+
+month(1) -> "Jan";
+month(2) -> "Feb";
+month(3) -> "Mar";
+month(4) -> "Apr";
+month(5) -> "May";
+month(6) -> "Jun";
+month(7) -> "Jul";
+month(8) -> "Aug";
+month(9) -> "Sep";
+month(10) -> "Oct";
+month(11) -> "Nov";
+month(12) -> "Dec".
@@ -49,9 +49,9 @@ cookie(Key, Value, Options) ->
RawAge ->
When = case proplists:get_value(local_time, Options) of
undefined ->
- calendar:local_time();
+ calendar:universal_time();
LocalTime ->
- LocalTime
+ calendar:local_time_to_universal_time_dst(LocalTime)
end,
Age = case RawAge < 0 of
true ->
@@ -115,12 +115,12 @@ quote(V0) ->
orelse erlang:error({cookie_quoting_required, V}),
V.
-add_seconds(Secs, LocalTime) ->
- Greg = calendar:datetime_to_gregorian_seconds(LocalTime),
+add_seconds(Secs, UniversalTime) ->
+ Greg = calendar:datetime_to_gregorian_seconds(UniversalTime),
calendar:gregorian_seconds_to_datetime(Greg + Secs).
-age_to_cookie_date(Age, LocalTime) ->
- httpd_util:rfc1123_date(add_seconds(Age, LocalTime)).
+age_to_cookie_date(Age, UniversalTime) ->
+ couch_util:rfc1123_date(add_seconds(Age, UniversalTime)).
%% @spec parse_cookie(string()) -> [{K::string(), V::string()}]
%% @doc Parse the contents of a Cookie header field, ignoring cookie
@@ -600,9 +600,9 @@ maybe_redirect(RelPath, FullPath, ExtraHeaders) ->
end.
maybe_serve_file(File, ExtraHeaders) ->
- case file:read_file_info(File) of
+ case file:read_file_info(File, [{time, universal}]) of
{ok, FileInfo} ->
- LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime),
+ LastModified = couch_util:rfc1123_date(FileInfo#file_info.mtime),
case get_header_value("if-modified-since") of
LastModified ->
respond({304, ExtraHeaders, ""});
@@ -626,7 +626,7 @@ maybe_serve_file(File, ExtraHeaders) ->
server_headers() ->
[{"Server", "MochiWeb/1.0 (" ++ ?QUIP ++ ")"},
- {"Date", httpd_util:rfc1123_date()}].
+ {"Date", couch_util:rfc1123_date()}].
make_code(X) when is_integer(X) ->
[integer_to_list(X), [" " | httpd_util:reason_phrase(X)]];

0 comments on commit b1a049b

Please sign in to comment.