neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

time.c (6193B)


      1 #include <inttypes.h>
      2 #include <limits.h>
      3 #include <stdbool.h>
      4 #include <string.h>
      5 #include <time.h>
      6 
      7 #include <uv.h>
      8 
      9 #include "auto/config.h"
     10 #include "nvim/event/loop.h"
     11 #include "nvim/event/multiqueue.h"
     12 #include "nvim/gettext_defs.h"
     13 #include "nvim/globals.h"
     14 #include "nvim/log.h"
     15 #include "nvim/main.h"
     16 #include "nvim/memory.h"
     17 #include "nvim/os/input.h"
     18 #include "nvim/os/os.h"
     19 #include "nvim/os/time.h"
     20 
     21 #include "os/time.c.generated.h"
     22 
     23 /// Gets a high-resolution (nanosecond), monotonically-increasing time relative
     24 /// to an arbitrary time in the past.
     25 ///
     26 /// Not related to the time of day and therefore not subject to clock drift.
     27 ///
     28 /// @return Relative time value with nanosecond precision.
     29 uint64_t os_hrtime(void)
     30  FUNC_ATTR_WARN_UNUSED_RESULT
     31 {
     32  return uv_hrtime();
     33 }
     34 
     35 /// Gets a millisecond-resolution, monotonically-increasing time relative to an
     36 /// arbitrary time in the past.
     37 ///
     38 /// Not related to the time of day and therefore not subject to clock drift.
     39 /// The value is cached by the loop, it will not change until the next
     40 /// loop-tick (unless uv_update_time is called).
     41 ///
     42 /// @return Relative time value with millisecond precision.
     43 uint64_t os_now(void)
     44  FUNC_ATTR_WARN_UNUSED_RESULT
     45 {
     46  return uv_now(&main_loop.uv);
     47 }
     48 
     49 /// Sleeps for `ms` milliseconds.
     50 ///
     51 /// @see uv_sleep() (libuv v1.34.0)
     52 ///
     53 /// @param ms          Number of milliseconds to sleep
     54 /// @param ignoreinput If true, only SIGINT (CTRL-C) can interrupt.
     55 void os_delay(uint64_t ms, bool ignoreinput)
     56 {
     57  DLOG("%" PRIu64 " ms", ms);
     58  if (ms > INT_MAX) {
     59    ms = INT_MAX;
     60  }
     61  LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)ms,
     62                            ignoreinput ? got_int : os_input_ready(NULL));
     63 }
     64 
     65 /// Sleeps for `ms` milliseconds without checking for events or interrupts.
     66 ///
     67 /// This blocks even "fast" events which is quite disruptive. This should only
     68 /// be used in debug code. Prefer os_delay() and decide if the delay should be
     69 /// interrupted by input or only a CTRL-C.
     70 ///
     71 /// @see uv_sleep() (libuv v1.34.0)
     72 ///
     73 /// @param us          Number of microseconds to sleep.
     74 void os_sleep(uint64_t ms)
     75 {
     76  if (ms > UINT_MAX) {
     77    ms = UINT_MAX;
     78  }
     79  uv_sleep((unsigned)ms);
     80 }
     81 
     82 // Cache of the current timezone name as retrieved from TZ, or an empty string
     83 // where unset, up to 64 octets long including trailing null byte.
     84 static char tz_cache[64];
     85 
     86 /// Portable version of POSIX localtime_r()
     87 ///
     88 /// @return NULL in case of error
     89 struct tm *os_localtime_r(const time_t *restrict clock,
     90                          struct tm *restrict result) FUNC_ATTR_NONNULL_ALL
     91 {
     92 #ifdef UNIX
     93  // POSIX provides localtime_r() as a thread-safe version of localtime().
     94  //
     95  // Check to see if the environment variable TZ has changed since the last run.
     96  // Call tzset(3) to update the global timezone variables if it has.
     97  // POSIX standard doesn't require localtime_r() implementations to do that
     98  // as it does with localtime(), and we don't want to call tzset() every time.
     99  const char *tz = os_getenv_noalloc("TZ");
    100  if (!tz) {
    101    tz = "";
    102  }
    103  if (strncmp(tz_cache, tz, sizeof(tz_cache) - 1) != 0) {
    104    tzset();
    105    xstrlcpy(tz_cache, tz, sizeof(tz_cache));
    106  }
    107  return localtime_r(clock, result);  // NOLINT(runtime/threadsafe_fn)
    108 #else
    109  // Windows version of localtime() is thread-safe.
    110  // See http://msdn.microsoft.com/en-us/library/bf12f0hc%28VS.80%29.aspx
    111  struct tm *local_time = localtime(clock);  // NOLINT(runtime/threadsafe_fn)
    112  if (!local_time) {
    113    return NULL;
    114  }
    115  *result = *local_time;
    116  return result;
    117 #endif
    118 }
    119 
    120 /// Gets the current Unix timestamp and adjusts it to local time.
    121 ///
    122 /// @param result Pointer to a 'struct tm' where the result should be placed
    123 /// @return A pointer to a 'struct tm' in the current time zone (the 'result'
    124 ///         argument) or NULL in case of error
    125 struct tm *os_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL
    126 {
    127  time_t rawtime = time(NULL);
    128  return os_localtime_r(&rawtime, result);
    129 }
    130 
    131 /// Portable version of POSIX ctime_r()
    132 ///
    133 /// @param clock[in]
    134 /// @param result[out] Pointer to a 'char' where the result should be placed
    135 /// @param result_len length of result buffer
    136 /// @return human-readable string of current local time
    137 char *os_ctime_r(const time_t *restrict clock, char *restrict result, size_t result_len,
    138                 bool add_newline)
    139  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
    140 {
    141  struct tm clock_local;
    142  struct tm *clock_local_ptr = os_localtime_r(clock, &clock_local);
    143  // MSVC returns NULL for an invalid value of seconds.
    144  if (clock_local_ptr == NULL) {
    145    xstrlcpy(result, _("(Invalid)"), result_len - 1);
    146  } else {
    147    // xgettext:no-c-format
    148    if (strftime(result, result_len - 1, _("%a %b %d %H:%M:%S %Y"), clock_local_ptr) == 0) {
    149      // Quoting "man strftime":
    150      // > If the length of the result string (including the terminating
    151      // > null byte) would exceed max bytes, then strftime() returns 0,
    152      // > and the contents of the array are undefined.
    153      xstrlcpy(result, _("(Invalid)"), result_len - 1);
    154    }
    155  }
    156  if (add_newline) {
    157    xstrlcat(result, "\n", result_len);
    158  }
    159  return result;
    160 }
    161 
    162 /// Gets the current Unix timestamp and adjusts it to local time.
    163 ///
    164 /// @param result[out] Pointer to a 'char' where the result should be placed
    165 /// @param result_len length of result buffer
    166 /// @return human-readable string of current local time
    167 char *os_ctime(char *result, size_t result_len, bool add_newline)
    168  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
    169 {
    170  time_t rawtime = time(NULL);
    171  return os_ctime_r(&rawtime, result, result_len, add_newline);
    172 }
    173 
    174 /// Portable version of POSIX strptime()
    175 ///
    176 /// @param str[in]  string to convert
    177 /// @param format[in]  format to parse "str"
    178 /// @param tm[out]  time representation of "str"
    179 /// @return Pointer to first unprocessed character or NULL
    180 char *os_strptime(const char *str, const char *format, struct tm *tm)
    181  FUNC_ATTR_NONNULL_ALL
    182 {
    183 #ifdef HAVE_STRPTIME
    184  return strptime(str, format, tm);
    185 #else
    186  return NULL;
    187 #endif
    188 }
    189 
    190 /// Obtains the current Unix timestamp.
    191 ///
    192 /// @return Seconds since epoch.
    193 Timestamp os_time(void)
    194  FUNC_ATTR_WARN_UNUSED_RESULT
    195 {
    196  return (Timestamp)time(NULL);
    197 }