1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(time_SUITE).
22-compile({nowarn_deprecated_function, {erlang,now,0}}).
23
24%% "Time is on my side." -- The Rolling Stones
25
26%% Tests the BIFs:
27%%	erlang:localtime_to_universaltime/1
28%%	erlang:universaltime_to_localtime/1
29%%	date/0
30%%	time/0
31%%	now/0
32%%
33
34-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
35	 init_per_group/2,end_per_group/2, univ_to_local/1, local_to_univ/1,
36	 bad_univ_to_local/1, bad_local_to_univ/1,
37	 univ_to_seconds/1, seconds_to_univ/1,
38	 consistency/1,
39	 now_unique/1, now_update/1, timestamp/1,
40	 time_warp_modes/1,
41	 monotonic_time_monotonicity/1,
42	 monotonic_time_monotonicity_parallel/1,
43	 time_unit_conversion/1,
44	 signed_time_unit_conversion/1,
45	 erlang_timestamp/1]).
46
47-export([init_per_testcase/2, end_per_testcase/2]).
48
49-export([local_to_univ_utc/1]).
50
51-include_lib("common_test/include/ct.hrl").
52
53-export([linear_time/1]).
54
55%% The following defines the timezone in which the test is run.
56%% It is interpreted as the number of hours to be added to UTC
57%% to obtain the local time.  The number will be positive east
58%% of Greenwhich, negative west of Greenwhich.
59%%
60%% Allowable range is -12 through 11.
61
62-define(timezone, 1).
63
64%% Similarly to timezone, but the difference when Daylight Saving Time
65%% is in use.  [Same range.]
66
67-define(dst_timezone, 2).
68
69init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
70    [{testcase, Func}|Config].
71
72end_per_testcase(_Func, _Config) ->
73    ok.
74
75suite() -> [{ct_hooks,[ts_install_cth]}].
76
77all() ->
78    [univ_to_local, local_to_univ, local_to_univ_utc,
79     bad_univ_to_local, bad_local_to_univ,
80     univ_to_seconds, seconds_to_univ,
81     consistency,
82     {group, now}, timestamp,
83     time_warp_modes,
84     monotonic_time_monotonicity,
85     monotonic_time_monotonicity_parallel,
86     time_unit_conversion,
87     signed_time_unit_conversion,
88     erlang_timestamp].
89
90groups() ->
91    [{now, [], [now_unique, now_update]}].
92
93init_per_suite(Config) ->
94    Config.
95
96end_per_suite(_Config) ->
97    ok.
98
99init_per_group(_GroupName, Config) ->
100    Config.
101
102end_per_group(_GroupName, Config) ->
103    Config.
104
105
106%% Test that DST = true on timezones without DST is ignored
107local_to_univ_utc(Config) when is_list(Config) ->
108    case os:type() of
109	{unix,_} ->
110	    %% TZ variable has a meaning
111	    {ok, Node} =
112		test_server:start_node(local_univ_utc,peer,
113				       [{args, "-env TZ UTC"}]),
114	    {{2008,8,1},{0,0,0}} =
115		rpc:call(Node,
116			 erlang,localtime_to_universaltime,
117			 [{{2008, 8, 1}, {0, 0, 0}},
118			  false]),
119	    {{2008,8,1},{0,0,0}} =
120		rpc:call(Node,
121			 erlang,localtime_to_universaltime,
122			 [{{2008, 8, 1}, {0, 0, 0}},
123			  true]),
124	    [{{2008,8,1},{0,0,0}}] =
125		rpc:call(Node,
126			 calendar,local_time_to_universal_time_dst,
127			 [{{2008, 8, 1}, {0, 0, 0}}]),
128	    test_server:stop_node(Node),
129	    ok;
130	_ ->
131	    {skip,"Only valid on Unix"}
132    end.
133
134
135%% Tests conversion from universal to local time.
136
137univ_to_local(Config) when is_list(Config) ->
138    test_univ_to_local(test_data()).
139
140test_univ_to_local([{Utc, Local}|Rest]) ->
141    io:format("Testing ~p => ~p~n", [Local, Utc]),
142    Local = erlang:universaltime_to_localtime(Utc),
143    test_univ_to_local(Rest);
144test_univ_to_local([]) ->
145    ok.
146
147%% Tests conversion from local to universal time.
148
149local_to_univ(Config) when is_list(Config) ->
150    test_local_to_univ(test_data()).
151
152test_local_to_univ([{Utc, Local}|Rest]) ->
153    io:format("Testing ~p => ~p~n", [Utc, Local]),
154    Utc = erlang:localtime_to_universaltime(Local),
155    test_local_to_univ(Rest);
156test_local_to_univ([]) ->
157    ok.
158
159%% Test bad arguments to erlang:universaltime_to_localtime; should
160%% generate a badarg.
161
162bad_univ_to_local(Config) when is_list(Config) ->
163    bad_test_univ_to_local(bad_dates()).
164
165bad_test_univ_to_local([Utc|Rest]) ->
166    io:format("Testing ~p~n", [Utc]),
167    case catch erlang:universaltime_to_localtime(Utc) of
168	      {'EXIT', {badarg, _}} -> bad_test_univ_to_local(Rest)
169	  end;
170bad_test_univ_to_local([]) ->
171    ok.
172
173%% Test bad arguments to erlang:localtime_to_universaltime/1; should
174%% generate a badarg.
175
176bad_local_to_univ(Config) when is_list(Config) ->
177    bad_test_local_to_univ(bad_dates()).
178
179bad_test_local_to_univ([Local|Rest]) ->
180    io:format("Testing ~p~n", [Local]),
181    case catch erlang:localtime_to_universaltime(Local) of
182	      {'EXIT', {badarg, _}} -> bad_test_local_to_univ(Rest)
183	  end;
184bad_test_local_to_univ([]) ->
185    ok.
186
187
188%% Test universaltime to seconds conversions
189univ_to_seconds(Config) when is_list(Config) ->
190    test_univ_to_seconds(ok_utc_seconds()).
191
192test_univ_to_seconds([{Datetime, Seconds}|DSs]) ->
193    io:format("universaltime = ~p -> seconds = ~p", [Datetime, Seconds]),
194    Seconds = erlang:universaltime_to_posixtime(Datetime),
195    test_univ_to_seconds(DSs);
196test_univ_to_seconds([]) ->
197    ok.
198
199%% Test seconds to universaltime conversions
200seconds_to_univ(Config) when is_list(Config) ->
201    test_seconds_to_univ(ok_utc_seconds()).
202
203test_seconds_to_univ([{Datetime, Seconds}|DSs]) ->
204    io:format("universaltime = ~p <- seconds = ~p", [Datetime, Seconds]),
205    Datetime = erlang:posixtime_to_universaltime(Seconds),
206    test_seconds_to_univ(DSs);
207test_seconds_to_univ([]) ->
208    ok.
209
210
211%% Test that the the different time functions return
212%% consistent results.
213consistency(_Config) ->
214    %% Test that:
215    %% 	 * date() & time() gives the same time as erlang:localtime()
216    %%
217    %%   * the difference between erlang:universaltime() and
218    %%     erlang:localtime() is reasonable (with assuming any
219    %%     particular timezone)
220
221    ok = compare_date_time_and_localtime(16),
222    compare_local_and_universal(16).
223
224compare_date_time_and_localtime(Times) when Times > 0 ->
225    {Year, Mon, Day} = date(),
226    {Hour, Min, Sec} = time(),
227    case erlang:localtime() of
228	{{Year, Mon, Day}, {Hour, Min, Sec}} -> ok;
229	_ -> compare_date_time_and_localtime(Times-1)
230    end;
231compare_date_time_and_localtime(0) ->
232    error.
233
234compare_local_and_universal(Times) when Times > 0 ->
235    Utc = erlang:universaltime(),
236    Local = erlang:localtime(),
237    io:format("local = ~p, utc = ~p", [Local,Utc]),
238
239    AcceptableDiff = 14*3600,
240    case linear_time(Utc) - linear_time(Local) of
241	Diff when abs(Diff) < AcceptableDiff ->
242	    ok;
243	Diff ->
244	    io:format("More than ~p seconds difference betwen "
245		      "local and universal time", [Diff]),
246	    ct:fail(huge_diff)
247    end.
248
249%% This function converts a date and time to a linear time.
250%% Two linear times can be subtracted to give their difference
251%% in seconds.
252%%
253%% XXX Limitations: Simplified leap year calc will fail for 2100 :-)
254
255linear_time({{Year, Mon, Day}, {Hour, Min, Sec}}) ->
256    86400*(year_to_days(Year) + month_to_days(Year,Mon) + (Day-1)) +
257	3600*Hour + 60*Min + Sec.
258
259year_to_days(Year) ->
260    Year * 365 + (Year-1) div 4.
261
262month_to_days(Year, Mon) ->
263    DoM = [31,days_in_february(Year),31,30,31,30,31,31,30,31,30,31],
264    {PastMonths,_} = lists:split(Mon-1, DoM),
265    lists:sum(PastMonths).
266
267days_in_february(Year) ->
268    case (Year rem 4) of
269	0 -> 29;
270	_ -> 28
271    end.
272
273%% Test (the bif) os:timestamp/0, which is something quite like, but not
274%% similar to erlang:now...
275
276%% Test that os:timestamp works.
277timestamp(Config) when is_list(Config) ->
278    try
279	repeating_timestamp_check(100000)
280    catch
281	throw : {fail, Failure} ->
282	    %%
283	    %% Our time warping test machines currently warps
284	    %% time every 6:th second. If we get a warp during
285	    %% 10 seconds, assume this is a time warping test
286	    %% and ignore the failure.
287	    %%
288	    case had_time_warp(10) of
289		true ->
290		    {skip, "Seems to be time warp test run..."};
291		false ->
292		    ct:fail(Failure)
293	    end
294    end.
295
296os_system_time_offset() ->
297    erlang:convert_time_unit(os:system_time() - erlang:monotonic_time(),
298			     native, microsecond).
299
300had_time_warp(Secs) ->
301    had_time_warp(os_system_time_offset(), Secs).
302
303had_time_warp(_OrigOffs, 0) ->
304    false;
305had_time_warp(OrigOffs, N) ->
306    receive after 1000 -> ok end,
307    case OrigOffs - os_system_time_offset() of
308	Diff when Diff > 500000; Diff < -500000 ->
309	    true;
310	_Diff ->
311	    had_time_warp(OrigOffs, N-1)
312    end.
313
314repeating_timestamp_check(0) ->
315    ok;
316repeating_timestamp_check(N) ->
317    {A,B,C} = TS = os:timestamp(),
318    if
319	is_integer(A),
320	is_integer(B),
321	is_integer(C),
322	B < 1000000,
323	C < 1000000 ->
324	    ok;
325	true ->
326	    ct:fail("Strange return from os:timestamp/0 ~w~n",[TS])
327    end,
328    %% I assume the now and timestamp should not differ more than 1 hour,
329    %% which is safe assuming the system has not had a large time-warp
330    %% during the testrun...
331    Secs = A*1000000+B+round(C/1000000),
332    {NA,NB,NC} = erlang:now(),
333    NSecs = NA*1000000+NB+round(NC/1000000),
334    case Secs - NSecs of
335	TooLarge when TooLarge > 3600 ->
336	    throw({fail,
337	       lists:flatten(
338		io_lib:format("os:timestamp/0 is ~w s more than erlang:now/0",
339			     [TooLarge]))});
340	TooSmall when TooSmall < -3600 ->
341	    throw({fail,
342	      lists:flatten(
343		io_lib:format("os:timestamp/0 is ~w s less than erlang:now/0",
344			     [-TooSmall]))});
345	_ ->
346	    ok
347    end,
348    repeating_timestamp_check(N-1).
349
350
351%% Test now/0.
352
353
354%% Tests that successive calls to now/0 returns different values.
355%% Also returns a comment string with the median difference between
356%% times (in microseconds).
357
358now_unique(Config) when is_list(Config) ->
359    now_unique(1000, now(), []),
360    fast_now_unique(100000, now()).
361
362now_unique(N, Previous, Result) when N > 0 ->
363    case now() of
364	      Previous ->
365		  ct:fail("now/0 returned the same value twice");
366	      New ->
367		  now_unique(N-1, New, [New|Result])
368	  end;
369now_unique(0, _, [Then|Rest]) ->
370    now_calc_increment(Rest, microsecs(Then), []).
371
372now_calc_increment([Then|Rest], Previous, _Result) ->
373    This = microsecs(Then),
374    now_calc_increment(Rest, This, [Previous-This]);
375now_calc_increment([], _, Differences) ->
376    {comment, "Median increment: " ++ integer_to_list(median(Differences))}.
377
378fast_now_unique(0, _) -> ok;
379fast_now_unique(N, Then) ->
380    case now() of
381	Then ->
382	    ct:fail("now/0 returned the same value twice");
383	Now ->
384	    fast_now_unique(N-1, Now)
385    end.
386
387median(Unsorted_List) ->
388    Length = length(Unsorted_List),
389    List = lists:sort(Unsorted_List),
390    case Length rem 2 of
391	0 ->					% Even length.
392	    [A, B] = lists:nthtail((Length div 2)-1, List),
393	    (A+B)/2;
394	1 ->					% Odd list length.
395	    lists:nth((Length div 2)+1, List)
396    end.
397
398microsecs({Mega_Secs, Secs, Microsecs}) ->
399    (Mega_Secs*1000000+Secs)*1000000+Microsecs.
400
401%% Test that the time differences returned by two calls to
402%% now/0 one second apart is comparable to the difference of two
403%% calls to erlang:localtime().
404
405now_update(Config) when is_list(Config) ->
406    case test_server:is_debug() of
407	false -> now_update1(10);
408	true -> {skip,"Unreliable in DEBUG build"}
409    end.
410
411
412now_update1(N) when N > 0 ->
413    T1_linear = linear_time(erlang:localtime()),
414    T1_now = microsecs(now()),
415
416    receive after 1008 -> ok end,
417
418    T2_linear = linear_time(erlang:localtime()),
419    T2_now = microsecs(now()),
420
421    Linear_Diff = (T2_linear-T1_linear)*1000000,
422    Now_Diff = T2_now-T1_now,
423    io:format("Localtime diff = ~p; now() diff = ~p", [Linear_Diff, Now_Diff]),
424    case abs(Linear_Diff - Now_Diff) of
425	      Abs_Delta when Abs_Delta =< 40000 -> ok;
426	      _ -> now_update1(N-1)
427	  end;
428now_update1(0) ->
429    ct:fail("now_update zero").
430
431time_warp_modes(Config) when is_list(Config) ->
432    %% All time warp modes always supported in
433    %% combination with no time correction...
434    check_time_warp_mode(Config, false, no_time_warp),
435    check_time_warp_mode(Config, false, single_time_warp),
436    check_time_warp_mode(Config, false, multi_time_warp),
437
438    erts_debug:set_internal_state(available_internal_state, true),
439    try
440	case erts_debug:get_internal_state({check_time_config,
441					    true, no_time_warp}) of
442	    false -> ok;
443	    true -> check_time_warp_mode(Config, true, no_time_warp)
444	end,
445	case erts_debug:get_internal_state({check_time_config,
446					    true, single_time_warp}) of
447	    false -> ok;
448	    true -> check_time_warp_mode(Config, true, single_time_warp)
449	end,
450	case erts_debug:get_internal_state({check_time_config,
451					    true, multi_time_warp}) of
452	    false -> ok;
453	    true -> check_time_warp_mode(Config, true, multi_time_warp)
454	end
455    after
456	erts_debug:set_internal_state(available_internal_state, false)
457    end.
458
459check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) ->
460    io:format("~n~n~n***** Testing TimeCorrection=~p TimeWarpMode=~p *****~n",
461	      [TimeCorrection, TimeWarpMode]),
462    Mon = erlang:monitor(time_offset, clock_service),
463    _ = erlang:time_offset(),
464    Start = erlang:monotonic_time(1000),
465    MonotonicityTimeout = 2000,
466    {ok, Node} = start_node(Config,
467			    "+c " ++ atom_to_list(TimeCorrection)
468			    ++ " +C " ++ atom_to_list(TimeWarpMode)),
469    StartTime = rpc:call(Node, erlang, system_info, [start_time]),
470    Me = self(),
471    MonotincityTestStarted = make_ref(),
472    MonotincityTestDone = make_ref(),
473    spawn_link(Node,
474	       fun () ->
475		       Me ! MonotincityTestStarted,
476		       cmp_times(erlang:start_timer(MonotonicityTimeout,
477						    self(),
478						    timeout),
479				 erlang:monotonic_time()),
480		       Me ! MonotincityTestDone
481	       end),
482    receive MonotincityTestStarted -> ok end,
483    check_time_offset(Node, TimeWarpMode),
484    TimeWarpMode = rpc:call(Node, erlang, system_info, [time_warp_mode]),
485    TimeCorrection = rpc:call(Node, erlang, system_info, [time_correction]),
486    receive MonotincityTestDone -> ok end,
487    MonotonicTime = rpc:call(Node, erlang, monotonic_time, []),
488    MonotonicTimeUnit = rpc:call(Node,
489				       erlang,
490				       convert_time_unit,
491				       [1, second, native]),
492    UpMilliSeconds = erlang:convert_time_unit(MonotonicTime - StartTime,
493					      MonotonicTimeUnit,
494					      millisecond),
495    io:format("UpMilliSeconds=~p~n", [UpMilliSeconds]),
496    End = erlang:monotonic_time(millisecond),
497    stop_node(Node),
498    try
499	true = (UpMilliSeconds > (98*MonotonicityTimeout) div 100),
500	true = (UpMilliSeconds < (102*(End-Start)) div 100)
501    catch
502	error:_ ->
503	    io:format("Uptime inconsistency", []),
504	    case {TimeCorrection, erlang:system_info(time_correction)} of
505		{true, true} ->
506		    ct:fail(uptime_inconsistency);
507		{true, false} ->
508		    _ = erlang:time_offset(),
509		    receive
510			{'CHANGE', Mon, time_offset, clock_service, _} ->
511			    ignore
512		    after 1000 ->
513			    ct:fail(uptime_inconsistency)
514		    end;
515		_ ->
516		    ignore
517	    end
518    end,
519    erlang:demonitor(Mon, [flush]),
520    ok.
521
522check_time_offset(Node, no_time_warp) ->
523    final = rpc:call(Node, erlang, system_info, [time_offset]),
524    final = rpc:call(Node, erlang, system_flag, [time_offset, finalize]),
525    final = rpc:call(Node, erlang, system_info, [time_offset]);
526check_time_offset(Node, single_time_warp) ->
527    preliminary = rpc:call(Node, erlang, system_info, [time_offset]),
528    preliminary = rpc:call(Node, erlang, system_flag, [time_offset, finalize]),
529    final = rpc:call(Node, erlang, system_info, [time_offset]),
530    final = rpc:call(Node, erlang, system_flag, [time_offset, finalize]);
531check_time_offset(Node, multi_time_warp) ->
532    volatile = rpc:call(Node, erlang, system_info, [time_offset]),
533    volatile = rpc:call(Node, erlang, system_flag, [time_offset, finalize]),
534    volatile = rpc:call(Node, erlang, system_info, [time_offset]).
535
536monotonic_time_monotonicity(Config) when is_list(Config) ->
537    Done = erlang:start_timer(10000,self(),timeout),
538    cmp_times(Done, erlang:monotonic_time()).
539
540cmp_times(Done, X0) ->
541    X1 = erlang:monotonic_time(),
542    X2 = erlang:monotonic_time(),
543    X3 = erlang:monotonic_time(),
544    X4 = erlang:monotonic_time(),
545    X5 = erlang:monotonic_time(),
546    true = (X0 =< X1),
547    true = (X1 =< X2),
548    true = (X2 =< X3),
549    true = (X3 =< X4),
550    true = (X4 =< X5),
551    receive
552	{timeout, Done, timeout} ->
553	    ok
554    after 0 ->
555	    cmp_times(Done, X5)
556    end.
557
558-define(NR_OF_MONOTONIC_CALLS, 100000).
559
560monotonic_time_monotonicity_parallel(Config) when is_list(Config) ->
561    Me = self(),
562    Result = make_ref(),
563    Go = make_ref(),
564    UpAndRunning = make_ref(),
565    NoOnlnScheds = erlang:system_info(schedulers_online),
566    OffsetUI = erlang:unique_integer([monotonic]),
567    OffsetMT = erlang:monotonic_time(),
568    MinHSz = ?NR_OF_MONOTONIC_CALLS*(2
569				     + 3
570				     + erts_debug:flat_size(OffsetUI)
571				     + erts_debug:flat_size(OffsetMT)),
572    Ps = lists:map(
573	   fun (Sched) ->
574		   spawn_opt(
575		     fun () ->
576			     Me ! {self(), UpAndRunning},
577			     receive Go -> ok end,
578			     Res = fetch_monotonic(?NR_OF_MONOTONIC_CALLS, []),
579			     Me ! {self(), Result, Sched, Res}
580		     end,
581		     [{scheduler, Sched},
582		      {priority, max},
583		      {min_heap_size, MinHSz}])
584	   end,
585	   lists:seq(1, NoOnlnScheds)),
586    lists:foreach(fun (P) -> receive {P, UpAndRunning} -> ok end end, Ps),
587    lists:foreach(fun (P) -> P ! Go end, Ps),
588    TMs = recv_monotonics(Result, OffsetMT, OffsetUI, NoOnlnScheds, []),
589    true = check_monotonic_result(TMs, OffsetMT, OffsetUI, true).
590
591check_monotonic_result([{_Sched, _PrevUI, _MT, _PostUI}],
592		       _OffsetMT, _OffsetUI, Res) ->
593    Res;
594check_monotonic_result([{_ASched, _APrevUI, AMT, APostUI} = A,
595			{_BSched, BPrevUI, BMT, _BPostUI} = B | _] = L,
596		       OffsetMT, OffsetUI, Res) ->
597    NewRes = case (AMT =< BMT) orelse (BPrevUI < APostUI) of
598		 true ->
599		     Res;
600		 false ->
601		     io:format("INCONSISTENCY: ~p ~p~n", [A, B]),
602		     false
603	     end,
604    check_monotonic_result(tl(L), OffsetMT, OffsetUI, NewRes).
605
606recv_monotonics(_Result, _OffsetMT, _OffsetUI, 0, Acc) ->
607    lists:keysort(2, Acc);
608recv_monotonics(Result, OffsetMT, OffsetUI, N, Acc) ->
609    receive
610	{_, Result, Sched, Res} ->
611	    CRes = convert_monotonic(Sched, OffsetMT, OffsetUI, Res, []),
612	    recv_monotonics(Result, OffsetMT, OffsetUI, N-1, CRes ++ Acc)
613    end.
614
615convert_monotonic(_Sched, _OffsetMT, _OffsetUI, [{_MT, _UI}], Acc) ->
616    Acc;
617convert_monotonic(Sched, OffsetMT, OffsetUI,
618		  [{MT, UI}, {_PrevMT, PrevUI} | _] = L, Acc) ->
619    convert_monotonic(Sched, OffsetMT, OffsetUI, tl(L),
620		      [{Sched, PrevUI-OffsetUI, MT-OffsetMT, UI-OffsetUI}
621		       | Acc]).
622
623fetch_monotonic(0, Acc) ->
624    Acc;
625fetch_monotonic(N, Acc) ->
626    MT = erlang:monotonic_time(),
627    UI = erlang:unique_integer([monotonic]),
628    fetch_monotonic(N-1, [{MT, UI} | Acc]).
629
630-define(CHK_RES_CONVS_TIMEOUT, 400).
631
632time_unit_conversion(Config) when is_list(Config) ->
633    Mon = erlang:monitor(time_offset, clock_service),
634    start_check_res_convs(Mon, 1000000000000),
635    start_check_res_convs(Mon, 2333333333333),
636    start_check_res_convs(Mon, 5732678356789),
637    erlang:demonitor(Mon, [flush]).
638
639start_check_res_convs(Mon, Res) ->
640    io:format("Checking ~p time_unit~n", [Res]),
641    check_res_convs(Mon,
642		    erlang:start_timer(?CHK_RES_CONVS_TIMEOUT,
643				       self(),
644				       timeout),
645		    Res).
646
647
648check_res_convs(Mon, Done, Res) ->
649    receive
650	{timeout, Done, timeout} ->
651	    case Res div 10 of
652		0 ->
653		    ok;
654		NewRes ->
655		    start_check_res_convs(Mon, NewRes)
656	    end
657    after 0 ->
658	    do_check_res_convs(Mon, Done, Res)
659    end.
660
661do_check_res_convs(Mon, Done, Res) ->
662    TStart = erlang:monotonic_time(),
663    T = erlang:monotonic_time(Res),
664    TEnd = erlang:monotonic_time(),
665    TMin = erlang:convert_time_unit(TStart, native, Res),
666    TMax = erlang:convert_time_unit(TEnd, native, Res),
667    %io:format("~p =< ~p =< ~p~n", [TMin, T, TEnd]),
668    true = (TMin =< T),
669    true = (TMax >= T),
670    check_time_offset_res_conv(Mon, Res),
671    check_res_convs(Mon, Done, Res).
672
673
674check_time_offset_res_conv(Mon, Res) ->
675    TORes = erlang:time_offset(Res),
676    TO = erlang:time_offset(),
677    case erlang:convert_time_unit(TO, native, Res) of
678	TORes ->
679	    ok;
680	TORes2 ->
681	    case check_time_offset_change(Mon, TO, 1000) of
682		{TO, false} ->
683		    ct:fail({time_unit_conversion_inconsistency,
684			     TO, TORes, TORes2});
685		{_NewTO, true} ->
686		    io:format("time_offset changed", []),
687		    check_time_offset_res_conv(Mon, Res)
688	    end
689    end.
690
691signed_time_unit_conversion(Config) when is_list(Config) ->
692    chk_strc(1000000000, 1000000),
693    chk_strc(1000000000, 1000),
694    chk_strc(1000000000, 1),
695    chk_strc(1000000, 1000),
696    chk_strc(1000000, 1),
697    chk_strc(1000, 1),
698    chk_strc(4711, 17),
699    chk_strc(1 bsl 10, 1),
700    chk_strc(1 bsl 16, 10),
701    chk_strc(1 bsl 17, 1 bsl 8),
702    chk_strc((1 bsl 17) + 1, (1 bsl 8) - 1),
703    chk_strc(1 bsl 17, 11),
704    ok.
705
706chk_strc(Res0, Res1) ->
707    case (Res0 /= Res1) andalso (Res0 =< 1000000) andalso (Res1 =< 1000000) of
708	true ->
709	    {FromRes, ToRes} = case Res0 > Res1 of
710				   true -> {Res0, Res1};
711				   false -> {Res1, Res0}
712			       end,
713	    MinFromValuesPerToValue = FromRes div ToRes,
714	    MaxFromValuesPerToValue = ((FromRes-1) div ToRes)+1,
715	    io:format("~p -> ~p [~p, ~p]~n",
716		      [FromRes, ToRes,
717		       MinFromValuesPerToValue, MaxFromValuesPerToValue]),
718	    chk_values_per_value(FromRes, ToRes,
719				 -10*FromRes, 10*FromRes,
720				 MinFromValuesPerToValue,
721				 MaxFromValuesPerToValue,
722				 undefined, MinFromValuesPerToValue);
723	_ ->
724	    ok
725    end,
726    chk_random_values(Res0, Res1),
727    chk_random_values(Res1, Res0),
728    ok.
729
730chk_random_values(FR, TR) ->
731    io:format("rand values ~p -> ~p~n", [FR, TR]),
732    rand:seed(exsplus, {268438039,268440479,268439161}),
733    Values = lists:map(fun (_) -> rand:uniform(1 bsl 65) - (1 bsl 64) end,
734		       lists:seq(1, 100000)),
735    CheckFun = fun (V) ->
736                       CV = erlang:convert_time_unit(V, FR, TR),
737                       case {(FR*CV) div TR =< V,
738                             (FR*(CV+1)) div TR >= V} of
739                           {true, true} ->
740                               ok;
741                           Failure ->
742                               ct:fail({Failure, CV, V, FR, TR})
743                       end
744               end,
745    lists:foreach(CheckFun, Values).
746
747
748chk_values_per_value(_FromRes, _ToRes,
749	 EndValue, EndValue,
750	 MinFromValuesPerToValue, MaxFromValuesPerToValue,
751	 _ToValue, FromValueCount) ->
752%    io:format("~p [~p]~n", [EndValue, FromValueCount]),
753    case ((MinFromValuesPerToValue =< FromValueCount)
754	  andalso (FromValueCount =< MaxFromValuesPerToValue)) of
755	false ->
756	    ct:fail({MinFromValuesPerToValue,
757		     FromValueCount,
758		     MaxFromValuesPerToValue});
759	true ->
760	    ok
761    end;
762chk_values_per_value(FromRes, ToRes, Value, EndValue,
763		     MinFromValuesPerToValue, MaxFromValuesPerToValue,
764		     ToValue, FromValueCount) ->
765    case erlang:convert_time_unit(Value, FromRes, ToRes) of
766        ToValue ->
767            chk_values_per_value(FromRes, ToRes,
768                                 Value+1, EndValue,
769                                 MinFromValuesPerToValue,
770                                 MaxFromValuesPerToValue,
771                                 ToValue, FromValueCount+1);
772        NewToValue ->
773            case ((MinFromValuesPerToValue =< FromValueCount)
774                  andalso (FromValueCount =< MaxFromValuesPerToValue)) of
775                false ->
776                    ct:fail({MinFromValuesPerToValue,
777                             FromValueCount,
778                             MaxFromValuesPerToValue});
779                true ->
780                    %		    io:format("~p -> ~p [~p]~n",
781                    %			      [Value, NewToValue, FromValueCount]),
782                    chk_values_per_value(FromRes, ToRes,
783                                         Value+1, EndValue,
784                                         MinFromValuesPerToValue,
785                                         MaxFromValuesPerToValue,
786                                         NewToValue, 1)
787            end
788    end.
789
790erlang_timestamp(Config) when is_list(Config) ->
791    Mon = erlang:monitor(time_offset, clock_service),
792    {TO, _} = check_time_offset_change(Mon,
793				       erlang:time_offset(),
794				       0),
795    Done = erlang:start_timer(10000,self(),timeout),
796    ok = check_erlang_timestamp(Done, Mon, TO).
797
798check_erlang_timestamp(Done, Mon, TO) ->
799    receive
800        {timeout, Done, timeout} ->
801            erlang:demonitor(Mon, [flush]),
802            ok
803    after 0 ->
804              do_check_erlang_timestamp(Done, Mon, TO)
805    end.
806
807do_check_erlang_timestamp(Done, Mon, TO) ->
808    MinMon = erlang:monotonic_time(),
809    {MegaSec, Sec, MicroSec} = erlang:timestamp(),
810    MaxMon = erlang:monotonic_time(),
811    TsMin = erlang:convert_time_unit(MinMon+TO,
812				     native,
813				     microsecond),
814    TsMax = erlang:convert_time_unit(MaxMon+TO,
815				     native,
816				     microsecond),
817    TsTime = (MegaSec*1000000+Sec)*1000000+MicroSec,
818    case (TsMin =< TsTime) andalso (TsTime =< TsMax) of
819	true ->
820	    NewTO = case erlang:time_offset() of
821			TO ->
822			    TO;
823			_ ->
824			    check_time_offset_change(Mon, TO, 0)
825		    end,
826	    check_erlang_timestamp(Done, Mon, NewTO);
827	false ->
828	    io:format("TsMin=~p TsTime=~p TsMax=~p~n", [TsMin, TsTime, TsMax]),
829	    io:format("Detected inconsistency; "
830		      "checking for time_offset change...", []),
831	    case check_time_offset_change(Mon, TO, 1000) of
832		{TO, false} ->
833		    ct:fail(timestamp_inconsistency);
834		{NewTO, true} ->
835		    io:format("time_offset changed", []),
836		    check_erlang_timestamp(Done, Mon, NewTO)
837	    end
838    end.
839
840check_time_offset_change(Mon, TO, Wait) ->
841    process_changed_time_offset(Mon, TO, false, Wait).
842
843process_changed_time_offset(Mon, TO, Changed, Wait) ->
844    receive
845	{'CHANGE', Mon, time_offset, clock_service, NewTO} ->
846	    process_changed_time_offset(Mon, NewTO, true, Wait)
847    after Wait ->
848	    case erlang:time_offset() of
849		TO ->
850		    {TO, Changed};
851		_OtherTO ->
852		    receive
853			{'CHANGE', Mon, time_offset, clock_service, NewTO} ->
854			    process_changed_time_offset(Mon, NewTO, true, Wait)
855		    end
856	    end
857    end.
858
859
860
861%% Returns the test data: a list of {Utc, Local} tuples.
862
863test_data() ->
864    {TZ,DSTTZ} =
865	case os:type() of
866	    {unix,_} ->
867		case os:cmd("date '+%Z'") of
868		    "SAST"++_ ->
869			{2,2};
870		    _ ->
871			{?timezone,?dst_timezone}
872		end;
873	    _ ->
874		{?timezone,?dst_timezone}
875	end,
876    test_data(nondst_dates(), TZ) ++
877	test_data(dst_dates(), DSTTZ) ++
878	crossover_test_data(crossover_dates(), TZ).
879
880
881%% test_data1() ->
882%%     test_data(nondst_dates(), ?timezone) ++
883%% 	test_data(dst_dates(), ?dst_timezone) ++
884%% 	crossover_test_data(crossover_dates(), ?timezone).
885
886crossover_test_data([{Year, Month, Day}|Rest], TimeZone) when TimeZone > 0 ->
887    Hour = 23,
888    Min = 35,
889    Sec = 55,
890    Utc = {{Year, Month, Day}, {Hour, Min, Sec}},
891    Local = {{Year, Month, Day+1}, {Hour+TimeZone-24, Min, Sec}},
892    [{Utc, Local}|crossover_test_data(Rest, TimeZone)];
893crossover_test_data([{Year, Month, Day}|Rest], TimeZone) when TimeZone < 0 ->
894    Hour = 0,
895    Min = 23,
896    Sec = 12,
897    Utc = {{Year, Month, Day}, {Hour, Min, Sec}},
898    Local = {{Year, Month, Day-1}, {Hour+TimeZone+24, Min, Sec}},
899    [{Utc, Local}|crossover_test_data(Rest, TimeZone)];
900crossover_test_data([], _) ->
901    [].
902
903test_data([Date|Rest], TimeZone) ->
904    Hour = 12,
905    Min = 45,
906    Sec = 7,
907    Utc = {Date, {Hour, Min, Sec}},
908    Local = {Date, {Hour+TimeZone, Min, Sec}},
909    [{Utc, Local}|test_data(Rest, TimeZone)];
910test_data([], _) ->
911    [].
912
913nondst_dates() ->
914    [{1996, 01, 30},
915     {1997, 01, 30},
916     {1998, 01, 30},
917     {1999, 01, 30},
918     {1996, 02, 29},
919     {1997, 02, 28},
920     {1998, 02, 28},
921     {1999, 02, 28},
922     {1996, 03, 2},
923     {1997, 03, 2},
924     {1998, 03, 2},
925     {1999, 03, 2}].
926
927dst_dates() ->
928    [{1996, 06, 1},
929     {1997, 06, 2},
930     {1998, 06, 3},
931     {1999, 06, 4}].
932
933%% exakt utc {date(), time()} which corresponds to the same seconds since 1 jan 1970
934%% negative seconds are ok
935%% generated with date --date='1979-05-28 12:30:35 UTC' +%s
936ok_utc_seconds() -> [
937	{ {{1970, 1, 1},{ 0, 0, 0}},            0 },
938	{ {{1970, 1, 1},{ 0, 0, 1}},            1 },
939	{ {{1969,12,31},{23,59,59}},           -1 },
940	{ {{1920,12,31},{23,59,59}},  -1546300801 },
941	{ {{1600,02,19},{15,14,08}}, -11671807552 },
942	{ {{1979,05,28},{12,30,35}},    296742635 },
943	{ {{1999,12,31},{23,59,59}},    946684799 },
944	{ {{2000, 1, 1},{ 0, 0, 0}},    946684800 },
945	{ {{2000, 1, 1},{ 0, 0, 1}},    946684801 },
946
947	{ {{2038, 1,19},{03,14,07}},   2147483647 }, % Sint32 full - 1
948	{ {{2038, 1,19},{03,14,08}},   2147483648 }, % Sint32 full
949	{ {{2038, 1,19},{03,14,09}},   2147483649 }, % Sint32 full + 1
950
951	{ {{2106, 2, 7},{ 6,28,14}},   4294967294 }, % Uint32 full  0xFFFFFFFF - 1
952	{ {{2106, 2, 7},{ 6,28,15}},   4294967295 }, % Uint32 full  0xFFFFFFFF
953	{ {{2106, 2, 7},{ 6,28,16}},   4294967296 }, % Uint32 full  0xFFFFFFFF + 1
954	{ {{2012,12, 6},{16,28,08}},   1354811288 },
955	{ {{2412,12, 6},{16,28,08}},  13977592088 }
956    ].
957
958
959%% The following dates should not be near the end or beginning of
960%% a month, because they will be used to test when the dates are
961%% different in UTC and local time.
962
963crossover_dates() ->
964    [{1996, 01, 25},
965     {1997, 01, 25},
966     {1998, 01, 25},
967     {1999, 01, 25},
968     {1996, 02, 27},
969     {1997, 02, 27},
970     {1998, 02, 27},
971     {1999, 02, 27}].
972
973bad_dates() ->
974    [{{1900, 7, 1}, {12, 0, 0}},		% Year
975
976     {{1996, 0, 20}, {12, 0, 0}},		% Month
977     {{1996, 13, 20}, {12, 0, 0}},
978
979     {{1996, 1, 0}, {12, 0, 0}},		% Date
980     {{1996, 1, 32}, {12, 0, 0}},
981     {{1996, 2, 30}, {12, 0, 0}},
982     {{1997, 2, 29}, {12, 0, 0}},
983     {{1998, 2, 29}, {12, 0, 0}},
984     {{1999, 2, 29}, {12, 0, 0}},
985     {{1996, 4, 31}, {12, 0, 0}},
986
987     {{1996, 4, 30}, {-1, 0, 0}},		% Hour
988     {{1996, 4, 30}, {25, 0, 0}},
989
990     {{1996, 4, 30}, {12,-1, 0}},		% Minute
991     {{1996, 4, 30}, {12, 60, 0}},
992
993     {{1996, 4, 30}, {12, 0, -1}},		% Sec
994     {{1996, 4, 30}, {12, 0, 60}}].
995
996start_node(Config, Args) ->
997    TestCase = proplists:get_value(testcase, Config),
998    PA = filename:dirname(code:which(?MODULE)),
999    ESTime = erlang:monotonic_time(1) + erlang:time_offset(1),
1000    Unique = erlang:unique_integer([positive]),
1001    Name = list_to_atom(atom_to_list(?MODULE)
1002			++ "-"
1003			++ atom_to_list(TestCase)
1004			++ "-"
1005			++ integer_to_list(ESTime)
1006			++ "-"
1007			++ integer_to_list(Unique)),
1008    test_server:start_node(Name,
1009			   slave,
1010			   [{args, "-pa " ++ PA ++ " " ++ Args}]).
1011
1012stop_node(Node) ->
1013    test_server:stop_node(Node).
1014