/*
** Copyright (C) 2001-2026 Carnegie Mellon University
**
** @OPENSOURCE_LICENSE_START@
**
** SiLK 3.24
**
** Copyright 2025 Carnegie Mellon University.
**
** NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING
** INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON
** UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR
** IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF
** FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS
** OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
** MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT,
** TRADEMARK, OR COPYRIGHT INFRINGEMENT.
**
** Licensed under a GNU GPL 2.0-style license, please see LICENSE.txt or
** contact permission@sei.cmu.edu for full terms.
**
** [DISTRIBUTION STATEMENT A] This material has been approved for public
** release and unlimited distribution.  Please see Copyright notice for
** non-US Government use and distribution.
**
** This Software includes and/or makes use of Third-Party Software each
** subject to its own license.
**
** DM25-0915
**
** @OPENSOURCE_LICENSE_END@
*/

/*
**  sktime.h
**
**      Types, macros, and functions for manipulating times, time differences,
**      and timestamps.
**
**      This file is included by utils.h so there is no need to include it
**      directly.
**
*/
#ifndef _SKTIME_H
#define _SKTIME_H
#ifdef __cplusplus
extern "C" {
#endif

#include <silk/silk.h>

RCSIDENTVAR(rcsID_SKTIME_H, "$SiLK: sktime.h 8e6b8cae8a2c 2026-04-21 19:03:04Z mthomas $");

#include <silk/silk_types.h>


/**
 *    Flags that can be use to specify how a timestamp will be
 *    printed.
 */
typedef enum sktimestamp_flags_en {
    /** Do not include fractional seconds when printing the time
     *  Since SiLK 3.23.0; replaced SKTIMESTAMP_NOMSEC */
    SKTIMESTAMP_NOFRAC   = (1u << 0),
    /** Print as MM/DD/YYYY HH:MM:SS[.sss] */
    SKTIMESTAMP_MMDDYYYY = (1u << 1),
    /** Print as seconds since the UNIX epoch */
    SKTIMESTAMP_EPOCH    = (1u << 2),
    /** Print as YYYY-MM-DD HH:MM:SS[.sss] */
    SKTIMESTAMP_ISO      = (1u << 3),
    /** Print the time as UTC (assuming TZ=0) */
    SKTIMESTAMP_UTC      = (1u << 4),
    /** Print the time in the local timezone */
    SKTIMESTAMP_LOCAL    = (1u << 5),
    /** Include milliseconds
     *  Since SiLK 3.23.0 */
    SKTIMESTAMP_MILLI    = (1u << 6),
    /** Include microseconds
     *  Since SiLK 3.23.0 */
    SKTIMESTAMP_MICRO    = (1u << 7),
    /** Include nanoseconds
     *  Since SiLK 3.23.0 */
    SKTIMESTAMP_NANO     = (1u << 8),
} sktimestamp_flags_t;

/**
 *    A mask to apply to values holding sktimestamp_flags_t to get only the
 *    flags relating to precision (representation of fractional seconds).
 *
 *    Since SiLK 3.23.0
 */
#define sktimestamp_flags_precision             \
    (SKTIMESTAMP_NOFRAC | SKTIMESTAMP_MILLI     \
     | SKTIMESTAMP_MICRO | SKTIMESTAMP_NANO)

/**
 *    Deprecate SKTIMESTAMP_NOMSEC.  Must be defined as a variable since enum
 *    entries cannot be marked as deprecated.  Deprecated in SiLK 3.23.0.
 */
extern const int SKTIMESTAMP_NOMSEC   SK_GCC_DEPRECATED;

/**
 *    Fill 'outbuf' with an ASCII version of the time 't' and return a
 *    pointer to 'output'.  This function assumes that 'outbuf' is at
 *    least SKTIMESTAMP_STRLEN characters long.  SKTIMESTAMP_STRLEN is
 *    defined in silk_types.h.
 *
 *    By default, the timestamp will be in the form:
 *
 *        "YYYY/MM/DDTHH:MM:SS.sss"
 *
 *    where "sss" == milliseconds, the "T" is a literal 'T'.
 *
 *    The parameter 'timestamp_flags' can be used to change the
 *    printed time, where the flags are a bitwise OR of the
 *    sktimestamp_flags_t values.
 *
 *    The following fields for 'timestamp_flags' are mutually
 *    exclusive; if more than one is set, the time is printed in the
 *    default form and no fractional seconds are printed:
 *
 *        SKTIMESTAMP_EPOCH prints the value as the number of seconds
 *        since the UNIX epoch.  The timezone bits are ignored.
 *
 *            "SSSSSSSSSS[.sss]"
 *
 *        SKTIMESTAMP_MMDDYYYY causes the time to be printed as:
 *
 *            "MM/DD/YYYY HH:MM:SS[.sss]"
 *
 *        SKTIMESTAMP_ISO causes the time to be printed as
 *
 *            "YYYY-MM-DD HH:MM:SS[.sss]"
 *
 *    The following bits operate independently of any other bits.  If
 *    multiple bits are set, no fractional seconds are printed.
 *
 *        SKTIMESTAMP_NOFRAC suppresses the printing of the fractional
 *        seconds.  The fractional seconds are dropped and the
 *        remaining value is NOT rounded.
 *
 *        SKTIMESTAMP_MILLI causes milliseconds to be printed.  This was the
 *        default behavior prior to SiLK 3.23.0.
 *
 *        SKTIMESTAMP_MICRO causes microseconds to be printed.
 *
 *        SKTIMESTAMP_NANO causes nanoseconds to be printed.
 *
 *    The 'timestamp_flags' value can affect the timezone used when
 *    printing the time.  If neither (or both) of the following bits
 *    are set, the time is printed in UTC unless SiLK was configured
 *    with --enable-local-timezone, in which case the local timezone
 *    is used.
 *
 *        SKTIMESTAMP_UTC causes the value to be printed in UTC,
 *        regardless of whether SiLK was configured with the
 *        --enable-local-timezone switch.
 *
 *        SKTIMESTAMP_LOCAL causes the value to be printed in the
 *        local timezone, regardless of whether SiLK was configured
 *        with the --enable-local-timezone switch.
 */
char *
sktimestamp_r(
    char               *outbuf,
    sktime_t            t,
    unsigned int        timestamp_flags);


/**
 *    Similar to sktimestamp_r(), except returns the value in a static
 *    buffer.
 */
char *
sktimestamp(
    sktime_t            t,
    unsigned int        timestamp_flags);


/**
 *    Returns the minimum column width needed to hold a timestamp formatted
 *    with `timestamp_flags`.
 *
 *    Since SiLK 3.23.0
 */
int
sktimestamp_width(
    unsigned int        timestamp_flags);


/**
 *    Return the maximum day in a given month/year
 *
 *    NOTE:  Months are in the 1..12 range and NOT 0..11
 *
 */
int
skGetMaxDayInMonth(
    int                 yr,
    int                 mo);


/**
 *    A static initializer for an sktime_t.
 *
 *    Since SiLK 3.23.0
 */
#define SKTIME_INITIALIZER  { 0 }

/**
 *    Maximum sktime value
 *
 *    Since SiLK 3.23.0
 */
extern const sktime_t SKTIME_MAX;

/**
 *    Initialize an sktime_t to 0.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeInit()    (sktimeCreateFromSeconds(0))

/**
 *    Returns 1 if the sktime_t has a value other than 0; returns 0 otherwise.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeIsSet(skts_time)  (sktimeCmp(sktimeInit(), (skts_time), !=))

/**
 *    Returns a non-zero value if the sktime_t is at the maximum value;
 *    returns 0 otherwise.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeIsMax(sktm_time)  (sktimeCmp((sktm_time), SKTIME_MAX, ==))

/**
 *    Return an sktime set to the current UTC time in the maximum internal
 *    precision.
 */
sktime_t
sktimeNow(
    void);

/**
 *    Tests whether the expression (`sktime_a` `oper_` `sktime_b`) is TRUE
 *    where `oper_` is one of
 *
 *    <  <=  ==  >=  >  !=
 *
 *    @see Use sktimeCompare() for a qsort-compatible comparison function.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeCmp(sktime_a, sktime_b, oper_)    \
    ((sktime_a).t oper_ (sktime_b).t)

/**
 *    Returns a value less than, equal to, or greater than zero if `sktime_a`
 *    is less than, equal to, or greater than `sktime_b`, respectively.
 *
 *    @see Use sktimeCmp() to compare `sktime_t`s with an arbitrary operator
 *
 *    Since SiLK 3.23.0
 */
static inline int
sktimeCompare(
    const sktime_t  sktime_a,
    const sktime_t  sktime_b)
{
    return ((sktime_a.t < sktime_b.t) ? -1 : (sktime_a.t > sktime_b.t));
}

static inline sktime_t  SK_GCC_WARN_UNUSED_RESULT
sk_time_creator(
    int64_t     sec,
    int64_t     frac,
    int         frac_units)
{
    sktime_t t;

    switch (frac_units) {
      case 0:
        t.t = INT64_C(1000000000) * sec;
        break;
      case 3:
        t.t = INT64_C(1000000000) * sec + frac * INT64_C(1000000);
        break;
      case 6:
        t.t = INT64_C(1000000000) * sec + frac * INT64_C(1000);
        break;
      case 9:
        t.t = INT64_C(1000000000) * sec + frac;
        break;
      default:
        skAbortBadCase(frac_units);
    }
    return t;
}

/**
 *    Computes the interval between two `sktime_t`s and returns an
 *    skInterval_t.
 *
 *    Use sktimeSubtractInterval() to subtract an interval from a time.
 *
 *    Since SiLK 3.23.0
 */
static inline skInterval_t  SK_GCC_WARN_UNUSED_RESULT
sktimeComputeInterval(
    const sktime_t  time_end,
    const sktime_t  time_start)
{
    skInterval_t nval;
    nval.d = (time_end.t - time_start.t);
    return nval;
}

/**
 *    Adds an interval to a time and returns a new time.
 *
 *    Since SiLK 3.23.0
 */
static inline sktime_t  SK_GCC_WARN_UNUSED_RESULT
sktimeAddInterval(
    const sktime_t      time_augend,
    const skInterval_t  diff_addend)
{
    sktime_t t;
    t.t = (time_augend.t + diff_addend.d);
    return t;
}

/**
 *    Subtracts an interval from a time and returns a new time.
 *
 *    Use sktimeComputeInterval() to compute the difference of two times.
 *
 *    Since SiLK 3.23.0
 */
static inline sktime_t  SK_GCC_WARN_UNUSED_RESULT
sktimeSubtractInterval(
    const sktime_t      time_minuend,
    const skInterval_t  diff_subtrahend)
{
    sktime_t t;
    t.t = (time_minuend.t - diff_subtrahend.d);
    return t;
}

/**
 *    Returns an sktime_t initialized from a value containing seconds since
 *    the UNIX epoch (such as a time_t).
 *
 *    Since SiLK 3.23.0
 */
#define sktimeCreateFromSeconds(sktc_seconds)           \
    sk_time_creator(sktc_seconds, 0, 0)


/**
 *    Returns an sktime_t initialized from values containing (1)seconds since
 *    the UNIX epoch (such as a time_t) and (2)a millisecond count.  The
 *    second parameter can be any value containing milliseconds; there is no
 *    restriction on the range of its value.
 *
 *    Renamed from sktimeCreate() in SiLK 3.23.0
 */
#define sktimeCreateFromMilli(sktc_seconds, sktc_milli) \
    sk_time_creator(sktc_seconds, sktc_milli, 3)

/**
 *    Previous name for sktimeCreateFromMilli().
 */
sktime_t
sktimeCreate(
    int64_t   seconds,
    int64_t   millis)
    SK_GCC_DEPRECATED;

/**
 *    Returns an sktime_t initialized from values containing (1)seconds since
 *    the UNIX epoch (such as a time_t) and (2)a microsecond count.  The
 *    second parameter can be any value containing microseconds; there is no
 *    restriction on the range of its value.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeCreateFromMicro(sktc_seconds, sktc_micro) \
    sk_time_creator(sktc_seconds, sktc_micro, 6)

/**
 *    Returns an sktime_t initialized from values containing (1)seconds since
 *    the UNIX epoch (such as a time_t) and (2)a nanosecond count.  The second
 *    parameter can be any value containing nanoseconds; there is no
 *    restriction on the range of its value.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeCreateFromNano(sktc_seconds, sktc_nano)   \
    sk_time_creator(sktc_seconds, sktc_nano, 9)

/**
 *    Returns an sktime_t initialized from a floating point number
 *    representing seconds since the UNIX epoch.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeCreateFromDouble(sktc_dbl)        \
    sk_time_creator(0, (1e9 * (sktc_dbl)), 9)

/**
 *    Returns an sktime_t initialized using a pointer to a struct timeval.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeCreateFromTimeval(sktc_tv)                                \
    sktimeCreateFromMicro((sktc_tv)->tv_sec, (sktc_tv)->tv_usec)

/**
 *    Returns an sktime_t initialized using a pointer to a struct timespec.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeCreateFromTimespec(sktc_ts)                       \
    sktimeCreateFromNano((sktc_ts)->tv_sec, (sktc_ts)->tv_nsec)

/**
 *    Returns an sktime_t initialized using a pointer to a 64-bit number
 *    representing an NTP time (RFC1305).
 *
 *    When the 'is_micro' flag is set, the function ignores the 11 lowest bits
 *    of the fractional part of the timestamp.
 *
 *    If 'ntp' is 0, return 2036-02-07 06:28:16Z for the start of NTP Era 1;
 *    that value in decimal epoch seconds is 2085978496.
 *
 *    Since SiLK 3.23.0.
 */
sktime_t  SK_GCC_WARN_UNUSED_RESULT
sktimeCreateFromNTP(
    const uint64_t *ntp,
    int             is_micro);

/**
 *    Returns 1 if `later_time` is equal to or greater then `earlier_time`
 *    plus `interval`.  Returns 0 otherwise.
 *
 *    Since SiLK 3.23.0
 */
static inline int
sktimeCheckInterval(
    const sktime_t      later_time,
    const sktime_t      earlier_time,
    const skInterval_t  interval)
{
    return (later_time.t >= (earlier_time.t + interval.d));
}

/**
 *    Given an sktime_t value, fills the referents of 'seconds' and
 *    'milliseconds' with the number of seconds and milliseconds that the
 *    sktime_t value represents.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeGetSecondsMilli(sktgp_time, sktgp_seconds, sktgp_milli)   \
    do {                                                                \
        imaxdiv_t sktgp_d = imaxdiv((sktgp_time).t,                     \
                                    INT64_C(1000000000));               \
        *(sktgp_seconds) = sktgp_d.quot;                                \
        *(sktgp_milli) = sktgp_d.rem / INT64_C(1000000);                \
    } while (0)

/**
 *    Given an sktime_t value, fills the referents of 'seconds' and
 *    'microseconds' with the number of seconds and microseconds that the
 *    sktime_t value represents.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeGetSecondsMicro(sktgp_time, sktgp_seconds, sktgp_micro)   \
    do {                                                                \
        imaxdiv_t sktgp_d = imaxdiv((sktgp_time).t,                     \
                                    INT64_C(1000000000));               \
        *(sktgp_seconds) = sktgp_d.quot;                                \
        *(sktgp_micro) = sktgp_d.rem / INT64_C(1000);                   \
    } while (0)

/**
 *    Given an sktime_t value, fills the referents of 'seconds' and
 *    'nanoseconds' with the number of seconds and nanoseconds that the
 *    sktime_t value represents.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeGetSecondsNano(sktgp_time, sktgp_seconds, sktgp_nano)     \
    do {                                                                \
        imaxdiv_t sktgp_d = imaxdiv((sktgp_time).t,                     \
                                    INT64_C(1000000000));               \
        *(sktgp_seconds) = sktgp_d.quot;                                \
        *(sktgp_nano) = sktgp_d.rem;                                    \
    } while (0)

/**
 *    Given an sktime_t and a struct timeval pointer, fills the referent of
 *    the timeval, truncating values less than 1 microsecond.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeGetTimeval(sktgtv_time, sktgtv_tval)                      \
    sktimeGetSecondsMicro((sktgtv_time), &((sktgtv_tval)->tv_sec),      \
                          &((sktgtv_tval)->tv_usec))

/**
 *    Given an sktime_t and a struct timespec pointer, fills the referent of
 *    the timespec, truncating values less than 1 nanosecond.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeGetTimespec(sktgtv_time, sktgtv_tspec)                    \
    sktimeGetSecondsNano((sktgtv_time), &((sktgtv_tspec)->tv_sec),      \
                         &((sktgtv_tspec)->tv_usec))

/**
 *    Given an sktime_t, returns the NTP representation of that time.
 *
 *    Since SiLK 3.23.0
 */
uint64_t
sktimeGetNTP(
    const sktime_t  t);

/**
 *    Returns the floating point number of seconds since the UNIX epoch
 *    represented by the sktime_t.
 *
 *    Since SiLK 3.23.0
 */
#define sktimeGetDouble(sktgd_time)             \
    ((double)((sktgd_time).t) / 1e9)

/**
 *    Given an sktime_t, returns the whole number of seconds since the UNIX
 *    epoch as an integer, truncating any fractional seconds.
 */
#define sktimeGetSeconds(sktgs_time)            \
    ((sktgs_time).t / INT64_C(1000000000))

/**
 *    Given an sktime_t, returns the number of milliseconds since the UNIX
 *    epoch, truncating values less than 1 millisecond.
 *
 *    To get the number of milliseconds within the current second, use
 *    sktimeGetFractionalMilli().
 *
 *    Since SiLK 3.23.0.
 */
#define sktimeGetEpochMilli(sktge_time)         \
    ((sktge_time).t / INT64_C(1000000))

/**
 *    Given an sktime_t, returns the number of microseconds since the UNIX
 *    epoch, truncating values less than 1 microsecond.
 *
 *    To get the number of microseconds within the current second, use
 *    sktimeGetFractionalMicro().
 *
 *    Since SiLK 3.23.0.
 */
#define sktimeGetEpochMicro(sktge_time)         \
    ((sktge_time).t / INT64_C(1000))

/**
 *    Given an sktime_t, returns the number of nanoseconds since the UNIX
 *    epoch, truncating values less than 1 nanosecond.
 *
 *    To get the number of nanoseconds within the current second, use
 *    sktimeGetFractionalNano().
 *
 *    Since SiLK 3.23.0.
 */
#define sktimeGetEpochNano(sktge_time)          \
    ((sktge_time).t)

/**
 *    Given an sktime_t, returns only the fractional seconds as a number of
 *    milliseconds (range 0-999), truncating values less than 1 millisecond.
 *
 *    To get the total number of milliseonds represented by an sktime_t, use
 *    sktimeGetEpochMilli().
 *
 *    Renamed from sktimeGetMilliseconds() in SiLK 3.23.0.
 */
#define sktimeGetFractionalMilli(sktgm_time)            \
    (sktimeGetEpochMilli(sktgm_time) % INT64_C(1000))

/**
 *    Previous name for sktimeGetFractionalMilli().
 */
uint32_t
sktimeGetMilliseconds(
    sktime_t    t)
    SK_GCC_DEPRECATED;

/**
 *    Given an sktime_t, returns only the fractional seconds as a number of
 *    microseconds (range 0-999,999), truncating values less than 1
 *    microsecond.
 *
 *    To get the total number of microseonds represented by an sktime_t, use
 *    sktimeGetEpochMicro().
 *
 *    Since SiLK 3.23.0
 */
#define sktimeGetFractionalMicro(sktgm_time)                    \
    (sktimeGetEpochMicro(sktgm_time) % INT64_C(1000000))

/**
 *    Given an sktime_t, returns only the fractional seconds as a number of
 *    nanoseconds (range 0-999,999,999), truncating values less than 1
 *    nanosecond.
 *
 *    To get the total number of nanoseonds represented by an sktime_t, use
 *    sktimeGetEpochNano().
 *
 *    Since SiLK 3.23.0
 */
#define sktimeGetFractionalNano(sktgm_time)             \
    ((sktgm_time).t % INT64_C(1000000000)))

