1/*	$NetBSD: difftime.c,v 1.24 2024/01/20 14:52:49 christos Exp $	*/
2
3/* Return the difference between two timestamps.  */
4
5/*
6** This file is in the public domain, so clarified as of
7** 1996-06-05 by Arthur David Olson.
8*/
9
10#include <sys/cdefs.h>
11#if defined(LIBC_SCCS) && !defined(lint)
12#if 0
13static char	elsieid[] = "@(#)difftime.c	8.1";
14#else
15__RCSID("$NetBSD: difftime.c,v 1.24 2024/01/20 14:52:49 christos Exp $");
16#endif
17#endif /* LIBC_SCCS and not lint */
18
19/*LINTLIBRARY*/
20
21#include "private.h"	/* for time_t and TYPE_SIGNED */
22
23/* Return -X as a double.  Using this avoids casting to 'double'.  */
24static double
25dminus(double x)
26{
27	return -x;
28}
29
30double
31difftime(time_t time1, time_t time0)
32{
33	/*
34	** If double is large enough, simply convert and subtract
35	** (assuming that the larger type has more precision).
36	*/
37	/*CONSTCOND*/
38	if (sizeof(time_t) < sizeof(double)) {
39		double t1 = time1, t0 = time0;
40		return t1 - t0;
41 	}
42
43	/*
44	** The difference of two unsigned values can't overflow
45	** if the minuend is greater than or equal to the subtrahend.
46	*/
47	if (!TYPE_SIGNED(time_t))
48		/*NOTREACHED*/
49		return time0 <= time1 ? time1 - time0 :
50		    dminus((double)(time0 - time1));
51
52	/* Use uintmax_t if wide enough.  */
53	/*CONSTCOND*/
54	if (sizeof(time_t) <= sizeof(uintmax_t)) {
55		uintmax_t t1 = time1, t0 = time0;
56		return time0 <= time1 ? t1 - t0 : dminus((double)(t0 - t1));
57	}
58
59	/*
60	** Handle cases where both time1 and time0 have the same sign
61	** (meaning that their difference cannot overflow).
62	*/
63	if ((time1 < 0) == (time0 < 0))
64		return time1 - time0;
65
66	/*
67	** The values have opposite signs and uintmax_t is too narrow.
68	** This suffers from double rounding; attempt to lessen that
69	** by using long double temporaries.
70	*/
71	{
72		long double t1 = time1, t0 = time0;
73		return t1 - t0;
74	}
75}
76