<feed xmlns='http://www.w3.org/2005/Atom'>
<title>libplist/test/date_val.c, branch 2.8.1</title>
<subtitle>Library to handle Apple Property List format files in binary or XML</subtitle>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/'/>
<entry>
<title>plist: fix the (sec, usec) date API round trip</title>
<updated>2026-11-06T09:29:06+00:00</updated>
<author>
<name>Flavio Toffalini</name>
</author>
<published>2026-10-26T09:03:57+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=1d3b54c784da260f71d885f5f560d69c211a645e'/>
<id>1d3b54c784da260f71d885f5f560d69c211a645e</id>
<content type='text'>
plist_set_date_val stores val = sec + usec / 1000000, but
plist_get_date_val splits it with a truncating cast and fabs():

    *sec  = (int32_t)val;
    val   = fabs((val - (int64_t)val) * 1000000);
    *usec = (int32_t)val;

Two results are wrong. The product is truncated, so set(5, 6) reads
back as (5, 5) - 29.3% of round trips over sec in [0, 3] come back a
microsecond short. And for a negative value, truncation toward zero
plus fabs() give a pair that does not denote the same instant:
set(-3, 250000) stores -2.75 and reads back as (-2, 750000), which the
setter would store as -1.25. Every round trip with sec &lt; 0 and
usec != 0 is wrong.

(int32_t)val is also undefined when the stored value is outside the
int32_t range.

Round the seconds down, round the fraction to microseconds carrying
1000000 into the seconds, and clamp the seconds before converting, so
sec + usec / 1000000 == val to the microsecond with 0 &lt;= usec &lt;
1000000 - the convention the setter uses.

Add test/date_val.c covering fixed cases and a sweep over sec in
[-2, 2]. It fails on an unmodified tree - set(5, 6), set(-3, 250000)
and set(-1, 1) all come back as different instants - and passes with
this change, with make check at 44/44.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Arn4YaWtcQBDsQuJtyBmFP
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
plist_set_date_val stores val = sec + usec / 1000000, but
plist_get_date_val splits it with a truncating cast and fabs():

    *sec  = (int32_t)val;
    val   = fabs((val - (int64_t)val) * 1000000);
    *usec = (int32_t)val;

Two results are wrong. The product is truncated, so set(5, 6) reads
back as (5, 5) - 29.3% of round trips over sec in [0, 3] come back a
microsecond short. And for a negative value, truncation toward zero
plus fabs() give a pair that does not denote the same instant:
set(-3, 250000) stores -2.75 and reads back as (-2, 750000), which the
setter would store as -1.25. Every round trip with sec &lt; 0 and
usec != 0 is wrong.

(int32_t)val is also undefined when the stored value is outside the
int32_t range.

Round the seconds down, round the fraction to microseconds carrying
1000000 into the seconds, and clamp the seconds before converting, so
sec + usec / 1000000 == val to the microsecond with 0 &lt;= usec &lt;
1000000 - the convention the setter uses.

Add test/date_val.c covering fixed cases and a sweep over sec in
[-2, 2]. It fails on an unmodified tree - set(5, 6), set(-3, 250000)
and set(-1, 1) all come back as different instants - and passes with
this change, with make check at 44/44.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Arn4YaWtcQBDsQuJtyBmFP
</pre>
</div>
</content>
</entry>
</feed>
