emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Observation of hysteresis in a GNU libc time conversion function


From: Max Nikulin
Subject: Observation of hysteresis in a GNU libc time conversion function
Date: Wed, 18 Jan 2023 00:22:12 +0700
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.4.2

#+title: Observation of hysteresis in a GNU libc time conversion function

#+begin_abstract
The ~mktime~ function in GNU libc for specific arguments
may possess properties similar to ferromagnetic materials.
Dependence of returned value on arguments passed during
earler calls gives evidences that it has hidden state.
#+end_abstract

I was experimenting with a code sample posted to the thread on
support of time zones in Org. I noticed rather strange behavior
of ~encode-time~ and thus underlying ~mktime~ function.
At first I considered it as non-deterministic, but later
I realized that time zone offset calculated earlier
may affect following calls. I was aware that the function is
not pure since it calls ~tzset~ to initialize time zone from
the ~TZ~ environment variable and e.g. ~tzname~ global variable.
It was a surprise that the function can not be considered
even as a stable one. Passing same arguments may lead
to different results. I am unsure whether it is intentional
or at least known property.

#+begin_src elisp :exports nil :results silent
  (require 'ob-shell)
  (require 'ob-gnuplot)
#+end_src

Let's take some backward time transition, so with ambiguous local time,
where daylight saving time state is not changed and preferably
is not in action. As a result it is impossible to disambiguate
whether local time is before or after time jump.
Attempt to specify ~dst~ as ~t~
(or set ~t.tm_isdst = 1~ for ~mktime~) will lead to an error.

#+begin_src sh :results verbatim :exports both
  zdump -v Africa/Juba | grep 2021
#+end_src

#+RESULTS:
: Africa/Juba Sun Jan 31 20:59:59 2021 UT = Sun Jan 31 23:59:59 2021 EAT isdst=0 gmtoff=10800 : Africa/Juba Sun Jan 31 21:00:00 2021 UT = Sun Jan 31 23:00:00 2021 CAT isdst=0 gmtoff=7200

We may convert broken down local time representation
around 23:30 local time
for a sequence of time moments passing values to ~encode-time~
in increasing and decreasing their order.
Under the hood the function calls the =mktime(3)= function.

#+name: timestamp
#+header: :exports code
#+begin_src elisp :var tz="Africa/Juba" :var t0='(0 30 23 31 1 2021)
    (let* ((f (lambda (minutes)
                (float-time
                 (encode-time
                  (list (nth 0 t0) (+ (nth 1 t0) minutes) (nth 2 t0)
                        (nth 3 t0) (nth 4 t0) (nth 5 t0)
                        nil -1 tz)))))
           (dt '(-90 -60 -31 -30 -29 -15 0 15 29 30 31 60 90))
           (values (mapcar (lambda (m)
                             (cons m (funcall f m)))
                           dt))
           (ts0 (cdr (nth (/ (length values) 2) values))))
      (mapcar (lambda (pair)
                (let ((m (car pair)))
                  (list m (- (cdr pair) ts0) (- (funcall f m) ts0))))
              (reverse values)))
#+end_src

#+RESULTS: timestamp
|  90 |  9000.0 |  9000.0 |
|  60 |  7200.0 |  7200.0 |
|  31 |  5460.0 |  5460.0 |
|  30 |  5400.0 |  5400.0 |
|  29 |  1740.0 |  5340.0 |
|  15 |   900.0 |  4500.0 |
|   0 |     0.0 |  3600.0 |
| -15 |  -900.0 |  2700.0 |
| -29 | -1740.0 |  1860.0 |
| -30 | -1800.0 |  1800.0 |
| -31 | -1860.0 | -1860.0 |
| -60 | -3600.0 | -3600.0 |
| -90 | -5400.0 | -5400.0 |

#+begin_src gnuplot :file mktime-hyst.png :var data=timestamp
  set key bottom right
  set xlabel 'minutes'
  set ylabel 'UNIX epoch difference, seconds'
  set title 'Hysteresis in GNU libc mktime'
  plot data using 1:2 with lp title 'increasing', \
     '' using 1:3 with lp title 'decreasing'
#+end_src

#+RESULTS:
[[file:mktime-hyst.png]]

So result for the same arguments may depend on previous calls.

Likely it is due to a
[[https://sourceware.org/git/?p=glibc.git;a=blob;f=time/mktime.c;h=94a4320e6ca9d935fc534991f9b57a2f1cc185de;hb=HEAD#l544][static variable]]
used in the =mktime.c= file. Such a variable appeared in the commit
[[https://sourceware.org/git/?p=glibc.git;a=commit;h=80fd73873b][80fd73873b]]
#+begin_example
Fri Sep 29 03:43:51 1995  Paul Eggert

Rewrite mktime from scratch for performance, and for correctness
in the presence of leap seconds.
#+end_example

Perhaps hidden internal state may be used to disambiguate local
time values around backward time steps with unchanged daylight
saving time. Unfortunately relying on such implementation details
hardly can be considered as a robust approach.

Attachment: mktime-hyst.png
Description: PNG image


reply via email to

[Prev in Thread] Current Thread [Next in Thread]