nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* GLIB - Library of useful routines for C programming |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
||
3 | * |
||
4 | * This library is free software; you can redistribute it and/or |
||
5 | * modify it under the terms of the GNU Lesser General Public |
||
6 | * License as published by the Free Software Foundation; either |
||
7 | * version 2 of the License, or (at your option) any later version. |
||
8 | * |
||
9 | * This library is distributed in the hope that it will be useful, |
||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
12 | * Lesser General Public License for more details. |
||
13 | * |
||
14 | * You should have received a copy of the GNU Lesser General Public |
||
15 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
16 | */ |
||
17 | |||
18 | /* |
||
19 | * Modified by the GLib Team and others 1997-2000. See the AUTHORS |
||
20 | * file for a list of people on the GLib Team. See the ChangeLog |
||
21 | * files for a list of changes. These files are distributed with |
||
22 | * GLib at ftp://ftp.gtk.org/pub/gtk/. |
||
23 | */ |
||
24 | |||
25 | /* |
||
26 | * MT safe |
||
27 | */ |
||
28 | |||
29 | #include "config.h" |
||
30 | #include "glibconfig.h" |
||
31 | |||
32 | #include <stdlib.h> |
||
33 | |||
34 | #ifdef G_OS_UNIX |
||
35 | #include <unistd.h> |
||
36 | #endif /* G_OS_UNIX */ |
||
37 | |||
38 | #ifdef HAVE_SYS_TIME_H |
||
39 | #include <sys/time.h> |
||
40 | #endif |
||
41 | #include <time.h> |
||
42 | #ifndef G_OS_WIN32 |
||
43 | #include <errno.h> |
||
44 | #endif /* G_OS_WIN32 */ |
||
45 | |||
46 | #ifdef G_OS_WIN32 |
||
47 | #include <windows.h> |
||
48 | #endif /* G_OS_WIN32 */ |
||
49 | |||
50 | #include "gtimer.h" |
||
51 | |||
52 | #include "gmem.h" |
||
53 | #include "gstrfuncs.h" |
||
54 | #include "gtestutils.h" |
||
55 | #include "gmain.h" |
||
56 | |||
57 | /** |
||
58 | * SECTION:timers |
||
59 | * @title: Timers |
||
60 | * @short_description: keep track of elapsed time |
||
61 | * |
||
62 | * #GTimer records a start time, and counts microseconds elapsed since |
||
63 | * that time. This is done somewhat differently on different platforms, |
||
64 | * and can be tricky to get exactly right, so #GTimer provides a |
||
65 | * portable/convenient interface. |
||
66 | **/ |
||
67 | |||
68 | /** |
||
69 | * GTimer: |
||
70 | * |
||
71 | * Opaque datatype that records a start time. |
||
72 | **/ |
||
73 | struct _GTimer |
||
74 | { |
||
75 | guint64 start; |
||
76 | guint64 end; |
||
77 | |||
78 | guint active : 1; |
||
79 | }; |
||
80 | |||
81 | /** |
||
82 | * g_timer_new: |
||
83 | * |
||
84 | * Creates a new timer, and starts timing (i.e. g_timer_start() is |
||
85 | * implicitly called for you). |
||
86 | * |
||
87 | * Returns: a new #GTimer. |
||
88 | **/ |
||
89 | GTimer* |
||
90 | g_timer_new (void) |
||
91 | { |
||
92 | GTimer *timer; |
||
93 | |||
94 | timer = g_new (GTimer, 1); |
||
95 | timer->active = TRUE; |
||
96 | |||
97 | timer->start = g_get_monotonic_time (); |
||
98 | |||
99 | return timer; |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * g_timer_destroy: |
||
104 | * @timer: a #GTimer to destroy. |
||
105 | * |
||
106 | * Destroys a timer, freeing associated resources. |
||
107 | **/ |
||
108 | void |
||
109 | g_timer_destroy (GTimer *timer) |
||
110 | { |
||
111 | g_return_if_fail (timer != NULL); |
||
112 | |||
113 | g_free (timer); |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * g_timer_start: |
||
118 | * @timer: a #GTimer. |
||
119 | * |
||
120 | * Marks a start time, so that future calls to g_timer_elapsed() will |
||
121 | * report the time since g_timer_start() was called. g_timer_new() |
||
122 | * automatically marks the start time, so no need to call |
||
123 | * g_timer_start() immediately after creating the timer. |
||
124 | **/ |
||
125 | void |
||
126 | g_timer_start (GTimer *timer) |
||
127 | { |
||
128 | g_return_if_fail (timer != NULL); |
||
129 | |||
130 | timer->active = TRUE; |
||
131 | |||
132 | timer->start = g_get_monotonic_time (); |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * g_timer_stop: |
||
137 | * @timer: a #GTimer. |
||
138 | * |
||
139 | * Marks an end time, so calls to g_timer_elapsed() will return the |
||
140 | * difference between this end time and the start time. |
||
141 | **/ |
||
142 | void |
||
143 | g_timer_stop (GTimer *timer) |
||
144 | { |
||
145 | g_return_if_fail (timer != NULL); |
||
146 | |||
147 | timer->active = FALSE; |
||
148 | |||
149 | timer->end = g_get_monotonic_time (); |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * g_timer_reset: |
||
154 | * @timer: a #GTimer. |
||
155 | * |
||
156 | * This function is useless; it's fine to call g_timer_start() on an |
||
157 | * already-started timer to reset the start time, so g_timer_reset() |
||
158 | * serves no purpose. |
||
159 | **/ |
||
160 | void |
||
161 | g_timer_reset (GTimer *timer) |
||
162 | { |
||
163 | g_return_if_fail (timer != NULL); |
||
164 | |||
165 | timer->start = g_get_monotonic_time (); |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * g_timer_continue: |
||
170 | * @timer: a #GTimer. |
||
171 | * |
||
172 | * Resumes a timer that has previously been stopped with |
||
173 | * g_timer_stop(). g_timer_stop() must be called before using this |
||
174 | * function. |
||
175 | * |
||
176 | * Since: 2.4 |
||
177 | **/ |
||
178 | void |
||
179 | g_timer_continue (GTimer *timer) |
||
180 | { |
||
181 | guint64 elapsed; |
||
182 | |||
183 | g_return_if_fail (timer != NULL); |
||
184 | g_return_if_fail (timer->active == FALSE); |
||
185 | |||
186 | /* Get elapsed time and reset timer start time |
||
187 | * to the current time minus the previously |
||
188 | * elapsed interval. |
||
189 | */ |
||
190 | |||
191 | elapsed = timer->end - timer->start; |
||
192 | |||
193 | timer->start = g_get_monotonic_time (); |
||
194 | |||
195 | timer->start -= elapsed; |
||
196 | |||
197 | timer->active = TRUE; |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * g_timer_elapsed: |
||
202 | * @timer: a #GTimer. |
||
203 | * @microseconds: return location for the fractional part of seconds |
||
204 | * elapsed, in microseconds (that is, the total number |
||
205 | * of microseconds elapsed, modulo 1000000), or %NULL |
||
206 | * |
||
207 | * If @timer has been started but not stopped, obtains the time since |
||
208 | * the timer was started. If @timer has been stopped, obtains the |
||
209 | * elapsed time between the time it was started and the time it was |
||
210 | * stopped. The return value is the number of seconds elapsed, |
||
211 | * including any fractional part. The @microseconds out parameter is |
||
212 | * essentially useless. |
||
213 | * |
||
214 | * Returns: seconds elapsed as a floating point value, including any |
||
215 | * fractional part. |
||
216 | **/ |
||
217 | gdouble |
||
218 | g_timer_elapsed (GTimer *timer, |
||
219 | gulong *microseconds) |
||
220 | { |
||
221 | gdouble total; |
||
222 | gint64 elapsed; |
||
223 | |||
224 | g_return_val_if_fail (timer != NULL, 0); |
||
225 | |||
226 | if (timer->active) |
||
227 | timer->end = g_get_monotonic_time (); |
||
228 | |||
229 | elapsed = timer->end - timer->start; |
||
230 | |||
231 | total = elapsed / 1e6; |
||
232 | |||
233 | if (microseconds) |
||
234 | *microseconds = elapsed % 1000000; |
||
235 | |||
236 | return total; |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * g_usleep: |
||
241 | * @microseconds: number of microseconds to pause |
||
242 | * |
||
243 | * Pauses the current thread for the given number of microseconds. |
||
244 | * |
||
245 | * There are 1 million microseconds per second (represented by the |
||
246 | * #G_USEC_PER_SEC macro). g_usleep() may have limited precision, |
||
247 | * depending on hardware and operating system; don't rely on the exact |
||
248 | * length of the sleep. |
||
249 | */ |
||
250 | void |
||
251 | g_usleep (gulong microseconds) |
||
252 | { |
||
253 | #ifdef G_OS_WIN32 |
||
254 | Sleep (microseconds / 1000); |
||
255 | #else |
||
256 | struct timespec request, remaining; |
||
257 | request.tv_sec = microseconds / G_USEC_PER_SEC; |
||
258 | request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC); |
||
259 | while (nanosleep (&request, &remaining) == -1 && errno == EINTR) |
||
260 | request = remaining; |
||
261 | #endif |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * g_time_val_add: |
||
266 | * @time_: a #GTimeVal |
||
267 | * @microseconds: number of microseconds to add to @time |
||
268 | * |
||
269 | * Adds the given number of microseconds to @time_. @microseconds can |
||
270 | * also be negative to decrease the value of @time_. |
||
271 | **/ |
||
272 | void |
||
273 | g_time_val_add (GTimeVal *time_, glong microseconds) |
||
274 | { |
||
275 | g_return_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC); |
||
276 | |||
277 | if (microseconds >= 0) |
||
278 | { |
||
279 | time_->tv_usec += microseconds % G_USEC_PER_SEC; |
||
280 | time_->tv_sec += microseconds / G_USEC_PER_SEC; |
||
281 | if (time_->tv_usec >= G_USEC_PER_SEC) |
||
282 | { |
||
283 | time_->tv_usec -= G_USEC_PER_SEC; |
||
284 | time_->tv_sec++; |
||
285 | } |
||
286 | } |
||
287 | else |
||
288 | { |
||
289 | microseconds *= -1; |
||
290 | time_->tv_usec -= microseconds % G_USEC_PER_SEC; |
||
291 | time_->tv_sec -= microseconds / G_USEC_PER_SEC; |
||
292 | if (time_->tv_usec < 0) |
||
293 | { |
||
294 | time_->tv_usec += G_USEC_PER_SEC; |
||
295 | time_->tv_sec--; |
||
296 | } |
||
297 | } |
||
298 | } |
||
299 | |||
300 | /* converts a broken down date representation, relative to UTC, |
||
301 | * to a timestamp; it uses timegm() if it's available. |
||
302 | */ |
||
303 | static time_t |
||
304 | mktime_utc (struct tm *tm) |
||
305 | { |
||
306 | time_t retval; |
||
307 | |||
308 | #ifndef HAVE_TIMEGM |
||
309 | static const gint days_before[] = |
||
310 | { |
||
311 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 |
||
312 | }; |
||
313 | #endif |
||
314 | |||
315 | #ifndef HAVE_TIMEGM |
||
316 | if (tm->tm_mon < 0 || tm->tm_mon > 11) |
||
317 | return (time_t) -1; |
||
318 | |||
319 | retval = (tm->tm_year - 70) * 365; |
||
320 | retval += (tm->tm_year - 68) / 4; |
||
321 | retval += days_before[tm->tm_mon] + tm->tm_mday - 1; |
||
322 | |||
323 | if (tm->tm_year % 4 == 0 && tm->tm_mon < 2) |
||
324 | retval -= 1; |
||
325 | |||
326 | retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec; |
||
327 | #else |
||
328 | retval = timegm (tm); |
||
329 | #endif /* !HAVE_TIMEGM */ |
||
330 | |||
331 | return retval; |
||
332 | } |
||
333 | |||
334 | /** |
||
335 | * g_time_val_from_iso8601: |
||
336 | * @iso_date: an ISO 8601 encoded date string |
||
337 | * @time_: (out): a #GTimeVal |
||
338 | * |
||
339 | * Converts a string containing an ISO 8601 encoded date and time |
||
340 | * to a #GTimeVal and puts it into @time_. |
||
341 | * |
||
342 | * @iso_date must include year, month, day, hours, minutes, and |
||
343 | * seconds. It can optionally include fractions of a second and a time |
||
344 | * zone indicator. (In the absence of any time zone indication, the |
||
345 | * timestamp is assumed to be in local time.) |
||
346 | * |
||
347 | * Returns: %TRUE if the conversion was successful. |
||
348 | * |
||
349 | * Since: 2.12 |
||
350 | */ |
||
351 | gboolean |
||
352 | g_time_val_from_iso8601 (const gchar *iso_date, |
||
353 | GTimeVal *time_) |
||
354 | { |
||
355 | struct tm tm = {0}; |
||
356 | long val; |
||
357 | |||
358 | g_return_val_if_fail (iso_date != NULL, FALSE); |
||
359 | g_return_val_if_fail (time_ != NULL, FALSE); |
||
360 | |||
361 | /* Ensure that the first character is a digit, the first digit |
||
362 | * of the date, otherwise we don't have an ISO 8601 date |
||
363 | */ |
||
364 | while (g_ascii_isspace (*iso_date)) |
||
365 | iso_date++; |
||
366 | |||
367 | if (*iso_date == '\0') |
||
368 | return FALSE; |
||
369 | |||
370 | if (!g_ascii_isdigit (*iso_date) && *iso_date != '-' && *iso_date != '+') |
||
371 | return FALSE; |
||
372 | |||
373 | val = strtoul (iso_date, (char **)&iso_date, 10); |
||
374 | if (*iso_date == '-') |
||
375 | { |
||
376 | /* YYYY-MM-DD */ |
||
377 | tm.tm_year = val - 1900; |
||
378 | iso_date++; |
||
379 | tm.tm_mon = strtoul (iso_date, (char **)&iso_date, 10) - 1; |
||
380 | |||
381 | if (*iso_date++ != '-') |
||
382 | return FALSE; |
||
383 | |||
384 | tm.tm_mday = strtoul (iso_date, (char **)&iso_date, 10); |
||
385 | } |
||
386 | else |
||
387 | { |
||
388 | /* YYYYMMDD */ |
||
389 | tm.tm_mday = val % 100; |
||
390 | tm.tm_mon = (val % 10000) / 100 - 1; |
||
391 | tm.tm_year = val / 10000 - 1900; |
||
392 | } |
||
393 | |||
394 | if (*iso_date != 'T') |
||
395 | return FALSE; |
||
396 | |||
397 | iso_date++; |
||
398 | |||
399 | /* If there is a 'T' then there has to be a time */ |
||
400 | if (!g_ascii_isdigit (*iso_date)) |
||
401 | return FALSE; |
||
402 | |||
403 | val = strtoul (iso_date, (char **)&iso_date, 10); |
||
404 | if (*iso_date == ':') |
||
405 | { |
||
406 | /* hh:mm:ss */ |
||
407 | tm.tm_hour = val; |
||
408 | iso_date++; |
||
409 | tm.tm_min = strtoul (iso_date, (char **)&iso_date, 10); |
||
410 | |||
411 | if (*iso_date++ != ':') |
||
412 | return FALSE; |
||
413 | |||
414 | tm.tm_sec = strtoul (iso_date, (char **)&iso_date, 10); |
||
415 | } |
||
416 | else |
||
417 | { |
||
418 | /* hhmmss */ |
||
419 | tm.tm_sec = val % 100; |
||
420 | tm.tm_min = (val % 10000) / 100; |
||
421 | tm.tm_hour = val / 10000; |
||
422 | } |
||
423 | |||
424 | time_->tv_usec = 0; |
||
425 | |||
426 | if (*iso_date == ',' || *iso_date == '.') |
||
427 | { |
||
428 | glong mul = 100000; |
||
429 | |||
430 | while (g_ascii_isdigit (*++iso_date)) |
||
431 | { |
||
432 | time_->tv_usec += (*iso_date - '0') * mul; |
||
433 | mul /= 10; |
||
434 | } |
||
435 | } |
||
436 | |||
437 | /* Now parse the offset and convert tm to a time_t */ |
||
438 | if (*iso_date == 'Z') |
||
439 | { |
||
440 | iso_date++; |
||
441 | time_->tv_sec = mktime_utc (&tm); |
||
442 | } |
||
443 | else if (*iso_date == '+' || *iso_date == '-') |
||
444 | { |
||
445 | gint sign = (*iso_date == '+') ? -1 : 1; |
||
446 | |||
447 | val = strtoul (iso_date + 1, (char **)&iso_date, 10); |
||
448 | |||
449 | if (*iso_date == ':') |
||
450 | val = 60 * val + strtoul (iso_date + 1, (char **)&iso_date, 10); |
||
451 | else |
||
452 | val = 60 * (val / 100) + (val % 100); |
||
453 | |||
454 | time_->tv_sec = mktime_utc (&tm) + (time_t) (60 * val * sign); |
||
455 | } |
||
456 | else |
||
457 | { |
||
458 | /* No "Z" or offset, so local time */ |
||
459 | tm.tm_isdst = -1; /* locale selects DST */ |
||
460 | time_->tv_sec = mktime (&tm); |
||
461 | } |
||
462 | |||
463 | while (g_ascii_isspace (*iso_date)) |
||
464 | iso_date++; |
||
465 | |||
466 | return *iso_date == '\0'; |
||
467 | } |
||
468 | |||
469 | /** |
||
470 | * g_time_val_to_iso8601: |
||
471 | * @time_: a #GTimeVal |
||
472 | * |
||
473 | * Converts @time_ into an RFC 3339 encoded string, relative to the |
||
474 | * Coordinated Universal Time (UTC). This is one of the many formats |
||
475 | * allowed by ISO 8601. |
||
476 | * |
||
477 | * ISO 8601 allows a large number of date/time formats, with or without |
||
478 | * punctuation and optional elements. The format returned by this function |
||
479 | * is a complete date and time, with optional punctuation included, the |
||
480 | * UTC time zone represented as "Z", and the @tv_usec part included if |
||
481 | * and only if it is nonzero, i.e. either |
||
482 | * "YYYY-MM-DDTHH:MM:SSZ" or "YYYY-MM-DDTHH:MM:SS.fffffZ". |
||
483 | * |
||
484 | * This corresponds to the Internet date/time format defined by |
||
485 | * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt), |
||
486 | * and to either of the two most-precise formats defined by |
||
487 | * the W3C Note |
||
488 | * [Date and Time Formats](http://www.w3.org/TR/NOTE-datetime-19980827). |
||
489 | * Both of these documents are profiles of ISO 8601. |
||
490 | * |
||
491 | * Use g_date_time_format() or g_strdup_printf() if a different |
||
492 | * variation of ISO 8601 format is required. |
||
493 | * |
||
494 | * Returns: a newly allocated string containing an ISO 8601 date |
||
495 | * |
||
496 | * Since: 2.12 |
||
497 | */ |
||
498 | gchar * |
||
499 | g_time_val_to_iso8601 (GTimeVal *time_) |
||
500 | { |
||
501 | gchar *retval; |
||
502 | struct tm *tm; |
||
503 | #ifdef HAVE_GMTIME_R |
||
504 | struct tm tm_; |
||
505 | #endif |
||
506 | time_t secs; |
||
507 | |||
508 | g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL); |
||
509 | |||
510 | secs = time_->tv_sec; |
||
511 | #ifdef _WIN32 |
||
512 | tm = gmtime (&secs); |
||
513 | #else |
||
514 | #ifdef HAVE_GMTIME_R |
||
515 | tm = gmtime_r (&secs, &tm_); |
||
516 | #else |
||
517 | tm = gmtime (&secs); |
||
518 | #endif |
||
519 | #endif |
||
520 | |||
521 | if (time_->tv_usec != 0) |
||
522 | { |
||
523 | /* ISO 8601 date and time format, with fractionary seconds: |
||
524 | * YYYY-MM-DDTHH:MM:SS.MMMMMMZ |
||
525 | */ |
||
526 | retval = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ", |
||
527 | tm->tm_year + 1900, |
||
528 | tm->tm_mon + 1, |
||
529 | tm->tm_mday, |
||
530 | tm->tm_hour, |
||
531 | tm->tm_min, |
||
532 | tm->tm_sec, |
||
533 | time_->tv_usec); |
||
534 | } |
||
535 | else |
||
536 | { |
||
537 | /* ISO 8601 date and time format: |
||
538 | * YYYY-MM-DDTHH:MM:SSZ |
||
539 | */ |
||
540 | retval = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02dZ", |
||
541 | tm->tm_year + 1900, |
||
542 | tm->tm_mon + 1, |
||
543 | tm->tm_mday, |
||
544 | tm->tm_hour, |
||
545 | tm->tm_min, |
||
546 | tm->tm_sec); |
||
547 | } |
||
548 | |||
549 | return retval; |
||
550 | } |