/*****************************************************************************\
 *  slurm_time.h - assorted time functions
 *****************************************************************************
 *  Convert `time_t' to `struct tm' in local time zone.
 *  Copyright (C) 1991-2015 Free Software Foundation, Inc.
 *  This file is part of the GNU C Library.
 *
 *  The GNU C Library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  The GNU C Library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with the GNU C Library; if not, see
 *  <http://www.gnu.org/licenses/>.
\*****************************************************************************/
#ifndef _HAVE_SLURM_TIME_H
#define _HAVE_SLURM_TIME_H

#include <stdbool.h>
#include <stdint.h>
#include <sys/time.h>
#include <time.h>

/* Always use CLOCK_REALTIME to match pthread_cond_timedwait() */
#define TIMESPEC_CLOCK_TYPE CLOCK_REALTIME

extern time_t slurm_mktime(struct tm *tp);

/* Slurm variants of ctime and ctime_r without a trailing new-line */
extern char *slurm_ctime2(const time_t *timep);
extern char *slurm_ctime2_r(const time_t *timep, char *time_str);

/*
 * Return the number of micro-seconds between now and argument "tv",
 * Initialize tv to NOW if zero on entry
 */
extern int slurm_delta_tv(struct timeval *tv);

/*
 * Slurm wrapper for the nanosleep() function. This function will call
 * nanosleep() until the elapsed time passes, or until nanosleep() returns
 * an error with errno != EINTR.
 *
 * According to nanosleep(2):
 *
 *     Compared to sleep(3) and usleep(3), nanosleep() has the following
 *     advantages: it provides a higher resolution for specifying the
 *     sleep interval; POSIX.1 explicitly specifies that it does not
 *     interact with signals; and it makes the task of resuming a sleep
 *     that has been interrupted by a signal handler easier.
 *
 * Note: This function is subject to drift. According to nanosleep(2):
 *
 *     The fact that nanosleep() sleeps for a relative interval can be
 *     problematic if the call is repeatedly restarted after being
 *     interrupted by signals, since the time between the interruptions and
 *     restarts of the call will lead to drift in the time when the sleep
 *     finally completes.  This problem can be avoided by using
 *     clock_nanosleep(2) with an absolute time value.
 *
 * Don't use this function if sleeping for an exact time is important.
 *
 * IN sleep_sec - number of seconds to sleep.
 * IN sleep_ns - number of nanoseconds to sleep. If this number is outside of
 *               the range [0, 999999999] then nanosleep() will return EINVAL.
 * Returns SLURM_SUCCESS on success. Returns errno set by nanosleep() on error.
 * This function will never return EINTR.
 */
extern int slurm_nanosleep(time_t sleep_sec, uint32_t sleep_ns);

/* Print the current date + time as formatted by slurm_ctime2_r */
extern void print_date(void);

/* Create typedef to follow *_t naming convention */
typedef struct timespec timespec_t;

/*
 * timespec equivalent of -1 (AKA forever in the future).
 * IEEE Std 1003.1, 2004 defines tv_sec as time_t but only defines time_t as a
 * "integer or real-floating types". "GNU C Library additionally guarantees that
 * time_t is a signed type".
 * Note: Ignores nsecs due to normalization
 * TIMESPEC_INFINITE is expected to be used for relative timestamps where
 * (timespec_t){0} is same as timespec_now() for absolute timestamps.
 */
#define TIMESPEC_INFINITE \
	((timespec_t) { \
		.tv_sec = -1, \
		.tv_nsec = 0, \
	})

/* True if X is equal to or after TIMESPEC_INFINITE */
extern bool timespec_is_infinite(timespec_t x);

/* Get timespec for current timestamp from UNIX Epoch */
extern timespec_t timespec_now(void);

/* Minimum number of bytes for output of timespec_ctime() */
#define TIMESPEC_CTIME_STR_LEN 72

/* Converts micro seconds to timespec_t */
#define TIMESPEC_FROM_USEC(usecs) \
	((timespec_t) { \
		.tv_sec = ((usecs) / USEC_IN_SEC), \
		.tv_nsec = (((usecs) % USEC_IN_SEC) * NSEC_IN_USEC), \
	})
/*
 * Convert timespec into human readable string
 *
 * IN ts - timestamp
 * IN abs_time -
 *	true if ts is time since UNIX epoch
 *	false if ts is arbitrary length of time
 * IN buffer - pointer to buffer to populate (always \0 terminates string)
 * IN buffer_len - number of bytes in buffer. Should always be >=
 *	TIMESPEC_CTIME_STR_LEN.
 * RET number of bytes written to buffer
 */
extern int timespec_ctime(timespec_t ts, bool abs_time, char *buffer,
			  size_t buffer_len);

typedef struct {
	char str[TIMESPEC_CTIME_STR_LEN];
} timespec_ctime_str_t;

/*
 * Populate string from timespec
 * WARNING: Use TIMESPEC_STR() macro instead calling func directly
 * IN ts - timestamp
 * IN abs_time -
 *	true if ts is time since UNIX epoch
 *	false if ts is arbitrary length of time
 * RET timespec_ctime_str_t::str populated
 */
extern timespec_ctime_str_t timespec_ctime_str(timespec_t ts, bool abs_time);

/*
 * Convert timespec to string (for logging)
 * IN ts - timestamp
 * IN abs_time -
 *	true if ts is time since UNIX epoch
 *	false if ts is arbitrary length of time
 * RET timestamp as string or "INVALID" (which must be used immediately)
 */
#define TIMESPEC_STR(ts, abs_time) timespec_ctime_str((ts), (abs_time)).str

/* Add overflow of nanoseconds into seconds */
extern timespec_t timespec_normalize(timespec_t ts);

/* Add timestamp X to timestamp Y */
extern timespec_t timespec_add(timespec_t x, timespec_t y);

/* Subtract timestamp Y from timestamp X */
extern timespec_t timespec_rem(timespec_t x, timespec_t y);

/* Is timestamp X after timestamp Y */
extern bool timespec_is_after(const timespec_t x, const timespec_t y);

/*
 * Subtract timestamp Y from timestamp X
 * RET diff in seconds (drops nanoseconds)
 */
extern int64_t timespec_diff(const timespec_t x, const timespec_t y);

typedef struct {
	timespec_t diff; /* x - y */
	bool after; /* x is after y */
} timespec_diff_ns_t;

/*
 * Subtract timestamp Y from timestamp X
 */
extern timespec_diff_ns_t timespec_diff_ns(const timespec_t x,
					   const timespec_t y);

/* Convert timestamp to seconds with decimal for nanoseconds */
extern double timespec_to_secs(const timespec_t x);

/*
 * Time diff to deadline passing against timespec_now()
 * IN deadline - absolute time of deadline
 * RET
 *	negative: seconds since deadline elapsed
 *	positive: seconds before deadline elapsed
 *	INFINITE64: deadline is TIMESPEC_INFINITE
 */
extern int64_t timespec_after_deadline(const timespec_t deadline);

/*
 * Return time in milliseconds since "start time"
 * Takes a struct timeval.
 */
extern int timeval_tot_wait(struct timeval *start_time);

/* True if X is equal to zero */
extern bool timespec_is_zero(timespec_t x);

#endif /* _HAVE_SLURM_TIME_H */
