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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a8b0c5d was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 24.0 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 <loc.h>
52#include <device/clock_dev.h>
53#include <thread.h>
54
55#define ASCTIME_BUF_LEN 26
56
57#define HOURS_PER_DAY 24
58#define MINS_PER_HOUR 60
59#define SECS_PER_MIN 60
60#define USECS_PER_SEC 1000000
61#define MINS_PER_DAY (MINS_PER_HOUR * HOURS_PER_DAY)
62#define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR)
63#define SECS_PER_DAY (SECS_PER_HOUR * HOURS_PER_DAY)
64
65/** Pointer to kernel shared variables with time */
66struct {
67 volatile sysarg_t seconds1;
68 volatile sysarg_t useconds;
69 volatile sysarg_t seconds2;
70} *ktime = NULL;
71
72static async_sess_t *clock_conn = NULL;
73
74/** Check whether the year is a leap year.
75 *
76 * @param year Year since 1900 (e.g. for 1970, the value is 70).
77 *
78 * @return true if year is a leap year, false otherwise
79 *
80 */
81static bool is_leap_year(time_t year)
82{
83 year += 1900;
84
85 if (year % 400 == 0)
86 return true;
87
88 if (year % 100 == 0)
89 return false;
90
91 if (year % 4 == 0)
92 return true;
93
94 return false;
95}
96
97/** How many days there are in the given month
98 *
99 * Return how many days there are in the given month of the given year.
100 * Note that year is only taken into account if month is February.
101 *
102 * @param year Year since 1900 (can be negative).
103 * @param mon Month of the year. 0 for January, 11 for December.
104 *
105 * @return Number of days in the specified month.
106 *
107 */
108static int days_in_month(time_t year, time_t mon)
109{
110 assert(mon >= 0);
111 assert(mon <= 11);
112
113 static int month_days[] = {
114 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
115 };
116
117 if (mon == 1) {
118 /* February */
119 year += 1900;
120 return is_leap_year(year) ? 29 : 28;
121 }
122
123 return month_days[mon];
124}
125
126/** Which day of that year it is.
127 *
128 * For specified year, month and day of month, return which day of that year
129 * it is.
130 *
131 * For example, given date 2011-01-03, the corresponding expression is:
132 * day_of_year(111, 0, 3) == 2
133 *
134 * @param year Year (year 1900 = 0, can be negative).
135 * @param mon Month (January = 0).
136 * @param mday Day of month (First day is 1).
137 *
138 * @return Day of year (First day is 0).
139 *
140 */
141static int day_of_year(time_t year, time_t mon, time_t mday)
142{
143 static int mdays[] = {
144 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
145 };
146
147 static int leap_mdays[] = {
148 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
149 };
150
151 return (is_leap_year(year) ? leap_mdays[mon] : mdays[mon]) + mday - 1;
152}
153
154/** Integer division that rounds to negative infinity.
155 *
156 * Used by some functions in this module.
157 *
158 * @param op1 Dividend.
159 * @param op2 Divisor.
160 *
161 * @return Rounded quotient.
162 *
163 */
164static time_t floor_div(time_t op1, time_t op2)
165{
166 if ((op1 >= 0) || (op1 % op2 == 0))
167 return op1 / op2;
168
169 return op1 / op2 - 1;
170}
171
172/** Modulo that rounds to negative infinity.
173 *
174 * Used by some functions in this module.
175 *
176 * @param op1 Dividend.
177 * @param op2 Divisor.
178 *
179 * @return Remainder.
180 *
181 */
182static time_t floor_mod(time_t op1, time_t op2)
183{
184 time_t div = floor_div(op1, op2);
185
186 /*
187 * (a / b) * b + a % b == a
188 * Thus: a % b == a - (a / b) * b
189 */
190
191 time_t result = op1 - div * op2;
192
193 /* Some paranoid checking to ensure there is mistake here. */
194 assert(result >= 0);
195 assert(result < op2);
196 assert(div * op2 + result == op1);
197
198 return result;
199}
200
201/** Number of days since the Epoch.
202 *
203 * Epoch is 1970-01-01, which is also equal to day 0.
204 *
205 * @param year Year (year 1900 = 0, may be negative).
206 * @param mon Month (January = 0).
207 * @param mday Day of month (first day = 1).
208 *
209 * @return Number of days since the Epoch.
210 *
211 */
212static time_t days_since_epoch(time_t year, time_t mon, time_t mday)
213{
214 return (year - 70) * 365 + floor_div(year - 69, 4) -
215 floor_div(year - 1, 100) + floor_div(year + 299, 400) +
216 day_of_year(year, mon, mday);
217}
218
219/** Seconds since the Epoch.
220 *
221 * See also days_since_epoch().
222 *
223 * @param tm Normalized broken-down time.
224 *
225 * @return Number of seconds since the epoch, not counting leap seconds.
226 *
227 */
228static time_t secs_since_epoch(const struct tm *tm)
229{
230 return days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) *
231 SECS_PER_DAY + tm->tm_hour * SECS_PER_HOUR +
232 tm->tm_min * SECS_PER_MIN + tm->tm_sec;
233}
234
235/** Which day of week the specified date is.
236 *
237 * @param year Year (year 1900 = 0).
238 * @param mon Month (January = 0).
239 * @param mday Day of month (first = 1).
240 *
241 * @return Day of week (Sunday = 0).
242 *
243 */
244static time_t day_of_week(time_t year, time_t mon, time_t mday)
245{
246 /* 1970-01-01 is Thursday */
247 return floor_mod(days_since_epoch(year, mon, mday) + 4, 7);
248}
249
250/** Normalize the broken-down time.
251 *
252 * Optionally add specified amount of seconds.
253 *
254 * @param tm Broken-down time to normalize.
255 * @param tv Timeval to add.
256 *
257 * @return 0 on success, -1 on overflow
258 *
259 */
260static int normalize_tm_tv(struct tm *tm, const struct timeval *tv)
261{
262 // TODO: DST correction
263
264 /* Set initial values. */
265 time_t usec = tm->tm_usec + tv->tv_usec;
266 time_t sec = tm->tm_sec + tv->tv_sec;
267 time_t min = tm->tm_min;
268 time_t hour = tm->tm_hour;
269 time_t day = tm->tm_mday - 1;
270 time_t mon = tm->tm_mon;
271 time_t year = tm->tm_year;
272
273 /* Adjust time. */
274 sec += floor_div(usec, USECS_PER_SEC);
275 usec = floor_mod(usec, USECS_PER_SEC);
276 min += floor_div(sec, SECS_PER_MIN);
277 sec = floor_mod(sec, SECS_PER_MIN);
278 hour += floor_div(min, MINS_PER_HOUR);
279 min = floor_mod(min, MINS_PER_HOUR);
280 day += floor_div(hour, HOURS_PER_DAY);
281 hour = floor_mod(hour, HOURS_PER_DAY);
282
283 /* Adjust month. */
284 year += floor_div(mon, 12);
285 mon = floor_mod(mon, 12);
286
287 /* Now the difficult part - days of month. */
288
289 /* First, deal with whole cycles of 400 years = 146097 days. */
290 year += floor_div(day, 146097) * 400;
291 day = floor_mod(day, 146097);
292
293 /* Then, go in one year steps. */
294 if (mon <= 1) {
295 /* January and February. */
296 while (day > 365) {
297 day -= is_leap_year(year) ? 366 : 365;
298 year++;
299 }
300 } else {
301 /* Rest of the year. */
302 while (day > 365) {
303 day -= is_leap_year(year + 1) ? 366 : 365;
304 year++;
305 }
306 }
307
308 /* Finally, finish it off month per month. */
309 while (day >= days_in_month(year, mon)) {
310 day -= days_in_month(year, mon);
311 mon++;
312
313 if (mon >= 12) {
314 mon -= 12;
315 year++;
316 }
317 }
318
319 /* Calculate the remaining two fields. */
320 tm->tm_yday = day_of_year(year, mon, day + 1);
321 tm->tm_wday = day_of_week(year, mon, day + 1);
322
323 /* And put the values back to the struct. */
324 tm->tm_usec = (int) usec;
325 tm->tm_sec = (int) sec;
326 tm->tm_min = (int) min;
327 tm->tm_hour = (int) hour;
328 tm->tm_mday = (int) day + 1;
329 tm->tm_mon = (int) mon;
330
331 /* Casts to work around POSIX brain-damage. */
332 if (year > ((int) INT_MAX) || year < ((int) INT_MIN)) {
333 tm->tm_year = (year < 0) ? ((int) INT_MIN) : ((int) INT_MAX);
334 return -1;
335 }
336
337 tm->tm_year = (int) year;
338 return 0;
339}
340
341static int normalize_tm_time(struct tm *tm, time_t time)
342{
343 struct timeval tv = {
344 .tv_sec = time,
345 .tv_usec = 0
346 };
347
348 return normalize_tm_tv(tm, &tv);
349}
350
351
352/** Which day the week-based year starts on.
353 *
354 * Relative to the first calendar day. E.g. if the year starts
355 * on December 31st, the return value is -1.
356 *
357 * @param Year since 1900.
358 *
359 * @return Offset of week-based year relative to calendar year.
360 *
361 */
362static int wbyear_offset(int year)
363{
364 int start_wday = day_of_week(year, 0, 1);
365
366 return floor_mod(4 - start_wday, 7) - 3;
367}
368
369/** Week-based year of the specified time.
370 *
371 * @param tm Normalized broken-down time.
372 *
373 * @return Week-based year.
374 *
375 */
376static int wbyear(const struct tm *tm)
377{
378 int day = tm->tm_yday - wbyear_offset(tm->tm_year);
379
380 if (day < 0) {
381 /* Last week of previous year. */
382 return tm->tm_year - 1;
383 }
384
385 if (day > 364 + is_leap_year(tm->tm_year)) {
386 /* First week of next year. */
387 return tm->tm_year + 1;
388 }
389
390 /* All the other days are in the calendar year. */
391 return tm->tm_year;
392}
393
394/** Week number of the year (assuming weeks start on Sunday).
395 *
396 * The first Sunday of January is the first day of week 1;
397 * days in the new year before this are in week 0.
398 *
399 * @param tm Normalized broken-down time.
400 *
401 * @return The week number (0 - 53).
402 *
403 */
404static int sun_week_number(const struct tm *tm)
405{
406 int first_day = (7 - day_of_week(tm->tm_year, 0, 1)) % 7;
407
408 return (tm->tm_yday - first_day + 7) / 7;
409}
410
411/** Week number of the year (assuming weeks start on Monday).
412 *
413 * If the week containing January 1st has four or more days
414 * in the new year, then it is considered week 1. Otherwise,
415 * it is the last week of the previous year, and the next week
416 * is week 1. Both January 4th and the first Thursday
417 * of January are always in week 1.
418 *
419 * @param tm Normalized broken-down time.
420 *
421 * @return The week number (1 - 53).
422 *
423 */
424static int iso_week_number(const struct tm *tm)
425{
426 int day = tm->tm_yday - wbyear_offset(tm->tm_year);
427
428 if (day < 0) {
429 /* Last week of previous year. */
430 return 53;
431 }
432
433 if (day > 364 + is_leap_year(tm->tm_year)) {
434 /* First week of next year. */
435 return 1;
436 }
437
438 /* All the other days give correct answer. */
439 return (day / 7 + 1);
440}
441
442/** Week number of the year (assuming weeks start on Monday).
443 *
444 * The first Monday of January is the first day of week 1;
445 * days in the new year before this are in week 0.
446 *
447 * @param tm Normalized broken-down time.
448 *
449 * @return The week number (0 - 53).
450 *
451 */
452static int mon_week_number(const struct tm *tm)
453{
454 int first_day = (1 - day_of_week(tm->tm_year, 0, 1)) % 7;
455
456 return (tm->tm_yday - first_day + 7) / 7;
457}
458
459static void tv_normalize(struct timeval *tv)
460{
461 while (tv->tv_usec > USECS_PER_SEC) {
462 tv->tv_sec++;
463 tv->tv_usec -= USECS_PER_SEC;
464 }
465 while (tv->tv_usec < 0) {
466 tv->tv_sec--;
467 tv->tv_usec += USECS_PER_SEC;
468 }
469}
470
471/** Add microseconds to given timeval.
472 *
473 * @param tv Destination timeval.
474 * @param usecs Number of microseconds to add.
475 *
476 */
477void tv_add_diff(struct timeval *tv, suseconds_t usecs)
478{
479 tv->tv_sec += usecs / USECS_PER_SEC;
480 tv->tv_usec += usecs % USECS_PER_SEC;
481 tv_normalize(tv);
482}
483
484/** Add two timevals.
485 *
486 * @param tv1 First timeval.
487 * @param tv2 Second timeval.
488 */
489void tv_add(struct timeval *tv1, struct timeval *tv2)
490{
491 tv1->tv_sec += tv2->tv_sec;
492 tv1->tv_usec += tv2->tv_usec;
493 tv_normalize(tv1);
494}
495
496/** Subtract two timevals.
497 *
498 * @param tv1 First timeval.
499 * @param tv2 Second timeval.
500 *
501 * @return Difference between tv1 and tv2 (tv1 - tv2) in
502 * microseconds.
503 *
504 */
505suseconds_t tv_sub_diff(struct timeval *tv1, struct timeval *tv2)
506{
507 return (tv1->tv_usec - tv2->tv_usec) +
508 ((tv1->tv_sec - tv2->tv_sec) * USECS_PER_SEC);
509}
510
511/** Subtract two timevals.
512 *
513 * @param tv1 First timeval.
514 * @param tv2 Second timeval.
515 *
516 */
517void tv_sub(struct timeval *tv1, struct timeval *tv2)
518{
519 tv1->tv_sec -= tv2->tv_sec;
520 tv1->tv_usec -= tv2->tv_usec;
521 tv_normalize(tv1);
522}
523
524/** Decide if one timeval is greater than the other.
525 *
526 * @param t1 First timeval.
527 * @param t2 Second timeval.
528 *
529 * @return True if tv1 is greater than tv2.
530 * @return False otherwise.
531 *
532 */
533int tv_gt(struct timeval *tv1, struct timeval *tv2)
534{
535 if (tv1->tv_sec > tv2->tv_sec)
536 return true;
537
538 if ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec > tv2->tv_usec))
539 return true;
540
541 return false;
542}
543
544/** Decide if one timeval is greater than or equal to the other.
545 *
546 * @param tv1 First timeval.
547 * @param tv2 Second timeval.
548 *
549 * @return True if tv1 is greater than or equal to tv2.
550 * @return False otherwise.
551 *
552 */
553int tv_gteq(struct timeval *tv1, struct timeval *tv2)
554{
555 if (tv1->tv_sec > tv2->tv_sec)
556 return true;
557
558 if ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec >= tv2->tv_usec))
559 return true;
560
561 return false;
562}
563
564/** Get time of day.
565 *
566 * The time variables are memory mapped (read-only) from kernel which
567 * updates them periodically.
568 *
569 * As it is impossible to read 2 values atomically, we use a trick:
570 * First we read the seconds, then we read the microseconds, then we
571 * read the seconds again. If a second elapsed in the meantime, set
572 * the microseconds to zero.
573 *
574 * This assures that the values returned by two subsequent calls
575 * to gettimeofday() are monotonous.
576 *
577 */
578void gettimeofday(struct timeval *tv, struct timezone *tz)
579{
580 if (tz) {
581 tz->tz_minuteswest = 0;
582 tz->tz_dsttime = DST_NONE;
583 }
584
585 if (clock_conn == NULL) {
586 category_id_t cat_id;
587 errno_t rc = loc_category_get_id("clock", &cat_id, IPC_FLAG_BLOCKING);
588 if (rc != EOK)
589 goto fallback;
590
591 service_id_t *svc_ids;
592 size_t svc_cnt;
593 rc = loc_category_get_svcs(cat_id, &svc_ids, &svc_cnt);
594 if (rc != EOK)
595 goto fallback;
596
597 if (svc_cnt == 0)
598 goto fallback;
599
600 char *svc_name;
601 rc = loc_service_get_name(svc_ids[0], &svc_name);
602 free(svc_ids);
603 if (rc != EOK)
604 goto fallback;
605
606 service_id_t svc_id;
607 rc = loc_service_get_id(svc_name, &svc_id, 0);
608 free(svc_name);
609 if (rc != EOK)
610 goto fallback;
611
612 clock_conn = loc_service_connect(svc_id, INTERFACE_DDF,
613 IPC_FLAG_BLOCKING);
614 if (!clock_conn)
615 goto fallback;
616 }
617
618 struct tm time;
619 errno_t rc = clock_dev_time_get(clock_conn, &time);
620 if (rc != EOK)
621 goto fallback;
622
623 tv->tv_usec = time.tm_usec;
624 tv->tv_sec = mktime(&time);
625
626 return;
627
628fallback:
629 getuptime(tv);
630}
631
632void getuptime(struct timeval *tv)
633{
634 if (ktime == NULL) {
635 uintptr_t faddr;
636 errno_t rc = sysinfo_get_value("clock.faddr", &faddr);
637 if (rc != EOK) {
638 errno = rc;
639 goto fallback;
640 }
641
642 void *addr = AS_AREA_ANY;
643 rc = physmem_map(faddr, 1, AS_AREA_READ | AS_AREA_CACHEABLE,
644 &addr);
645 if (rc != EOK) {
646 as_area_destroy(addr);
647 errno = rc;
648 goto fallback;
649 }
650
651 ktime = addr;
652 }
653
654 sysarg_t s2 = ktime->seconds2;
655
656 read_barrier();
657 tv->tv_usec = ktime->useconds;
658
659 read_barrier();
660 sysarg_t s1 = ktime->seconds1;
661
662 if (s1 != s2) {
663 tv->tv_sec = max(s1, s2);
664 tv->tv_usec = 0;
665 } else
666 tv->tv_sec = s1;
667
668 return;
669
670fallback:
671 tv->tv_sec = 0;
672 tv->tv_usec = 0;
673}
674
675time_t time(time_t *tloc)
676{
677 struct timeval tv;
678 gettimeofday(&tv, NULL);
679
680 if (tloc)
681 *tloc = tv.tv_sec;
682
683 return tv.tv_sec;
684}
685
686void udelay(useconds_t time)
687{
688 (void) __SYSCALL1(SYS_THREAD_UDELAY, (sysarg_t) time);
689}
690
691/** Get time from broken-down time.
692 *
693 * First normalize the provided broken-down time
694 * (moves all values to their proper bounds) and
695 * then try to calculate the appropriate time_t
696 * representation.
697 *
698 * @param tm Broken-down time.
699 *
700 * @return time_t representation of the time.
701 * @return Undefined value on overflow.
702 *
703 */
704time_t mktime(struct tm *tm)
705{
706 // TODO: take DST flag into account
707 // TODO: detect overflow
708
709 normalize_tm_time(tm, 0);
710 return secs_since_epoch(tm);
711}
712
713/*
714 * FIXME: This requires POSIX-correct snprintf.
715 * Otherwise it won't work with non-ASCII chars.
716 */
717#define APPEND(...) \
718 { \
719 consumed = snprintf(ptr, remaining, __VA_ARGS__); \
720 if (consumed >= remaining) \
721 return 0; \
722 \
723 ptr += consumed; \
724 remaining -= consumed; \
725 }
726
727#define RECURSE(fmt) \
728 { \
729 consumed = strftime(ptr, remaining, fmt, tm); \
730 if (consumed == 0) \
731 return 0; \
732 \
733 ptr += consumed; \
734 remaining -= consumed; \
735 }
736
737#define TO_12H(hour) \
738 (((hour) > 12) ? ((hour) - 12) : \
739 (((hour) == 0) ? 12 : (hour)))
740
741/** Convert time and date to a string.
742 *
743 * @param s Buffer to write string to.
744 * @param maxsize Size of the buffer.
745 * @param format Format of the output.
746 * @param tm Broken-down time to format.
747 *
748 * @return Number of bytes written.
749 *
750 */
751size_t strftime(char *restrict s, size_t maxsize,
752 const char *restrict format, const struct tm *restrict tm)
753{
754 assert(s != NULL);
755 assert(format != NULL);
756 assert(tm != NULL);
757
758 // TODO: use locale
759
760 static const char *wday_abbr[] = {
761 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
762 };
763
764 static const char *wday[] = {
765 "Sunday", "Monday", "Tuesday", "Wednesday",
766 "Thursday", "Friday", "Saturday"
767 };
768
769 static const char *mon_abbr[] = {
770 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
771 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
772 };
773
774 static const char *mon[] = {
775 "January", "February", "March", "April", "May", "June", "July",
776 "August", "September", "October", "November", "December"
777 };
778
779 if (maxsize < 1)
780 return 0;
781
782 char *ptr = s;
783 size_t consumed;
784 size_t remaining = maxsize;
785
786 while (*format != '\0') {
787 if (*format != '%') {
788 APPEND("%c", *format);
789 format++;
790 continue;
791 }
792
793 format++;
794 if ((*format == '0') || (*format == '+')) {
795 // TODO: padding
796 format++;
797 }
798
799 while (isdigit(*format)) {
800 // TODO: padding
801 format++;
802 }
803
804 if ((*format == 'O') || (*format == 'E')) {
805 // TODO: locale's alternative format
806 format++;
807 }
808
809 switch (*format) {
810 case 'a':
811 APPEND("%s", wday_abbr[tm->tm_wday]);
812 break;
813 case 'A':
814 APPEND("%s", wday[tm->tm_wday]);
815 break;
816 case 'b':
817 APPEND("%s", mon_abbr[tm->tm_mon]);
818 break;
819 case 'B':
820 APPEND("%s", mon[tm->tm_mon]);
821 break;
822 case 'c':
823 // TODO: locale-specific datetime format
824 RECURSE("%Y-%m-%d %H:%M:%S");
825 break;
826 case 'C':
827 APPEND("%02d", (1900 + tm->tm_year) / 100);
828 break;
829 case 'd':
830 APPEND("%02d", tm->tm_mday);
831 break;
832 case 'D':
833 RECURSE("%m/%d/%y");
834 break;
835 case 'e':
836 APPEND("%2d", tm->tm_mday);
837 break;
838 case 'F':
839 RECURSE("%+4Y-%m-%d");
840 break;
841 case 'g':
842 APPEND("%02d", wbyear(tm) % 100);
843 break;
844 case 'G':
845 APPEND("%d", wbyear(tm));
846 break;
847 case 'h':
848 RECURSE("%b");
849 break;
850 case 'H':
851 APPEND("%02d", tm->tm_hour);
852 break;
853 case 'I':
854 APPEND("%02d", TO_12H(tm->tm_hour));
855 break;
856 case 'j':
857 APPEND("%03d", tm->tm_yday);
858 break;
859 case 'k':
860 APPEND("%2d", tm->tm_hour);
861 break;
862 case 'l':
863 APPEND("%2d", TO_12H(tm->tm_hour));
864 break;
865 case 'm':
866 APPEND("%02d", tm->tm_mon);
867 break;
868 case 'M':
869 APPEND("%02d", tm->tm_min);
870 break;
871 case 'n':
872 APPEND("\n");
873 break;
874 case 'p':
875 APPEND("%s", tm->tm_hour < 12 ? "AM" : "PM");
876 break;
877 case 'P':
878 APPEND("%s", tm->tm_hour < 12 ? "am" : "PM");
879 break;
880 case 'r':
881 RECURSE("%I:%M:%S %p");
882 break;
883 case 'R':
884 RECURSE("%H:%M");
885 break;
886 case 's':
887 APPEND("%ld", secs_since_epoch(tm));
888 break;
889 case 'S':
890 APPEND("%02d", tm->tm_sec);
891 break;
892 case 't':
893 APPEND("\t");
894 break;
895 case 'T':
896 RECURSE("%H:%M:%S");
897 break;
898 case 'u':
899 APPEND("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
900 break;
901 case 'U':
902 APPEND("%02d", sun_week_number(tm));
903 break;
904 case 'V':
905 APPEND("%02d", iso_week_number(tm));
906 break;
907 case 'w':
908 APPEND("%d", tm->tm_wday);
909 break;
910 case 'W':
911 APPEND("%02d", mon_week_number(tm));
912 break;
913 case 'x':
914 // TODO: locale-specific date format
915 RECURSE("%Y-%m-%d");
916 break;
917 case 'X':
918 // TODO: locale-specific time format
919 RECURSE("%H:%M:%S");
920 break;
921 case 'y':
922 APPEND("%02d", tm->tm_year % 100);
923 break;
924 case 'Y':
925 APPEND("%d", 1900 + tm->tm_year);
926 break;
927 case 'z':
928 // TODO: timezone
929 break;
930 case 'Z':
931 // TODO: timezone
932 break;
933 case '%':
934 APPEND("%%");
935 break;
936 default:
937 /* Invalid specifier, print verbatim. */
938 while (*format != '%')
939 format--;
940
941 APPEND("%%");
942 break;
943 }
944
945 format++;
946 }
947
948 return maxsize - remaining;
949}
950
951/** Convert a time value to a broken-down UTC time/
952 *
953 * @param time Time to convert
954 * @param result Structure to store the result to
955 *
956 * @return EOK or an error code
957 *
958 */
959errno_t time_utc2tm(const time_t time, struct tm *restrict result)
960{
961 assert(result != NULL);
962
963 /* Set result to epoch. */
964 result->tm_usec = 0;
965 result->tm_sec = 0;
966 result->tm_min = 0;
967 result->tm_hour = 0;
968 result->tm_mday = 1;
969 result->tm_mon = 0;
970 result->tm_year = 70; /* 1970 */
971
972 if (normalize_tm_time(result, time) == -1)
973 return EOVERFLOW;
974
975 return EOK;
976}
977
978/** Convert a time value to a NULL-terminated string.
979 *
980 * The format is "Wed Jun 30 21:49:08 1993\n" expressed in UTC.
981 *
982 * @param time Time to convert.
983 * @param buf Buffer to store the string to, must be at least
984 * ASCTIME_BUF_LEN bytes long.
985 *
986 * @return EOK or an error code.
987 *
988 */
989errno_t time_utc2str(const time_t time, char *restrict buf)
990{
991 struct tm tm;
992 errno_t ret = time_utc2tm(time, &tm);
993 if (ret != EOK)
994 return ret;
995
996 time_tm2str(&tm, buf);
997 return EOK;
998}
999
1000/** Convert broken-down time to a NULL-terminated string.
1001 *
1002 * The format is "Sun Jan 1 00:00:00 1970\n". (Obsolete)
1003 *
1004 * @param timeptr Broken-down time structure.
1005 * @param buf Buffer to store string to, must be at least
1006 * ASCTIME_BUF_LEN bytes long.
1007 *
1008 */
1009void time_tm2str(const struct tm *restrict timeptr, char *restrict buf)
1010{
1011 assert(timeptr != NULL);
1012 assert(buf != NULL);
1013
1014 static const char *wday[] = {
1015 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1016 };
1017
1018 static const char *mon[] = {
1019 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1020 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1021 };
1022
1023 snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n",
1024 wday[timeptr->tm_wday],
1025 mon[timeptr->tm_mon],
1026 timeptr->tm_mday, timeptr->tm_hour,
1027 timeptr->tm_min, timeptr->tm_sec,
1028 1900 + timeptr->tm_year);
1029}
1030
1031/** Converts a time value to a broken-down local time.
1032 *
1033 * Time is expressed relative to the user's specified timezone.
1034 *
1035 * @param tv Timeval to convert.
1036 * @param result Structure to store the result to.
1037 *
1038 * @return EOK on success or an error code.
1039 *
1040 */
1041errno_t time_tv2tm(const struct timeval *tv, struct tm *restrict result)
1042{
1043 // TODO: Deal with timezones.
1044 // Currently assumes system and all times are in UTC
1045
1046 /* Set result to epoch. */
1047 result->tm_usec = 0;
1048 result->tm_sec = 0;
1049 result->tm_min = 0;
1050 result->tm_hour = 0;
1051 result->tm_mday = 1;
1052 result->tm_mon = 0;
1053 result->tm_year = 70; /* 1970 */
1054
1055 if (normalize_tm_tv(result, tv) == -1)
1056 return EOVERFLOW;
1057
1058 return EOK;
1059}
1060
1061/** Converts a time value to a broken-down local time.
1062 *
1063 * Time is expressed relative to the user's specified timezone.
1064 *
1065 * @param timer Time to convert.
1066 * @param result Structure to store the result to.
1067 *
1068 * @return EOK on success or an error code.
1069 *
1070 */
1071errno_t time_local2tm(const time_t time, struct tm *restrict result)
1072{
1073 struct timeval tv = {
1074 .tv_sec = time,
1075 .tv_usec = 0
1076 };
1077
1078 return time_tv2tm(&tv, result);
1079}
1080
1081/** Convert the calendar time to a NULL-terminated string.
1082 *
1083 * The format is "Wed Jun 30 21:49:08 1993\n" expressed relative to the
1084 * user's specified timezone.
1085 *
1086 * @param timer Time to convert.
1087 * @param buf Buffer to store the string to. Must be at least
1088 * ASCTIME_BUF_LEN bytes long.
1089 *
1090 * @return EOK on success or an error code.
1091 *
1092 */
1093errno_t time_local2str(const time_t time, char *buf)
1094{
1095 struct tm loctime;
1096 errno_t ret = time_local2tm(time, &loctime);
1097 if (ret != EOK)
1098 return ret;
1099
1100 time_tm2str(&loctime, buf);
1101 return EOK;
1102}
1103
1104/** Calculate the difference between two times, in seconds.
1105 *
1106 * @param time1 First time.
1107 * @param time0 Second time.
1108 *
1109 * @return Time difference in seconds.
1110 *
1111 */
1112double difftime(time_t time1, time_t time0)
1113{
1114 return (double) (time1 - time0);
1115}
1116
1117/** @}
1118 */
Note: See TracBrowser for help on using the repository browser.