xref: /netbsd-src/lib/libc/time/difftime.c (revision ace5b9b5feb0e7608bd2da7a617428d2e1cf8aa3)
1*ace5b9b5Schristos /*	$NetBSD: difftime.c,v 1.24 2024/01/20 14:52:49 christos Exp $	*/
2b2b04f7eSchristos 
3b2b04f7eSchristos /* Return the difference between two timestamps.  */
49b9f8829Sjtc 
59b9f8829Sjtc /*
69b9f8829Sjtc ** This file is in the public domain, so clarified as of
786162a51Smlelstv ** 1996-06-05 by Arthur David Olson.
89b9f8829Sjtc */
9ac908354Sjtc 
103ad08ca2Schristos #include <sys/cdefs.h>
11f9b799b6Smsaitoh #if defined(LIBC_SCCS) && !defined(lint)
123ad08ca2Schristos #if 0
1386162a51Smlelstv static char	elsieid[] = "@(#)difftime.c	8.1";
143ad08ca2Schristos #else
15*ace5b9b5Schristos __RCSID("$NetBSD: difftime.c,v 1.24 2024/01/20 14:52:49 christos Exp $");
163ad08ca2Schristos #endif
17f9b799b6Smsaitoh #endif /* LIBC_SCCS and not lint */
18f9b799b6Smsaitoh 
191fac1c17Sjtc /*LINTLIBRARY*/
201fac1c17Sjtc 
2133d9f9e0Schristos #include "private.h"	/* for time_t and TYPE_SIGNED */
221fac1c17Sjtc 
23d856f74aSchristos /* Return -X as a double.  Using this avoids casting to 'double'.  */
24d856f74aSchristos static double
dminus(double x)25d856f74aSchristos dminus(double x)
26d856f74aSchristos {
27d856f74aSchristos 	return -x;
28d856f74aSchristos }
29d856f74aSchristos 
305f2f4c60Schristos double
difftime(time_t time1,time_t time0)31d856f74aSchristos difftime(time_t time1, time_t time0)
321fac1c17Sjtc {
3386162a51Smlelstv 	/*
34d856f74aSchristos 	** If double is large enough, simply convert and subtract
3586162a51Smlelstv 	** (assuming that the larger type has more precision).
3686162a51Smlelstv 	*/
37d472a915Schristos 	/*CONSTCOND*/
38d856f74aSchristos 	if (sizeof(time_t) < sizeof(double)) {
39d856f74aSchristos 		double t1 = time1, t0 = time0;
40d856f74aSchristos 		return t1 - t0;
41d856f74aSchristos  	}
42d856f74aSchristos 
431fac1c17Sjtc 	/*
4486162a51Smlelstv 	** The difference of two unsigned values can't overflow
4586162a51Smlelstv 	** if the minuend is greater than or equal to the subtrahend.
461fac1c17Sjtc 	*/
47d856f74aSchristos 	if (!TYPE_SIGNED(time_t))
48*ace5b9b5Schristos 		/*NOTREACHED*/
491ef6fb3dSchristos 		return time0 <= time1 ? time1 - time0 :
501ef6fb3dSchristos 		    dminus((double)(time0 - time1));
51d856f74aSchristos 
52d856f74aSchristos 	/* Use uintmax_t if wide enough.  */
53d472a915Schristos 	/*CONSTCOND*/
54d856f74aSchristos 	if (sizeof(time_t) <= sizeof(uintmax_t)) {
55d856f74aSchristos 		uintmax_t t1 = time1, t0 = time0;
561ef6fb3dSchristos 		return time0 <= time1 ? t1 - t0 : dminus((double)(t0 - t1));
5786162a51Smlelstv 	}
58d856f74aSchristos 
591fac1c17Sjtc 	/*
6086162a51Smlelstv 	** Handle cases where both time1 and time0 have the same sign
6186162a51Smlelstv 	** (meaning that their difference cannot overflow).
621fac1c17Sjtc 	*/
6386162a51Smlelstv 	if ((time1 < 0) == (time0 < 0))
6486162a51Smlelstv 		return time1 - time0;
65d856f74aSchristos 
661fac1c17Sjtc 	/*
67d856f74aSchristos 	** The values have opposite signs and uintmax_t is too narrow.
68a37624b5Schristos 	** This suffers from double rounding; attempt to lessen that
69a37624b5Schristos 	** by using long double temporaries.
701fac1c17Sjtc 	*/
71d856f74aSchristos 	{
72d856f74aSchristos 		long double t1 = time1, t0 = time0;
73d856f74aSchristos 		return t1 - t0;
74d856f74aSchristos 	}
751fac1c17Sjtc }
76