/**
 *    Given an sktime_t, return an sktime_t set to the most recent UTC hour
 *    containing that time.
 *
 *    Since SiLK 3.23.0
 */
static inline sktime_t  SK_GCC_WARN_UNUSED_RESULT
sktimeGetHourFloor(
    const sktime_t  t)
{
    sktime_t hour;
    hour.t = t.t - t.t % INT64_C(3600000000000);
    return hour;
}



/*    ****  skInterval_t  ****    */

/**
 *    A static initializer for an skInterval_t.
 *
 *    Since SiLK 3.23.0
 */
#define SKINTERVAL_INITIALIZER  { 0 }

/**
 *    Maximum skInterval_t value
 *
 *    Since SiLK 3.23.0
 */
#define SKINTERVAL_MAX          (skIntervalSetFromNano(0, INT64_MAX))

/**
 *    Minimum non-zero skInterval_t value.
 *
 *    Since SiLK 3.23.0
 */
#define SKINTERVAL_RESOLUTION   (skIntervalSetFromNano(0, 1))

/**
 *    Initialize an skInterval_t to 0.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalInit()        (skIntervalSetFromSeconds(0))

/**
 *    Returns 1 if the skInterval_t has a value other than 0; returns 0
 *    otherwise.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalIsSet(skis_interval)                          \
    (skIntervalCmp(skIntervalInit(), (skis_interval), !=))

/**
 *    Returns 1 if the skInterval_t has the maximum value.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalIsMax(skim_interval)                  \
    skIntervalCmp((skim_interval), SKINTERVAL_MAX)

/**
 *    Tests whether the expression (`skinterval_a` `oper_` `skinterval_b`) is
 *    TRUE where `oper_` is one of
 *
 *    <  <=  ==  >=  >  !=
 *
 *    @see Use skIntervalCompare() for a qsort-compatible comparison function.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalCmp(skinterval_a, skinterval_b, oper_)        \
    ((skinterval_a).d oper_ (skinterval_b).d)

/**
 *    Returns a value less than, equal to, or greater than zero if
 *    `skinterval_a` is less than, equal to, or greater than `skinterval_b`,
 *    respectively.
 *
 *    @see Use skIntervalCmp() to compare `skInterval_t`s with an arbitrary
 *    operator
 *
 *    Since SiLK 3.23.0
 */
