The previous patch I posted on September 18, 2008 had a bug in the new
BIF erlang:now_utc/0 which I added. This patch is a revised one for
R12B4, now also tested on a non-leap-second system (Ubuntu 8.04 LTS
Desktop).
Kenji Rikitake
A patch to correct erlang:universaltime_to_localtime/1
and adding erlang:now_utc/0 new BIF for obtaining UTC
for FreeBSD running leap-second-enabled timezone
by Kenji Rikitake <kenji.rikitake@...>
First version 18-SEP-2008
Revised version 31-OCT-2008
* Changelog
31-OCT-2008: revised the erl_time_sup.c patch for a non-leap-second
system, tested under Ubuntu 8.04 LTS Desktop edition, which did not
handle the variable megasec properly in get_now_utc() of
erlang:now_utc/0.
* Summary
This patch fixes the time calculation problem of
FreeBSD 6.x and 7.x, which has the internal leap-second
correction enabled.
This patch is tested with Erlang/OTP R12B-4 source distribution
on FreeBSD 6.3-RELEASE and 7.0-RELEASE port (lang/erlang).
* Symptom
Without this patch,
erlang:localtime_to_universaltime/1
and
erlang:universaltime_to_localtime/1
are not symmetric and will break
calendar:local_time_to_universal_time_dst_1/1
and
httpd_util:rfc1123_date/1.
Without this patch,
erlang:now/0
returns uncorrected time (of gettimeofday(2))
and represents TAI on leap-second-enabled system.
This will affect computations of
calendar:now_to_datetime/1
calendar:now_to_universal_time/1 (equivalent to calendar:now_to_datetime/1)
calendar:now_to_local_time/1
because the above three functions assume that
the argument (type Now) value is based on non-leap-second value.
This will break calendar:now_to_local_time/1.
* Example of symptom:
(under where local time is GMT + 9 hours)
1> erlang:localtime_to_universaltime({{2008,9,1},{12,0,0}}).
{{2008,9,1},{3,0,0}}
2> erlang:universaltime_to_localtime({{2008,9,1},{3,0,0}}).
{{2008,9,1},{11,59,37}}
(Note that as of September 1, 2008, TAI - UTC = 33 seconds.
UNIX time_t with TAI correction is 10 seconds ahead of UTC.
So the 23-second difference occurs when the leap-second
correction is NOT performed, as in the C function of
univ_to_local() in erts/emulator/beam/erl_time_sup.c)
* Workaround in this patch
This patch changes the operation of
erlang:universaltime_to_localtime/1
so that the "universaltime" is handled properly
with leap-year correction.
(Note: OS time_t is in TAI)
This patch adds a new BIF of
erlang:now_utc/0
which has the same semantics of erlang:now/0
while the return value corresponds to non-leap-second
value (of UTC) in leap-second-enabled system.
Using erlang:now_utc/0 instead of erlang:now/0
for
calendar:now_to_datetime/1
calendar:now_to_universal_time/1 (equivalent to calendar:now_to_datetime/1)
calendar:now_to_local_time/1
will return the correct values based on UTC.
See FreeBSD man time2posix(3), posix2time(3) and
/usr/src/lib/libc/stdtime/localtime.c
for the further details.
This patch will NOT affect a FreeBSD machine
without leap-year correction;
time2posix() and posix2time() will do nothing
in such a situation. (NOT tested though)
* Caveats, TODO and suggestions
There is no portable way to do this among UNIX-derived OSes.
HAVE_POSIX2TIME and HAVE_TIME2POSIX should be set by configure.
Linux, OpenBSD, NetBSD also have time2posix() and posix2time(),
though in other UNIX-derived OSes the availability is unconfirmed.
* How to apply this patch
Apply this at Erlang R12B4 source tree's directory under:
erts/emulator/beam
then recompile the distribution.
* Acknowledgement
Matthew Dempsky for his analysis of time2posix() and
posix2time() availability in various operating systems.
[End of patch document]
--- bif.tab.orig 2008-09-01 21:50:45.000000000 +0900
+++ bif.tab 2008-09-18 11:17:25.000000000 +0900
@@ -724,3 +724,9 @@
#
bif erlang:hash/2
+
+### Kenji Rikitake extension
+
+bif erlang:now_utc/0
+
+### end of extension
--- bif.c.orig 2008-09-02 22:21:30.000000000 +0900
+++ bif.c 2008-09-18 11:17:25.000000000 +0900
@@ -3850,3 +3850,23 @@
}
BIF_RET(ret);
}
+
+/* Kenji Rikitake mods */
+
+/**********************************************************************/
+
+
+/* return a timestamp with time2posix offset correction */
+BIF_RETTYPE now_utc_0(BIF_ALIST_0)
+{
+ Uint megasec, sec, microsec;
+ Eterm* hp;
+
+ get_now_utc(&megasec, &sec, µsec);
+ hp = HAlloc(BIF_P, 4);
+ BIF_RET(TUPLE3(hp, make_small(megasec), make_small(sec),
+ make_small(microsec)));
+}
+
+/**********************************************************************/
+
--- erl_time_sup.c.orig 2008-04-07 22:57:50.000000000 +0900
+++ erl_time_sup.c 2008-10-31 08:17:00.000000000 +0900
@@ -71,6 +71,12 @@
**
*/
+/* FreeBSD internal leap year correction function */
+/* those macros should be automatically configured */
+/* define this for FreeBSD 6.x and 7.x */
+#define HAVE_POSIX2TIME
+#define HAVE_TIME2POSIX
+
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
@@ -686,6 +692,18 @@
the_clock = *second + 60 * (*minute + 60 * (*hour + 24 *
gregday(*year, *month, *day)));
+#ifdef HAVE_POSIX2TIME
+ /*
+ * leap-second correction performed
+ * if system is configured so;
+ * do nothing if not
+ * See FreeBSD 6.x and 7.x
+ * /usr/src/lib/libc/stdtime/localtime.c
+ * for the details
+ */
+ the_clock = posix2time(the_clock);
+#endif
+
#ifdef HAVE_LOCALTIME_R
localtime_r(&the_clock, (tm = &tmbuf));
#else
@@ -866,3 +884,56 @@
erts_smp_mtx_unlock(&erts_timeofday_mtx);
}
+
+/* beginning of extension by Kenji Rikitake */
+
+/* get a timestamp with time2posix correction */
+/* (subtract TAI-UTC offset from the get_now() value) */
+void
+get_now_utc(Uint* megasec, Uint* sec, Uint* microsec)
+{
+ SysTimeval now;
+ time_t now_utc_tvsec;
+
+ erts_smp_mtx_lock(&erts_timeofday_mtx);
+
+ get_tolerant_timeofday(&now);
+ do_erts_deliver_time(&now);
+
+ /* Make sure time is later than last */
+ if (then.tv_sec > now.tv_sec ||
+ (then.tv_sec == now.tv_sec && then.tv_usec >= now.tv_usec)) {
+ now = then;
+ now.tv_usec++;
+ }
+ /* Check for carry from above + general reasonability */
+ if (now.tv_usec >= 1000000) {
+ now.tv_usec = 0;
+ now.tv_sec++;
+ }
+ then = now;
+
+ erts_smp_mtx_unlock(&erts_timeofday_mtx);
+
+#ifdef HAVE_TIME2POSIX
+ /* see gettimeofday(2) for the type difference issue */
+ /*
+ * leap-second correction performed
+ * if system is configured so;
+ * do nothing if not
+ * See FreeBSD 6.x and 7.x
+ * /usr/src/lib/libc/stdtime/localtime.c
+ * for the details
+ */
+ now_utc_tvsec = time2posix((time_t) now.tv_sec);
+#else /* HAVE_TIME2POSIX */
+ /* copy the original time value */
+ now_utc_tvsec = now.tv_sec;
+#endif /* HAVE_TIME2POSIX */
+
+ *megasec = (Uint) (now_utc_tvsec / 1000000);
+ *sec = (Uint) (now_utc_tvsec % 1000000);
+ *microsec = (Uint) (now.tv_usec);
+}
+
+/* end of extension */
_______________________________________________
erlang-patches mailing list
erlang-patches@...
http://www.erlang.org/mailman/listinfo/erlang-patches