open5gs/lib/core/ogs-time.c

326 lines
8.6 KiB
C

/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright 2013 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright Beman Dawes 2008
* Copyright 2009-2010 Vicente J. Botet Escriba
*
* Distributed under the Boost Software License, Version 1.0.
* See http://www.boost.org/LICENSE_1_0.txt
*/
/*
* Copyright (C) 2019-2020 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef __APPLE__
#include <mach/clock.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#include "core-config-private.h"
#include "ogs-core.h"
/*
* The following code is stolen from mongodb-c-driver
* https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-clock.c
*/
int ogs_gettimeofday(struct timeval *tv)
{
#if defined(_WIN32)
#if defined(_MSC_VER)
#define DELTA_EPOCH_IN_MICROSEC 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSEC 11644473600000000ULL
#endif
FILETIME ft;
uint64_t tmp = 0;
/*
* The const value is shamelessy stolen from
* http://www.boost.org/doc/libs/1_55_0/boost/chrono/detail/inlined/win/chrono.hpp
*
* File times are the number of 100 nanosecond intervals elapsed since
* 12:00 am Jan 1, 1601 UTC. I haven't check the math particularly hard
*
* ... good luck
*/
if (tv) {
GetSystemTimeAsFileTime (&ft);
/* pull out of the filetime into a 64 bit uint */
tmp |= ft.dwHighDateTime;
tmp <<= 32;
tmp |= ft.dwLowDateTime;
/* convert from 100's of nanosecs to microsecs */
tmp /= 10;
/* adjust to unix epoch */
tmp -= DELTA_EPOCH_IN_MICROSEC;
tv->tv_sec = (long) (tmp / 1000000UL);
tv->tv_usec = (long) (tmp % 1000000UL);
}
return 0;
#else
int rc = gettimeofday(tv, NULL);
ogs_assert(rc == 0);
return 0;
#endif
}
ogs_time_t ogs_time_now(void)
{
int rc;
struct timeval tv;
rc = ogs_gettimeofday(&tv);
ogs_assert(rc == 0);
return ogs_time_from_sec(tv.tv_sec) + tv.tv_usec;
}
/* The following code is stolen from APR library */
int ogs_time_from_lt(ogs_time_t *t, struct tm *tm, int tm_usec)
{
ogs_time_t year = tm->tm_year;
ogs_time_t days;
static const int dayoffset[12] =
{306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
if (tm->tm_mon < 0 || tm->tm_mon >= 12)
return OGS_ERROR;
/* shift new year to 1st March in order to make leap year calc easy */
if (tm->tm_mon < 2)
year--;
/* Find number of days since 1st March 1900 (in the Gregorian calendar). */
days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
days += dayoffset[tm->tm_mon] + tm->tm_mday - 1;
days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
days = ((days * 24 + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
if (days < 0) {
return OGS_ERROR;
}
*t = days * OGS_USEC_PER_SEC + tm_usec;
return OGS_OK;
}
int ogs_time_from_gmt(ogs_time_t *t, struct tm *tm, int tm_usec)
{
int status = ogs_time_from_lt(t, tm, tm_usec);
if (status == OGS_OK)
*t -= (ogs_time_t) tm->tm_gmtoff * OGS_USEC_PER_SEC;
return status;
}
/* RFC 5905 A.1.1, A.4
* PFCP entity uses NTP timestamp(1900), but Open5GS uses UNIX(1970).
*
* One is the offset between the two epochs.
* Unix uses an epoch located at 1/1/1970-00:00h (UTC) and
* NTP uses 1/1/1900-00:00h. This leads to an offset equivalent
* to 70 years in seconds (there are 17 leap years
* between the two dates so the offset is
*
* (70*365 + 17)*86400 = 2208988800
*
* to be substracted from NTP time to get Unix struct timeval.
*/
uint32_t ogs_time_ntp32_now(void)
{
int rc;
struct timeval tv;
rc = ogs_gettimeofday(&tv);
ogs_assert(rc == 0);
return ogs_time_to_ntp32(ogs_time_from_sec(tv.tv_sec) + tv.tv_usec);
}
ogs_time_t ogs_time_from_ntp32(uint32_t ntp_timestamp)
{
if (ntp_timestamp < OGS_1970_1900_SEC_DIFF)
return 0;
return ogs_time_from_sec(ntp_timestamp - OGS_1970_1900_SEC_DIFF);
}
uint32_t ogs_time_to_ntp32(ogs_time_t time)
{
return (time / OGS_USEC_PER_SEC) + OGS_1970_1900_SEC_DIFF;
}
int ogs_timezone(void)
{
#if defined(_WIN32)
u_long n;
TIME_ZONE_INFORMATION tz;
n = GetTimeZoneInformation(&tz);
switch (n) {
case TIME_ZONE_ID_UNKNOWN:
/* Bias = UTC - local time in minutes
* tm_gmtoff is seconds east of UTC
*/
return tz.Bias * -60;
case TIME_ZONE_ID_STANDARD:
return (tz.Bias + tz.StandardBias) * -60;
case TIME_ZONE_ID_DAYLIGHT:
return (tz.Bias + tz.DaylightBias) * -60;
default:
ogs_assert_if_reached();
return 0;
}
#else
struct timeval tv;
struct tm tm;
int ret;
ret = ogs_gettimeofday(&tv);
ogs_assert(ret == 0);
ogs_localtime(tv.tv_sec, &tm);
return tm.tm_gmtoff;
#endif
}
ogs_time_t ogs_get_monotonic_time(void)
{
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ogs_time_from_sec(ts.tv_sec) + ts.tv_nsec / 1000UL;
#elif defined(__APPLE__)
static mach_timebase_info_data_t info = {0};
static double ratio = 0.0;
if (!info.denom) {
/* the value from mach_absolute_time () * info.numer / info.denom
* is in nano seconds. So we have to divid by 1000.0 to get micro
* seconds*/
mach_timebase_info(&info);
ratio = (double) info.numer / (double) info.denom / 1000.0;
}
return mach_absolute_time() * ratio;
#elif defined(_WIN32)
/* Despite it's name, this is in milliseconds! */
ogs_time_t ticks = GetTickCount64();
return (ticks * 1000L);
#elif defined(__hpux__)
ogs_time_t nanosec = gethrtime();
return (nanosec / 1000UL);
#else
#warning "Monotonic clock is not yet supported on your platform."
struct timeval tv;
ogs_gettimeofday(&tv);
return ogs_time_from_sec(tv.tv_sec) + tv.tv_usec;
#endif
}
void ogs_localtime(time_t s, struct tm *tm)
{
ogs_assert(tm);
memset(tm, 0, sizeof(*tm));
#if (HAVE_LOCALTIME_R)
(void)localtime_r(&s, tm);
#else
struct tm *t;
t = localtime(&s);
*tm = *t;
#endif
}
void ogs_gmtime(time_t s, struct tm *tm)
{
ogs_assert(tm);
memset(tm, 0, sizeof(*tm));
#if (HAVE_LOCALTIME_R)
(void)gmtime_r(&s, tm);
#else
struct tm *t;
t = gmtime(&s);
*tm = *t;
#endif
}
void ogs_msleep(time_t msec)
{
#if defined(_WIN32)
Sleep(msec);
#else
ogs_usleep(msec * 1000);
#endif
}
void ogs_usleep(time_t usec)
{
#if defined(_WIN32)
Sleep(usec ? (1 + (usec - 1) / 1000) : 0);
#else
struct timespec req, rem;
req.tv_sec = usec / OGS_USEC_PER_SEC;
req.tv_nsec = (usec % OGS_USEC_PER_SEC) * 1000;
while (nanosleep(&req, &rem) == -1 && errno == EINTR)
req = rem;
#endif
}