source: mainline/uspace/lib/c/generic/time.c@ df956b9b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since df956b9b was 3e6a98c5, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Standards-compliant boolean type.

  • Property mode set to 100644
File size: 23.1 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
3 * Copyright (c) 2011 Petr Koupy
4 * Copyright (c) 2011 Jiri Zarevucky
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup libc
32 * @{
33 */
34/** @file
35 */
36
37#include <sys/time.h>
38#include <time.h>
39#include <stdbool.h>
40#include <libarch/barrier.h>
41#include <macros.h>
42#include <errno.h>
43#include <sysinfo.h>
44#include <as.h>
45#include <ddi.h>
46#include <libc.h>
47#include <stdint.h>
48#include <stdio.h>
49#include <ctype.h>
50#include <assert.h>
51#include <unistd.h>
52#include <loc.h>
53#include <device/clock_dev.h>
54#include <malloc.h>
55
56#define ASCTIME_BUF_LEN 26
57
58/** Pointer to kernel shared variables with time */
59struct {
60 volatile sysarg_t seconds1;
61 volatile sysarg_t useconds;
62 volatile sysarg_t seconds2;
63} *ktime = NULL;
64
65/* Helper functions ***********************************************************/
66
67#define HOURS_PER_DAY (24)
68#define MINS_PER_HOUR (60)
69#define SECS_PER_MIN (60)
70#define MINS_PER_DAY (MINS_PER_HOUR * HOURS_PER_DAY)
71#define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR)
72#define SECS_PER_DAY (SECS_PER_HOUR * HOURS_PER_DAY)
73
74/**
75 * Checks whether the year is a leap year.
76 *
77 * @param year Year since 1900 (e.g. for 1970, the value is 70).
78 * @return true if year is a leap year, false otherwise
79 */
80static bool _is_leap_year(time_t year)
81{
82 year += 1900;
83
84 if (year % 400 == 0)
85 return true;
86 if (year % 100 == 0)
87 return false;
88 if (year % 4 == 0)
89 return true;
90 return false;
91}
92
93/**
94 * Returns how many days there are in the given month of the given year.
95 * Note that year is only taken into account if month is February.
96 *
97 * @param year Year since 1900 (can be negative).
98 * @param mon Month of the year. 0 for January, 11 for December.
99 * @return Number of days in the specified month.
100 */
101static int _days_in_month(time_t year, time_t mon)
102{
103 assert(mon >= 0 && mon <= 11);
104
105 static int month_days[] =
106 { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
107
108 if (mon == 1) {
109 year += 1900;
110 /* february */
111 return _is_leap_year(year) ? 29 : 28;
112 } else {
113 return month_days[mon];
114 }
115}
116
117/**
118 * For specified year, month and day of month, returns which day of that year
119 * it is.
120 *
121 * For example, given date 2011-01-03, the corresponding expression is:
122 * _day_of_year(111, 0, 3) == 2
123 *
124 * @param year Year (year 1900 = 0, can be negative).
125 * @param mon Month (January = 0).
126 * @param mday Day of month (First day is 1).
127 * @return Day of year (First day is 0).
128 */
129static int _day_of_year(time_t year, time_t mon, time_t mday)
130{
131 static int mdays[] =
132 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
133 static int leap_mdays[] =
134 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
135
136 return (_is_leap_year(year) ? leap_mdays[mon] : mdays[mon]) + mday - 1;
137}
138
139/**
140 * Integer division that rounds to negative infinity.
141 * Used by some functions in this file.
142 *
143 * @param op1 Dividend.
144 * @param op2 Divisor.
145 * @return Rounded quotient.
146 */
147static time_t _floor_div(time_t op1, time_t op2)
148{
149 if (op1 >= 0 || op1 % op2 == 0) {
150 return op1 / op2;
151 } else {
152 return op1 / op2 - 1;
153 }
154}
155
156/**
157 * Modulo that rounds to negative infinity.
158 * Used by some functions in this file.
159 *
160 * @param op1 Dividend.
161 * @param op2 Divisor.
162 * @return Remainder.
163 */
164static time_t _floor_mod(time_t op1, time_t op2)
165{
166 int div = _floor_div(op1, op2);
167
168 /* (a / b) * b + a % b == a */
169 /* thus, a % b == a - (a / b) * b */
170
171 int result = op1 - div * op2;
172
173 /* Some paranoid checking to ensure I didn't make a mistake here. */
174 assert(result >= 0);
175 assert(result < op2);
176 assert(div * op2 + result == op1);
177
178 return result;
179}
180
181/**
182 * Number of days since the Epoch.
183 * Epoch is 1970-01-01, which is also equal to day 0.
184 *
185 * @param year Year (year 1900 = 0, may be negative).
186 * @param mon Month (January = 0).
187 * @param mday Day of month (first day = 1).
188 * @return Number of days since the Epoch.
189 */
190static time_t _days_since_epoch(time_t year, time_t mon, time_t mday)
191{
192 return (year - 70) * 365 + _floor_div(year - 69, 4) -
193 _floor_div(year - 1, 100) + _floor_div(year + 299, 400) +
194 _day_of_year(year, mon, mday);
195}
196
197/**
198 * Seconds since the Epoch. see also _days_since_epoch().
199 *
200 * @param tm Normalized broken-down time.
201 * @return Number of seconds since the epoch, not counting leap seconds.
202 */
203static time_t _secs_since_epoch(const struct tm *tm)
204{
205 return _days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) *
206 SECS_PER_DAY + tm->tm_hour * SECS_PER_HOUR +
207 tm->tm_min * SECS_PER_MIN + tm->tm_sec;
208}
209
210/**
211 * Which day of week the specified date is.
212 *
213 * @param year Year (year 1900 = 0).
214 * @param mon Month (January = 0).
215 * @param mday Day of month (first = 1).
216 * @return Day of week (Sunday = 0).
217 */
218static int _day_of_week(time_t year, time_t mon, time_t mday)
219{
220 /* 1970-01-01 is Thursday */
221 return _floor_mod((_days_since_epoch(year, mon, mday) + 4), 7);
222}
223
224/**
225 * Normalizes the broken-down time and optionally adds specified amount of
226 * seconds.
227 *
228 * @param tm Broken-down time to normalize.
229 * @param sec_add Seconds to add.
230 * @return 0 on success, -1 on overflow
231 */
232static int _normalize_time(struct tm *tm, time_t sec_add)
233{
234 // TODO: DST correction
235
236 /* Set initial values. */
237 time_t sec = tm->tm_sec + sec_add;
238 time_t min = tm->tm_min;
239 time_t hour = tm->tm_hour;
240 time_t day = tm->tm_mday - 1;
241 time_t mon = tm->tm_mon;
242 time_t year = tm->tm_year;
243
244 /* Adjust time. */
245 min += _floor_div(sec, SECS_PER_MIN);
246 sec = _floor_mod(sec, SECS_PER_MIN);
247 hour += _floor_div(min, MINS_PER_HOUR);
248 min = _floor_mod(min, MINS_PER_HOUR);
249 day += _floor_div(hour, HOURS_PER_DAY);
250 hour = _floor_mod(hour, HOURS_PER_DAY);
251
252 /* Adjust month. */
253 year += _floor_div(mon, 12);
254 mon = _floor_mod(mon, 12);
255
256 /* Now the difficult part - days of month. */
257
258 /* First, deal with whole cycles of 400 years = 146097 days. */
259 year += _floor_div(day, 146097) * 400;
260 day = _floor_mod(day, 146097);
261
262 /* Then, go in one year steps. */
263 if (mon <= 1) {
264 /* January and February. */
265 while (day > 365) {
266 day -= _is_leap_year(year) ? 366 : 365;
267 year++;
268 }
269 } else {
270 /* Rest of the year. */
271 while (day > 365) {
272 day -= _is_leap_year(year + 1) ? 366 : 365;
273 year++;
274 }
275 }
276
277 /* Finally, finish it off month per month. */
278 while (day >= _days_in_month(year, mon)) {
279 day -= _days_in_month(year, mon);
280 mon++;
281 if (mon >= 12) {
282 mon -= 12;
283 year++;
284 }
285 }
286
287 /* Calculate the remaining two fields. */
288 tm->tm_yday = _day_of_year(year, mon, day + 1);
289 tm->tm_wday = _day_of_week(year, mon, day + 1);
290
291 /* And put the values back to the struct. */
292 tm->tm_sec = (int) sec;
293 tm->tm_min = (int) min;
294 tm->tm_hour = (int) hour;
295 tm->tm_mday = (int) day + 1;
296 tm->tm_mon = (int) mon;
297
298 /* Casts to work around libc brain-damage. */
299 if (year > ((int)INT_MAX) || year < ((int)INT_MIN)) {
300 tm->tm_year = (year < 0) ? ((int)INT_MIN) : ((int)INT_MAX);
301 return -1;
302 }
303
304 tm->tm_year = (int) year;
305 return 0;
306}
307
308/**
309 * Which day the week-based year starts on, relative to the first calendar day.
310 * E.g. if the year starts on December 31st, the return value is -1.
311 *
312 * @param Year since 1900.
313 * @return Offset of week-based year relative to calendar year.
314 */
315static int _wbyear_offset(int year)
316{
317 int start_wday = _day_of_week(year, 0, 1);
318 return _floor_mod(4 - start_wday, 7) - 3;
319}
320
321/**
322 * Returns week-based year of the specified time.
323 *
324 * @param tm Normalized broken-down time.
325 * @return Week-based year.
326 */
327static int _wbyear(const struct tm *tm)
328{
329 int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
330 if (day < 0) {
331 /* Last week of previous year. */
332 return tm->tm_year - 1;
333 }
334 if (day > 364 + _is_leap_year(tm->tm_year)) {
335 /* First week of next year. */
336 return tm->tm_year + 1;
337 }
338 /* All the other days are in the calendar year. */
339 return tm->tm_year;
340}
341
342/**
343 * Week number of the year, assuming weeks start on sunday.
344 * The first Sunday of January is the first day of week 1;
345 * days in the new year before this are in week 0.
346 *
347 * @param tm Normalized broken-down time.
348 * @return The week number (0 - 53).
349 */
350static int _sun_week_number(const struct tm *tm)
351{
352 int first_day = (7 - _day_of_week(tm->tm_year, 0, 1)) % 7;
353 return (tm->tm_yday - first_day + 7) / 7;
354}
355
356/**
357 * Week number of the year, assuming weeks start on monday.
358 * If the week containing January 1st has four or more days in the new year,
359 * then it is considered week 1. Otherwise, it is the last week of the previous
360 * year, and the next week is week 1. Both January 4th and the first Thursday
361 * of January are always in week 1.
362 *
363 * @param tm Normalized broken-down time.
364 * @return The week number (1 - 53).
365 */
366static int _iso_week_number(const struct tm *tm)
367{
368 int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
369 if (day < 0) {
370 /* Last week of previous year. */
371 return 53;
372 }
373 if (day > 364 + _is_leap_year(tm->tm_year)) {
374 /* First week of next year. */
375 return 1;
376 }
377 /* All the other days give correct answer. */
378 return (day / 7 + 1);
379}
380
381/**
382 * Week number of the year, assuming weeks start on monday.
383 * The first Monday of January is the first day of week 1;
384 * days in the new year before this are in week 0.
385 *
386 * @param tm Normalized broken-down time.
387 * @return The week number (0 - 53).
388 */
389static int _mon_week_number(const struct tm *tm)
390{
391 int first_day = (1 - _day_of_week(tm->tm_year, 0, 1)) % 7;
392 return (tm->tm_yday - first_day + 7) / 7;
393}
394
395/******************************************************************************/
396
397
398/** Add microseconds to given timeval.
399 *
400 * @param tv Destination timeval.
401 * @param usecs Number of microseconds to add.
402 *
403 */
404void tv_add(struct timeval *tv, suseconds_t usecs)
405{
406 tv->tv_sec += usecs / 1000000;
407 tv->tv_usec += usecs % 1000000;
408
409 if (tv->tv_usec > 1000000) {
410 tv->tv_sec++;
411 tv->tv_usec -= 1000000;
412 }
413}
414
415/** Subtract two timevals.
416 *
417 * @param tv1 First timeval.
418 * @param tv2 Second timeval.
419 *
420 * @return Difference between tv1 and tv2 (tv1 - tv2) in
421 * microseconds.
422 *
423 */
424suseconds_t tv_sub(struct timeval *tv1, struct timeval *tv2)
425{
426 return (tv1->tv_usec - tv2->tv_usec) +
427 ((tv1->tv_sec - tv2->tv_sec) * 1000000);
428}
429
430/** Decide if one timeval is greater than the other.
431 *
432 * @param t1 First timeval.
433 * @param t2 Second timeval.
434 *
435 * @return True if tv1 is greater than tv2.
436 * @return False otherwise.
437 *
438 */
439int tv_gt(struct timeval *tv1, struct timeval *tv2)
440{
441 if (tv1->tv_sec > tv2->tv_sec)
442 return true;
443
444 if ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec > tv2->tv_usec))
445 return true;
446
447 return false;
448}
449
450/** Decide if one timeval is greater than or equal to the other.
451 *
452 * @param tv1 First timeval.
453 * @param tv2 Second timeval.
454 *
455 * @return True if tv1 is greater than or equal to tv2.
456 * @return False otherwise.
457 *
458 */
459int tv_gteq(struct timeval *tv1, struct timeval *tv2)
460{
461 if (tv1->tv_sec > tv2->tv_sec)
462 return true;
463
464 if ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec >= tv2->tv_usec))
465 return true;
466
467 return false;
468}
469
470/** Get time of day
471 *
472 * The time variables are memory mapped (read-only) from kernel which
473 * updates them periodically.
474 *
475 * As it is impossible to read 2 values atomically, we use a trick:
476 * First we read the seconds, then we read the microseconds, then we
477 * read the seconds again. If a second elapsed in the meantime, set
478 * the microseconds to zero.
479 *
480 * This assures that the values returned by two subsequent calls
481 * to gettimeofday() are monotonous.
482 *
483 */
484int gettimeofday(struct timeval *tv, struct timezone *tz)
485{
486 int rc;
487 struct tm t;
488 category_id_t cat_id;
489 size_t svc_cnt;
490 service_id_t *svc_ids = NULL;
491 service_id_t svc_id;
492 char *svc_name = NULL;
493
494 static async_sess_t *clock_conn = NULL;
495
496 if (tz) {
497 tz->tz_minuteswest = 0;
498 tz->tz_dsttime = DST_NONE;
499 }
500
501 if (clock_conn == NULL) {
502 rc = loc_category_get_id("clock", &cat_id, IPC_FLAG_BLOCKING);
503 if (rc != EOK)
504 goto ret_uptime;
505
506 rc = loc_category_get_svcs(cat_id, &svc_ids, &svc_cnt);
507 if (rc != EOK)
508 goto ret_uptime;
509
510 if (svc_cnt == 0)
511 goto ret_uptime;
512
513 rc = loc_service_get_name(svc_ids[0], &svc_name);
514 if (rc != EOK)
515 goto ret_uptime;
516
517 rc = loc_service_get_id(svc_name, &svc_id, 0);
518 if (rc != EOK)
519 goto ret_uptime;
520
521 clock_conn = loc_service_connect(EXCHANGE_SERIALIZE,
522 svc_id, IPC_FLAG_BLOCKING);
523 if (!clock_conn)
524 goto ret_uptime;
525 }
526
527 rc = clock_dev_time_get(clock_conn, &t);
528 if (rc != EOK)
529 goto ret_uptime;
530
531 tv->tv_usec = 0;
532 tv->tv_sec = mktime(&t);
533
534 free(svc_name);
535 free(svc_ids);
536
537 return EOK;
538
539ret_uptime:
540
541 free(svc_name);
542 free(svc_ids);
543
544 return getuptime(tv);
545}
546
547int getuptime(struct timeval *tv)
548{
549 if (ktime == NULL) {
550 uintptr_t faddr;
551 int rc = sysinfo_get_value("clock.faddr", &faddr);
552 if (rc != EOK) {
553 errno = rc;
554 return -1;
555 }
556
557 void *addr;
558 rc = physmem_map((void *) faddr, 1,
559 AS_AREA_READ | AS_AREA_CACHEABLE, &addr);
560 if (rc != EOK) {
561 as_area_destroy(addr);
562 errno = rc;
563 return -1;
564 }
565
566 ktime = addr;
567 }
568
569 sysarg_t s2 = ktime->seconds2;
570
571 read_barrier();
572 tv->tv_usec = ktime->useconds;
573
574 read_barrier();
575 sysarg_t s1 = ktime->seconds1;
576
577 if (s1 != s2) {
578 tv->tv_sec = max(s1, s2);
579 tv->tv_usec = 0;
580 } else
581 tv->tv_sec = s1;
582
583 return 0;
584}
585
586time_t time(time_t *tloc)
587{
588 struct timeval tv;
589 if (gettimeofday(&tv, NULL))
590 return (time_t) -1;
591
592 if (tloc)
593 *tloc = tv.tv_sec;
594
595 return tv.tv_sec;
596}
597
598/** Wait unconditionally for specified number of microseconds
599 *
600 */
601int usleep(useconds_t usec)
602{
603 (void) __SYSCALL1(SYS_THREAD_USLEEP, usec);
604 return 0;
605}
606
607void udelay(useconds_t time)
608{
609 (void) __SYSCALL1(SYS_THREAD_UDELAY, (sysarg_t) time);
610}
611
612
613/** Wait unconditionally for specified number of seconds
614 *
615 */
616unsigned int sleep(unsigned int sec)
617{
618 /*
619 * Sleep in 1000 second steps to support
620 * full argument range
621 */
622
623 while (sec > 0) {
624 unsigned int period = (sec > 1000) ? 1000 : sec;
625
626 usleep(period * 1000000);
627 sec -= period;
628 }
629
630 return 0;
631}
632
633/**
634 * This function first normalizes the provided broken-down time
635 * (moves all values to their proper bounds) and then tries to
636 * calculate the appropriate time_t representation.
637 *
638 * @param tm Broken-down time.
639 * @return time_t representation of the time, undefined value on overflow.
640 */
641time_t mktime(struct tm *tm)
642{
643 // TODO: take DST flag into account
644 // TODO: detect overflow
645
646 _normalize_time(tm, 0);
647 return _secs_since_epoch(tm);
648}
649
650/**
651 * Convert time and date to a string, based on a specified format and
652 * current locale.
653 *
654 * @param s Buffer to write string to.
655 * @param maxsize Size of the buffer.
656 * @param format Format of the output.
657 * @param tm Broken-down time to format.
658 * @return Number of bytes written.
659 */
660size_t strftime(char *restrict s, size_t maxsize,
661 const char *restrict format, const struct tm *restrict tm)
662{
663 assert(s != NULL);
664 assert(format != NULL);
665 assert(tm != NULL);
666
667 // TODO: use locale
668 static const char *wday_abbr[] = {
669 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
670 };
671 static const char *wday[] = {
672 "Sunday", "Monday", "Tuesday", "Wednesday",
673 "Thursday", "Friday", "Saturday"
674 };
675 static const char *mon_abbr[] = {
676 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
677 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
678 };
679 static const char *mon[] = {
680 "January", "February", "March", "April", "May", "June", "July",
681 "August", "September", "October", "November", "December"
682 };
683
684 if (maxsize < 1) {
685 return 0;
686 }
687
688 char *ptr = s;
689 size_t consumed;
690 size_t remaining = maxsize;
691
692 #define append(...) { \
693 /* FIXME: this requires POSIX-correct snprintf */ \
694 /* otherwise it won't work with non-ascii chars */ \
695 consumed = snprintf(ptr, remaining, __VA_ARGS__); \
696 if (consumed >= remaining) { \
697 return 0; \
698 } \
699 ptr += consumed; \
700 remaining -= consumed; \
701 }
702
703 #define recurse(fmt) { \
704 consumed = strftime(ptr, remaining, fmt, tm); \
705 if (consumed == 0) { \
706 return 0; \
707 } \
708 ptr += consumed; \
709 remaining -= consumed; \
710 }
711
712 #define TO_12H(hour) (((hour) > 12) ? ((hour) - 12) : \
713 (((hour) == 0) ? 12 : (hour)))
714
715 while (*format != '\0') {
716 if (*format != '%') {
717 append("%c", *format);
718 format++;
719 continue;
720 }
721
722 format++;
723 if (*format == '0' || *format == '+') {
724 // TODO: padding
725 format++;
726 }
727 while (isdigit(*format)) {
728 // TODO: padding
729 format++;
730 }
731 if (*format == 'O' || *format == 'E') {
732 // TODO: locale's alternative format
733 format++;
734 }
735
736 switch (*format) {
737 case 'a':
738 append("%s", wday_abbr[tm->tm_wday]); break;
739 case 'A':
740 append("%s", wday[tm->tm_wday]); break;
741 case 'b':
742 append("%s", mon_abbr[tm->tm_mon]); break;
743 case 'B':
744 append("%s", mon[tm->tm_mon]); break;
745 case 'c':
746 // TODO: locale-specific datetime format
747 recurse("%Y-%m-%d %H:%M:%S"); break;
748 case 'C':
749 append("%02d", (1900 + tm->tm_year) / 100); break;
750 case 'd':
751 append("%02d", tm->tm_mday); break;
752 case 'D':
753 recurse("%m/%d/%y"); break;
754 case 'e':
755 append("%2d", tm->tm_mday); break;
756 case 'F':
757 recurse("%+4Y-%m-%d"); break;
758 case 'g':
759 append("%02d", _wbyear(tm) % 100); break;
760 case 'G':
761 append("%d", _wbyear(tm)); break;
762 case 'h':
763 recurse("%b"); break;
764 case 'H':
765 append("%02d", tm->tm_hour); break;
766 case 'I':
767 append("%02d", TO_12H(tm->tm_hour)); break;
768 case 'j':
769 append("%03d", tm->tm_yday); break;
770 case 'k':
771 append("%2d", tm->tm_hour); break;
772 case 'l':
773 append("%2d", TO_12H(tm->tm_hour)); break;
774 case 'm':
775 append("%02d", tm->tm_mon); break;
776 case 'M':
777 append("%02d", tm->tm_min); break;
778 case 'n':
779 append("\n"); break;
780 case 'p':
781 append("%s", tm->tm_hour < 12 ? "AM" : "PM"); break;
782 case 'P':
783 append("%s", tm->tm_hour < 12 ? "am" : "PM"); break;
784 case 'r':
785 recurse("%I:%M:%S %p"); break;
786 case 'R':
787 recurse("%H:%M"); break;
788 case 's':
789 append("%ld", _secs_since_epoch(tm)); break;
790 case 'S':
791 append("%02d", tm->tm_sec); break;
792 case 't':
793 append("\t"); break;
794 case 'T':
795 recurse("%H:%M:%S"); break;
796 case 'u':
797 append("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
798 break;
799 case 'U':
800 append("%02d", _sun_week_number(tm)); break;
801 case 'V':
802 append("%02d", _iso_week_number(tm)); break;
803 case 'w':
804 append("%d", tm->tm_wday); break;
805 case 'W':
806 append("%02d", _mon_week_number(tm)); break;
807 case 'x':
808 // TODO: locale-specific date format
809 recurse("%Y-%m-%d"); break;
810 case 'X':
811 // TODO: locale-specific time format
812 recurse("%H:%M:%S"); break;
813 case 'y':
814 append("%02d", tm->tm_year % 100); break;
815 case 'Y':
816 append("%d", 1900 + tm->tm_year); break;
817 case 'z':
818 // TODO: timezone
819 break;
820 case 'Z':
821 // TODO: timezone
822 break;
823 case '%':
824 append("%%");
825 break;
826 default:
827 /* Invalid specifier, print verbatim. */
828 while (*format != '%') {
829 format--;
830 }
831 append("%%");
832 break;
833 }
834 format++;
835 }
836
837 #undef append
838 #undef recurse
839
840 return maxsize - remaining;
841}
842
843
844/** Converts a time value to a broken-down UTC time
845 *
846 * @param time Time to convert
847 * @param result Structure to store the result to
848 *
849 * @return EOK or a negative error code
850 */
851int time_utc2tm(const time_t time, struct tm *restrict result)
852{
853 assert(result != NULL);
854
855 /* Set result to epoch. */
856 result->tm_sec = 0;
857 result->tm_min = 0;
858 result->tm_hour = 0;
859 result->tm_mday = 1;
860 result->tm_mon = 0;
861 result->tm_year = 70; /* 1970 */
862
863 if (_normalize_time(result, time) == -1)
864 return EOVERFLOW;
865
866 return EOK;
867}
868
869/** Converts a time value to a null terminated string of the form
870 * "Wed Jun 30 21:49:08 1993\n" expressed in UTC.
871 *
872 * @param time Time to convert.
873 * @param buf Buffer to store the string to, must be at least
874 * ASCTIME_BUF_LEN bytes long.
875 *
876 * @return EOK or a negative error code.
877 */
878int time_utc2str(const time_t time, char *restrict buf)
879{
880 struct tm t;
881 int r;
882
883 if ((r = time_utc2tm(time, &t)) != EOK)
884 return r;
885
886 time_tm2str(&t, buf);
887 return EOK;
888}
889
890
891/**
892 * Converts broken-down time to a string in format
893 * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
894 *
895 * @param timeptr Broken-down time structure.
896 * @param buf Buffer to store string to, must be at least ASCTIME_BUF_LEN
897 * bytes long.
898 */
899void time_tm2str(const struct tm *restrict timeptr, char *restrict buf)
900{
901 assert(timeptr != NULL);
902 assert(buf != NULL);
903
904 static const char *wday[] = {
905 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
906 };
907 static const char *mon[] = {
908 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
909 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
910 };
911
912 snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n",
913 wday[timeptr->tm_wday],
914 mon[timeptr->tm_mon],
915 timeptr->tm_mday, timeptr->tm_hour,
916 timeptr->tm_min, timeptr->tm_sec,
917 1900 + timeptr->tm_year);
918}
919
920/**
921 * Converts a time value to a broken-down local time, expressed relative
922 * to the user's specified timezone.
923 *
924 * @param timer Time to convert.
925 * @param result Structure to store the result to.
926 *
927 * @return EOK on success or a negative error code.
928 */
929int time_local2tm(const time_t time, struct tm *restrict result)
930{
931 // TODO: deal with timezone
932 // currently assumes system and all times are in GMT
933
934 /* Set result to epoch. */
935 result->tm_sec = 0;
936 result->tm_min = 0;
937 result->tm_hour = 0;
938 result->tm_mday = 1;
939 result->tm_mon = 0;
940 result->tm_year = 70; /* 1970 */
941
942 if (_normalize_time(result, time) == -1)
943 return EOVERFLOW;
944
945 return EOK;
946}
947
948/**
949 * Converts the calendar time to a null terminated string
950 * of the form "Wed Jun 30 21:49:08 1993\n" expressed relative to the
951 * user's specified timezone.
952 *
953 * @param timer Time to convert.
954 * @param buf Buffer to store the string to. Must be at least
955 * ASCTIME_BUF_LEN bytes long.
956 *
957 * @return EOK on success or a negative error code.
958 */
959int time_local2str(const time_t time, char *buf)
960{
961 struct tm loctime;
962 int r;
963
964 if ((r = time_local2tm(time, &loctime)) != EOK)
965 return r;
966
967 time_tm2str(&loctime, buf);
968
969 return EOK;
970}
971
972/**
973 * Calculate the difference between two times, in seconds.
974 *
975 * @param time1 First time.
976 * @param time0 Second time.
977 * @return Time in seconds.
978 */
979double difftime(time_t time1, time_t time0)
980{
981 return (double) (time1 - time0);
982}
983
984/** @}
985 */
Note: See TracBrowser for help on using the repository browser.