aboutsummaryrefslogtreecommitdiff
path: root/libbutl/timestamp.mxx
blob: 76c7978fda10468c9c9489838411aa6788290b99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// file      : libbutl/timestamp.mxx -*- C++ -*-
// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef __cpp_modules
#pragma once
#endif

// C includes.

#ifndef __cpp_lib_modules
#include <iosfwd>
#include <string>
#include <chrono>
#endif

// Other includes.

#ifdef __cpp_modules
export module butl.timestamp;
#ifdef __cpp_lib_modules
import std.core;
import std.io;
#endif
#endif

//@@ MOD TODO: should't we re-export chrono (for somparison operator, etc)?
//   or ADL should kick in?

#include <libbutl/export.hxx>

LIBBUTL_MODEXPORT namespace butl
{
  // On all three main platforms that we target (GNU/Linux, Windows (both
  // VC++ and GCC/MinGW64), and MacOS X) with recent C++ runtimes,
  // system_clock has nanoseconds resolution and counts from the UNIX
  // epoch. The latter is important since struct stat also returns times
  // based on UNIX epoch.
  //
  // The underlying type for nanoseconds duration is signed integer type
  // of at least 64 bits (currently int64_t, available as duration::rep).
  // Because it is signed, we will overflow in year 2262 but by then the
  // underlying type will most likely have changed to something larger
  // than 64-bit.
  //
  // So to support other platforms that could possibly use a different
  // system_clock resolutions (e.g., microseconds), we actually not going
  // to assume anywhere (except perhaps timestamp.cxx) that we are dealing
  // with nanoseconds or the 64-bit underlying type.
  //
  using std::chrono::system_clock;

  // Note that the default-initialized timestamp has the timestamp_nonexistent
  // value.
  //
  using timestamp = system_clock::time_point;
  using duration = system_clock::duration;

  // Generally-useful special values.
  //
  // Note that unknown is less than nonexistent which in turn is less than
  // unreal and all of them are less than any non-special value (strictly
  // speaking unreal is no greater (older) than any real value).
  //
#if defined(__cpp_modules) && defined(__clang__) //@@ MOD Clang duplicate sym.
  inline const timestamp::rep timestamp_unknown_rep     = -1;
  inline const timestamp      timestamp_unknown         = timestamp (duration (-1));
  inline const timestamp::rep timestamp_nonexistent_rep = 0;
  inline const timestamp      timestamp_nonexistent     = timestamp (duration (0));
  inline const timestamp::rep timestamp_unreal_rep      = 1;
  inline const timestamp      timestamp_unreal          = timestamp (duration (1));
#else
  const timestamp::rep timestamp_unknown_rep     = -1;
  const timestamp      timestamp_unknown         = timestamp (duration (-1));
  const timestamp::rep timestamp_nonexistent_rep = 0;
  const timestamp      timestamp_nonexistent     = timestamp (duration (0));
  const timestamp::rep timestamp_unreal_rep      = 1;
  const timestamp      timestamp_unreal          = timestamp (duration (1));
#endif

  // Print human-readable representation of the timestamp.
  //
  // By default the timestamp is printed by localtime_r() in the local
  // timezone, so tzset() from <time.h> should be called prior to using the
  // corresponding operator or the to_stream() function (normally from main()
  // or equivalent).
  //
  // The format argument in the to_stream() function is the put_time() format
  // string except that it also supports the nanoseconds conversion specifier
  // in the form %[<d>N] where <d> is the optional single delimiter character,
  // for example '.'. If the nanoseconds part is 0, then it is not printed
  // (nor the delimiter character). Otherwise, if necessary, the nanoseconds
  // part is padded to 9 characters with leading zeros.
  //
  // The special argument in the to_stream() function indicates whether the
  // special timestamp_{unknown,nonexistent,unreal} values should be printed
  // as '<unknown>', '<nonexistent>', and '<unreal>', respectively.
  //
  // The local argument in the to_stream() function indicates whether to use
  // localtime_r() or gmtime_r().
  //
  // Note also that these operators/function may throw std::system_error.
  //
  // Finally, padding is not fully supported by these operators/function. They
  // throw runtime_error if nanoseconds conversion specifier is present and
  // the stream's width field has been set to non-zero value before the call.
  //
  // Potential improvements:
  //  - add flag to to_stream() to use
  //  - support %[<d>U] (microseconds) and %[<d>M] (milliseconds).
  //  - make to_stream() a manipulator, similar to put_time()
  //  - support %(N) version for non-optional printing
  //  - support for suffix %[<d>N<s>], for example %[N nsec]
  //
  LIBBUTL_SYMEXPORT std::ostream&
  to_stream (std::ostream&,
             const timestamp&,
             const char* format,
             bool special,
             bool local);

  // Same as above, but provide the result as a string. Note that it is
  // implemented via to_stream() and std::ostringstream.
  //
  LIBBUTL_SYMEXPORT std::string
  to_string (const timestamp&,
             const char* format,
             bool special,
             bool local);

  inline std::ostream&
  operator<< (std::ostream& os, const timestamp& ts)
  {
    return to_stream (os, ts, "%Y-%m-%d %H:%M:%S%[.N]", true, true);
  }

  // Print human-readable representation of the duration.
  //
  LIBBUTL_SYMEXPORT std::ostream&
  to_stream (std::ostream&, const duration&, bool nanoseconds);

  // Same as above, but provide the result as a string. Note that it is
  // implemented via to_stream() and std::ostringstream.
  //
  LIBBUTL_SYMEXPORT std::string
  to_string (const duration&, bool nanoseconds);

  inline std::ostream&
  operator<< (std::ostream& os, const duration& d)
  {
    return to_stream (os, d, true);
  }

  // Parse human-readable representation of the timestamp.
  //
  // The format argument is the strptime() format string except that it also
  // supports the fraction of a second specifier in the form %[<d><f>], where
  // <d> is the optional single delimiter character, for example '.', and <f>
  // is one of the 'N', 'U', 'M' characters, denoting nanoseconds,
  // microseconds and milliseconds, respectively.
  //
  // The delimiter <d> is mandatory. If no such character is encountered at
  // the corresponding position of the input string, the function behaves as
  // if no %[] specifier were provided. Only single %[] specifier in the
  // format string is currently supported.
  //
  // If the delimiter is present, then it should be followed by 9 (N), 6 (U),
  // or 3 (M) digit value padded with leading zeros if necessary.
  //
  // If the local argument is true, then the input is assume to be local time
  // and the result is returned as local time as well. Otherwise, UCT is used
  // in both cases.
  //
  // If the end argument is not NULL, then it points to the first character
  // that was not parsed. Otherwise, throw invalid_argument in case of any
  // unparsed characters.
  //
  // Throw std::system_error on input/format mismatch and underlying time
  // conversion function failures.
  //
  // Note that internally from_string() calls strptime(), which behaves
  // according to the process' C locale (set with std::setlocale()) and not
  // the C++ locale (set with std::locale::global()). However the behaviour
  // can be affected by std::locale::global() as well, as it itself calls
  // std::setlocale() for the locale with a name.
  //
  // Potential improvements:
  //   - support %() version for non-optional component but with optional
  //     delimiter
  //   - ability to parse local, return UTC and vice-versa
  //   - handle timezone parsing
  //
  LIBBUTL_SYMEXPORT timestamp
  from_string (const char* input,
               const char* format,
               bool local,
               const char** end = nullptr);
}