static inline int
skIntervalCompare(
    const skInterval_t  skinterval_a,
    const skInterval_t  skinterval_b)
{
    return ((skinterval_a.d < skinterval_b.d)
            ? -1 : (skinterval_a.d > skinterval_b.d));
}

static inline skInterval_t  SK_GCC_WARN_UNUSED_RESULT
sk_interval_creator(
    int64_t     sec,
    int64_t     frac,
    int         frac_units)
{
    skInterval_t i;

    switch (frac_units) {
      case 0:
        i.d = INT64_C(1000000000) * sec;
        break;
      case 3:
        i.d = INT64_C(1000000000) * sec + frac * INT64_C(1000000);
        break;
      case 6:
        i.d = INT64_C(1000000000) * sec + frac * INT64_C(1000);
        break;
      case 9:
        i.d = INT64_C(1000000000) * sec + frac;
        break;
      default:
        skAbortBadCase(frac_units);
    }
    return i;
}

/**
 *    Returns an skInterval_t initialized from a value containing a time
 *    difference in seconds.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalSetFromSeconds(skis_seconds)          \
    sk_interval_creator((skis_seconds), 0, 0)

/**
 *    Returns an skInterval_t initialized from values containing (1)a time
 *    difference in seconds and (2)in milliseconds.  The second parameter may
 *    have any value: there is no restriction on its range.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalSetFromMilli(skis_seconds, skis_milli)        \
    sk_interval_creator((skis_seconds), (skis_milli), 3)

/**
 *    Returns an skInterval_t initialized from values containing (1)a time
 *    difference in seconds and (2)in microseconds.  The second parameter may
 *    have any value: there is no restriction on its range.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalSetFromMicro(skis_seconds, skis_micro)        \
    sk_interval_creator((skis_seconds), (skis_micro), 6)

/**
 *    Returns an skInterval_t initialized from values containing (1)a time
 *    difference in seconds and (2)in nanoseconds.  The second parameter may
 *    have any value: there is no restriction on its range.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalSetFromNano(skis_seconds, skis_nano)  \
    sk_interval_creator((skis_seconds), (skis_nano), 9)

/**
 *    Returns an skInterval_t initialized from a floating point number of
 *    seconds.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalSetFromDouble(skis_double)            \
    sk_interval_creator(0, (1e9 * (skis_double)), 9)

/**
 *    Given an skInterval_t value, fills the referents of 'seconds' and
 *    'milliseconds' with the number of seconds and milliseconds that the
 *    skInterval_t value represents.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalGetSecondsMilli(skig_interval, skig_seconds, skig_milli) \
    do {                                                                \
        imaxdiv_t skig_d = imaxdiv((skig_interval).d,                   \
                                   INT64_C(1000000000));                \
        *(skig_seconds) = skig_d.quot;                                  \
        *(skig_milli) = skig_d.rem / INT64_C(1000000);                  \
    } while (0)

/**
 *    Given an skInterval_t value, fills the referents of 'seconds' and
 *    'microseconds' with the number of seconds and microseconds that the
 *    skInterval_t value represents.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalGetSecondsMicro(skig_interval, skig_seconds, skig_micro) \
    do {                                                                \
        imaxdiv_t skig_d = imaxdiv((skig_interval).d,                   \
                                   INT64_C(1000000000));                \
        *(skig_seconds) = skig_d.quot;                                  \
        *(skig_micro) = skig_d.rem / INT64_C(1000);                     \
    } while (0)

/**
 *    Given an skInterval_t value, fills the referents of 'seconds' and
 *    'nanoseconds' with the number of seconds and nanoseconds that the
 *    skInterval_t value represents.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalGetSecondsNano(skig_interval, skig_seconds, skig_nano) \
    do {                                                                \
        imaxdiv_t skig_d = imaxdiv((skig_interval).d,                   \
                                   INT64_C(1000000000));                \
        *(skig_seconds) = skig_d.quot;                                  \
        *(skig_nano) = skig_d.rem;                                      \
    } while (0)

/**
 *    Given an skInterval_t value, returns that interval as a floating point
 *    number of seconds.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalGetDouble(skig_interval)      \
    ((double)((skig_interval).d) / 1e9)

/**
 *    Given an skInterval_t value, returns that interval as a number of
 *    seconds, truncating values less than 1 second.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalGetSeconds(skit_interval)     \
    ((skit_interval).d / INT64_C(1000000000))

/**
 *    Given an skInterval_t value, returns that interval as a number of
 *    milliseconds, truncating values less than 1 millisecond.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalGetMilli(skit_interval)       \
    ((skit_interval).d / INT64_C(1000000))

/**
 *    Given an skInterval_t value, returns that interval as a number of
 *    microseconds, truncating values less than 1 microsecond.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalGetMicro(skit_interval)       \
    ((skit_interval).d / INT64_C(1000))

/**
 *    Given an skInterval_t value, returns that interval as a number of
 *    nanoseconds, truncating values less than 1 nanosecond.
 *
 *    Since SiLK 3.23.0
 */
#define skIntervalGetNano(skit_interval)        \
    ((skit_interval).d)

/**
 *    Returns the sum of two intervals.
 *
 *    Since SiLK 3.23.0
 */
static inline skInterval_t  SK_GCC_WARN_UNUSED_RESULT
skIntervalAdd(
    const skInterval_t interval_a,
    const skInterval_t interval_b)
{
    skInterval_t c;
    c.d = interval_a.d + interval_b.d;
    return c;
}

/**
 *    Returns the difference of `skig_interval_a` and `skig_interval_b`.
 *
 *    Since SiLK 3.23.0
 */
static inline skInterval_t  SK_GCC_WARN_UNUSED_RESULT
skIntervalSubtract(
    const skInterval_t interval_a,
    const skInterval_t interval_b)
{
    skInterval_t c;
    c.d = interval_a.d - interval_b.d;
    return c;
}


#ifdef __cplusplus
}
#endif
#endif /* _UTILS_H */

/*
** Local Variables:
** mode:c
** indent-tabs-mode:nil
** c-basic-offset:4
** End:
*/
