source: mainline/uspace/lib/posix/time.c@ d4d74dc

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d4d74dc was d4d74dc, checked in by Vojtech Horky <vojtechhorky@…>, 13 years ago

Less includes in library headers

There is no need for errno.h to include fibril.h.
Similarly, tinput.h does not need list.h or async.h.

Unfortunately, many programs depended on the fact that including
errno.h would (recursively) include unistd.h and NULL would be
defined. Most of the fixes remedy this problem.

  • Property mode set to 100644
File size: 22.5 KB
RevLine 
[2fc5072]1/*
2 * Copyright (c) 2011 Petr Koupy
3 * Copyright (c) 2011 Jiri Zarevucky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libposix
31 * @{
32 */
[4cf8ca6]33/** @file Time measurement support.
[2fc5072]34 */
35
[4d10fc8]36#define LIBPOSIX_INTERNAL
[2fc5072]37
[324d46b]38/* Must be first. */
39#include "stdbool.h"
40
[9b1503e]41#include "internal/common.h"
[4d10fc8]42#include "time.h"
[a6d908c1]43
[324d46b]44#include "ctype.h"
45#include "errno.h"
[3f466c33]46#include "signal.h"
[d4d74dc]47#include "assert.h"
[324d46b]48
[06cb827]49#include "libc/malloc.h"
[a6d908c1]50#include "libc/task.h"
51#include "libc/stats.h"
[3f466c33]52#include "libc/sys/time.h"
[2fc5072]53
[324d46b]54// TODO: test everything in this file
55
[e6165be]56/* In some places in this file, phrase "normalized broken-down time" is used.
57 * This means time broken down to components (year, month, day, hour, min, sec),
58 * in which every component is in its proper bounds. Non-normalized time could
59 * e.g. be 2011-54-5 29:13:-5, which would semantically mean start of year 2011
60 * + 53 months + 4 days + 29 hours + 13 minutes - 5 seconds.
61 */
62
63
64
[324d46b]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
[55b1efd]74/**
75 * Checks whether the year is a leap year.
[4cf8ca6]76 *
[e6165be]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
[4cf8ca6]79 */
[324d46b]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
[55b1efd]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.
[4cf8ca6]96 *
[e6165be]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.
[4cf8ca6]100 */
[324d46b]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) {
[e6165be]109 year += 1900;
[324d46b]110 /* february */
111 return _is_leap_year(year) ? 29 : 28;
112 } else {
113 return month_days[mon];
114 }
115}
116
[55b1efd]117/**
118 * For specified year, month and day of month, returns which day of that year
119 * it is.
[4cf8ca6]120 *
[e6165be]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).
[4cf8ca6]128 */
[324d46b]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
[55b1efd]139/**
140 * Integer division that rounds to negative infinity.
141 * Used by some functions in this file.
[4cf8ca6]142 *
[55b1efd]143 * @param op1 Divident.
144 * @param op2 Divisor.
145 * @return Rounded quotient.
[324d46b]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
[55b1efd]156/**
157 * Modulo that rounds to negative infinity.
158 * Used by some functions in this file.
[4cf8ca6]159 *
[55b1efd]160 * @param op1 Divident.
161 * @param op2 Divisor.
162 * @return Remainder.
[324d46b]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
[55b1efd]181/**
182 * Number of days since the Epoch.
183 * Epoch is 1970-01-01, which is also equal to day 0.
[4cf8ca6]184 *
[e6165be]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.
[4cf8ca6]189 */
[324d46b]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
[55b1efd]197/**
198 * Seconds since the Epoch. see also _days_since_epoch().
[e6165be]199 *
200 * @param tm Normalized broken-down time.
201 * @return Number of seconds since the epoch, not counting leap seconds.
[4cf8ca6]202 */
[324d46b]203static time_t _secs_since_epoch(const struct posix_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
[55b1efd]210/**
211 * Which day of week the specified date is.
[4cf8ca6]212 *
[e6165be]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).
[4cf8ca6]217 */
[324d46b]218static int _day_of_week(time_t year, time_t mon, time_t mday)
219{
220 /* 1970-01-01 is Thursday */
[e6165be]221 return _floor_mod((_days_since_epoch(year, mon, mday) + 4), 7);
[324d46b]222}
223
[55b1efd]224/**
225 * Normalizes the broken-down time and optionally adds specified amount of
226 * seconds.
[4cf8ca6]227 *
[e6165be]228 * @param tm Broken-down time to normalize.
229 * @param sec_add Seconds to add.
230 * @return 0 on success, -1 on overflow
[4cf8ca6]231 */
[e6165be]232static int _normalize_time(struct posix_tm *tm, time_t sec_add)
[324d46b]233{
234 // TODO: DST correction
235
[e6165be]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
[324d46b]244 /* Adjust time. */
[e6165be]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);
[324d46b]251
252 /* Adjust month. */
[e6165be]253 year += _floor_div(mon, 12);
254 mon = _floor_mod(mon, 12);
[324d46b]255
256 /* Now the difficult part - days of month. */
[e6165be]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++;
[324d46b]274 }
275 }
[e6165be]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++;
[324d46b]284 }
285 }
[e6165be]286
[324d46b]287 /* Calculate the remaining two fields. */
[e6165be]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;
[324d46b]306}
307
[55b1efd]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.
[4cf8ca6]311 *
[e6165be]312 * @param Year since 1900.
313 * @return Offset of week-based year relative to calendar year.
[324d46b]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
[55b1efd]321/**
322 * Returns week-based year of the specified time.
[4cf8ca6]323 *
[e6165be]324 * @param tm Normalized broken-down time.
325 * @return Week-based year.
[324d46b]326 */
327static int _wbyear(const struct posix_tm *tm)
328{
[3f466c33]329 int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
330 if (day < 0) {
331 /* Last week of previous year. */
[324d46b]332 return tm->tm_year - 1;
333 }
[2aadf2b]334 if (day > 364 + _is_leap_year(tm->tm_year)) {
[3f466c33]335 /* First week of next year. */
[324d46b]336 return tm->tm_year + 1;
337 }
[3f466c33]338 /* All the other days are in the calendar year. */
[324d46b]339 return tm->tm_year;
340}
341
[55b1efd]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.
[3f466c33]346 *
347 * @param tm Normalized broken-down time.
348 * @return The week number (0 - 53).
[324d46b]349 */
350static int _sun_week_number(const struct posix_tm *tm)
351{
[3f466c33]352 int first_day = (7 - _day_of_week(tm->tm_year, 0, 1)) % 7;
353 return (tm->tm_yday - first_day + 7) / 7;
[324d46b]354}
355
[55b1efd]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.
[3f466c33]362 *
363 * @param tm Normalized broken-down time.
364 * @return The week number (1 - 53).
[324d46b]365 */
366static int _iso_week_number(const struct posix_tm *tm)
367{
[3f466c33]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 }
[2aadf2b]373 if (day > 364 + _is_leap_year(tm->tm_year)) {
[3f466c33]374 /* First week of next year. */
375 return 1;
376 }
377 /* All the other days give correct answer. */
378 return (day / 7 + 1);
[324d46b]379}
380
[55b1efd]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.
[3f466c33]385 *
386 * @param tm Normalized broken-down time.
387 * @return The week number (0 - 53).
[324d46b]388 */
389static int _mon_week_number(const struct posix_tm *tm)
390{
[3f466c33]391 int first_day = (1 - _day_of_week(tm->tm_year, 0, 1)) % 7;
392 return (tm->tm_yday - first_day + 7) / 7;
[324d46b]393}
394
395/******************************************************************************/
396
397int posix_daylight;
398long posix_timezone;
399char *posix_tzname[2];
400
[55b1efd]401/**
402 * Set timezone conversion information.
[4cf8ca6]403 */
[324d46b]404void posix_tzset(void)
405{
406 // TODO: read environment
407 posix_tzname[0] = (char *) "GMT";
408 posix_tzname[1] = (char *) "GMT";
409 posix_daylight = 0;
410 posix_timezone = 0;
411}
412
[55b1efd]413/**
414 * Calculate the difference between two times, in seconds.
[4cf8ca6]415 *
[55b1efd]416 * @param time1 First time.
417 * @param time0 Second time.
[e6165be]418 * @return Time in seconds.
[4cf8ca6]419 */
[324d46b]420double posix_difftime(time_t time1, time_t time0)
421{
422 return (double) (time1 - time0);
423}
424
[55b1efd]425/**
426 * This function first normalizes the provided broken-down time
427 * (moves all values to their proper bounds) and then tries to
428 * calculate the appropriate time_t representation.
[324d46b]429 *
[4cf8ca6]430 * @param tm Broken-down time.
[55b1efd]431 * @return time_t representation of the time, undefined value on overflow.
[324d46b]432 */
433time_t posix_mktime(struct posix_tm *tm)
434{
435 // TODO: take DST flag into account
436 // TODO: detect overflow
437
[e6165be]438 _normalize_time(tm, 0);
[324d46b]439 return _secs_since_epoch(tm);
440}
441
[55b1efd]442/**
443 * Converts a time value to a broken-down UTC time.
[4cf8ca6]444 *
[e6165be]445 * @param timer Time to convert.
446 * @return Normalized broken-down time in UTC, NULL on overflow.
[4cf8ca6]447 */
[3f466c33]448struct posix_tm *posix_gmtime(const time_t *timer)
[2fc5072]449{
[e6165be]450 assert(timer != NULL);
451
[324d46b]452 static struct posix_tm result;
[3f466c33]453 return posix_gmtime_r(timer, &result);
[324d46b]454}
455
[55b1efd]456/**
457 * Converts a time value to a broken-down UTC time.
[4cf8ca6]458 *
[e6165be]459 * @param timer Time to convert.
460 * @param result Structure to store the result to.
461 * @return Value of result on success, NULL on overflow.
[4cf8ca6]462 */
[3f466c33]463struct posix_tm *posix_gmtime_r(const time_t *restrict timer,
[324d46b]464 struct posix_tm *restrict result)
465{
466 assert(timer != NULL);
467 assert(result != NULL);
468
[e6165be]469 /* Set result to epoch. */
470 result->tm_sec = 0;
471 result->tm_min = 0;
472 result->tm_hour = 0;
473 result->tm_mday = 1;
474 result->tm_mon = 0;
475 result->tm_year = 70; /* 1970 */
[324d46b]476
[e6165be]477 if (_normalize_time(result, *timer) == -1) {
[324d46b]478 errno = EOVERFLOW;
479 return NULL;
480 }
481
482 return result;
[2fc5072]483}
484
[55b1efd]485/**
486 * Converts a time value to a broken-down local time.
[3f466c33]487 *
[e6165be]488 * @param timer Time to convert.
489 * @return Normalized broken-down time in local timezone, NULL on overflow.
[3f466c33]490 */
491struct posix_tm *posix_localtime(const time_t *timer)
492{
493 static struct posix_tm result;
494 return posix_localtime_r(timer, &result);
495}
496
[55b1efd]497/**
498 * Converts a time value to a broken-down local time.
[4cf8ca6]499 *
[e6165be]500 * @param timer Time to convert.
501 * @param result Structure to store the result to.
502 * @return Value of result on success, NULL on overflow.
[4cf8ca6]503 */
[3f466c33]504struct posix_tm *posix_localtime_r(const time_t *restrict timer,
505 struct posix_tm *restrict result)
506{
507 // TODO: deal with timezone
508 // currently assumes system and all times are in GMT
509 return posix_gmtime_r(timer, result);
510}
511
[55b1efd]512/**
513 * Converts broken-down time to a string in format
514 * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
[2fc5072]515 *
[e6165be]516 * @param timeptr Broken-down time structure.
517 * @return Pointer to a statically allocated string.
[2fc5072]518 */
[324d46b]519char *posix_asctime(const struct posix_tm *timeptr)
[2fc5072]520{
[324d46b]521 static char buf[ASCTIME_BUF_LEN];
522 return posix_asctime_r(timeptr, buf);
523}
524
[55b1efd]525/**
526 * Converts broken-down time to a string in format
527 * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
[e6165be]528 *
529 * @param timeptr Broken-down time structure.
530 * @param buf Buffer to store string to, must be at least ASCTIME_BUF_LEN
531 * bytes long.
532 * @return Value of buf.
[4cf8ca6]533 */
[324d46b]534char *posix_asctime_r(const struct posix_tm *restrict timeptr,
535 char *restrict buf)
536{
537 assert(timeptr != NULL);
538 assert(buf != NULL);
539
540 static const char *wday[] = {
541 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
542 };
543 static const char *mon[] = {
544 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
545 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
546 };
547
548 snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n",
549 wday[timeptr->tm_wday],
550 mon[timeptr->tm_mon],
551 timeptr->tm_mday, timeptr->tm_hour,
552 timeptr->tm_min, timeptr->tm_sec,
553 1900 + timeptr->tm_year);
554
555 return buf;
[2fc5072]556}
557
[55b1efd]558/**
559 * Equivalent to asctime(localtime(clock)).
[2fc5072]560 *
[e6165be]561 * @param timer Time to convert.
562 * @return Pointer to a statically allocated string holding the date.
[2fc5072]563 */
[3f466c33]564char *posix_ctime(const time_t *timer)
[2fc5072]565{
[3f466c33]566 struct posix_tm *loctime = posix_localtime(timer);
567 if (loctime == NULL) {
568 return NULL;
569 }
570 return posix_asctime(loctime);
571}
572
[55b1efd]573/**
574 * Reentrant variant of ctime().
[4cf8ca6]575 *
[e6165be]576 * @param timer Time to convert.
577 * @param buf Buffer to store string to. Must be at least ASCTIME_BUF_LEN
578 * bytes long.
579 * @return Pointer to buf on success, NULL on falure.
[4cf8ca6]580 */
[3f466c33]581char *posix_ctime_r(const time_t *timer, char *buf)
582{
583 struct posix_tm loctime;
584 if (posix_localtime_r(timer, &loctime) == NULL) {
585 return NULL;
586 }
587 return posix_asctime_r(&loctime, buf);
[2fc5072]588}
589
[55b1efd]590/**
591 * Convert time and date to a string, based on a specified format and
592 * current locale.
[2fc5072]593 *
[e6165be]594 * @param s Buffer to write string to.
595 * @param maxsize Size of the buffer.
596 * @param format Format of the output.
597 * @param tm Broken-down time to format.
598 * @return Number of bytes written.
[2fc5072]599 */
[4cf8ca6]600size_t posix_strftime(char *restrict s, size_t maxsize,
601 const char *restrict format, const struct posix_tm *restrict tm)
[2fc5072]602{
[e6165be]603 assert(s != NULL);
604 assert(format != NULL);
605 assert(tm != NULL);
606
[324d46b]607 // TODO: use locale
608 static const char *wday_abbr[] = {
609 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
610 };
611 static const char *wday[] = {
612 "Sunday", "Monday", "Tuesday", "Wednesday",
613 "Thursday", "Friday", "Saturday"
614 };
615 static const char *mon_abbr[] = {
616 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
617 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
618 };
619 static const char *mon[] = {
620 "January", "February", "March", "April", "May", "June", "July",
621 "August", "September", "October", "November", "December"
622 };
623
624 if (maxsize < 1) {
625 return 0;
626 }
627
628 char *ptr = s;
629 size_t consumed;
630 size_t remaining = maxsize;
631
632 #define append(...) { \
633 /* FIXME: this requires POSIX-correct snprintf */ \
634 /* otherwise it won't work with non-ascii chars */ \
635 consumed = snprintf(ptr, remaining, __VA_ARGS__); \
636 if (consumed >= remaining) { \
637 return 0; \
638 } \
639 ptr += consumed; \
640 remaining -= consumed; \
641 }
642
643 #define recurse(fmt) { \
644 consumed = posix_strftime(ptr, remaining, fmt, tm); \
645 if (consumed == 0) { \
646 return 0; \
647 } \
648 ptr += consumed; \
649 remaining -= consumed; \
650 }
651
652 #define TO_12H(hour) (((hour) > 12) ? ((hour) - 12) : \
653 (((hour) == 0) ? 12 : (hour)))
654
655 while (*format != '\0') {
656 if (*format != '%') {
657 append("%c", *format);
658 format++;
659 continue;
660 }
661
662 format++;
663 if (*format == '0' || *format == '+') {
664 // TODO: padding
665 format++;
666 }
667 while (isdigit(*format)) {
668 // TODO: padding
669 format++;
670 }
671 if (*format == 'O' || *format == 'E') {
672 // TODO: locale's alternative format
673 format++;
674 }
675
676 switch (*format) {
677 case 'a':
678 append("%s", wday_abbr[tm->tm_wday]); break;
679 case 'A':
680 append("%s", wday[tm->tm_wday]); break;
681 case 'b':
682 append("%s", mon_abbr[tm->tm_mon]); break;
683 case 'B':
684 append("%s", mon[tm->tm_mon]); break;
685 case 'c':
686 // TODO: locale-specific datetime format
687 recurse("%Y-%m-%d %H:%M:%S"); break;
688 case 'C':
689 append("%02d", (1900 + tm->tm_year) / 100); break;
690 case 'd':
691 append("%02d", tm->tm_mday); break;
692 case 'D':
693 recurse("%m/%d/%y"); break;
694 case 'e':
695 append("%2d", tm->tm_mday); break;
696 case 'F':
697 recurse("%+4Y-%m-%d"); break;
698 case 'g':
699 append("%02d", _wbyear(tm) % 100); break;
700 case 'G':
701 append("%d", _wbyear(tm)); break;
702 case 'h':
703 recurse("%b"); break;
704 case 'H':
705 append("%02d", tm->tm_hour); break;
706 case 'I':
707 append("%02d", TO_12H(tm->tm_hour)); break;
708 case 'j':
709 append("%03d", tm->tm_yday); break;
710 case 'k':
711 append("%2d", tm->tm_hour); break;
712 case 'l':
713 append("%2d", TO_12H(tm->tm_hour)); break;
714 case 'm':
715 append("%02d", tm->tm_mon); break;
716 case 'M':
717 append("%02d", tm->tm_min); break;
718 case 'n':
719 append("\n"); break;
720 case 'p':
721 append("%s", tm->tm_hour < 12 ? "AM" : "PM"); break;
722 case 'P':
723 append("%s", tm->tm_hour < 12 ? "am" : "PM"); break;
724 case 'r':
725 recurse("%I:%M:%S %p"); break;
726 case 'R':
727 recurse("%H:%M"); break;
728 case 's':
729 append("%ld", _secs_since_epoch(tm)); break;
730 case 'S':
731 append("%02d", tm->tm_sec); break;
732 case 't':
733 append("\t"); break;
734 case 'T':
735 recurse("%H:%M:%S"); break;
736 case 'u':
737 append("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); break;
738 case 'U':
739 append("%02d", _sun_week_number(tm)); break;
740 case 'V':
741 append("%02d", _iso_week_number(tm)); break;
742 case 'w':
743 append("%d", tm->tm_wday); break;
744 case 'W':
745 append("%02d", _mon_week_number(tm)); break;
746 case 'x':
747 // TODO: locale-specific date format
748 recurse("%Y-%m-%d"); break;
749 case 'X':
750 // TODO: locale-specific time format
751 recurse("%H:%M:%S"); break;
752 case 'y':
753 append("%02d", tm->tm_year % 100); break;
754 case 'Y':
755 append("%d", 1900 + tm->tm_year); break;
756 case 'z':
757 // TODO: timezone
758 break;
759 case 'Z':
760 // TODO: timezone
761 break;
762 case '%':
763 append("%%");
764 break;
765 default:
766 /* Invalid specifier, print verbatim. */
767 while (*format != '%') {
768 format--;
769 }
770 append("%%");
771 break;
772 }
773 format++;
[9067152]774 }
[324d46b]775
776 #undef append
777 #undef recurse
778
779 return maxsize - remaining;
[2fc5072]780}
781
[55b1efd]782/**
783 * Get clock resolution. Only CLOCK_REALTIME is supported.
[4cf8ca6]784 *
[e6165be]785 * @param clock_id Clock ID.
786 * @param res Pointer to the variable where the resolution is to be written.
787 * @return 0 on success, -1 with errno set on failure.
[4cf8ca6]788 */
[3f466c33]789int posix_clock_getres(posix_clockid_t clock_id, struct posix_timespec *res)
790{
791 assert(res != NULL);
792
793 switch (clock_id) {
794 case CLOCK_REALTIME:
795 res->tv_sec = 0;
796 res->tv_nsec = 1000; /* Microsecond resolution. */
797 return 0;
798 default:
799 errno = EINVAL;
800 return -1;
801 }
802}
803
[55b1efd]804/**
805 * Get time. Only CLOCK_REALTIME is supported.
[4cf8ca6]806 *
[e6165be]807 * @param clock_id ID of the clock to query.
808 * @param tp Pointer to the variable where the time is to be written.
[55b1efd]809 * @return 0 on success, -1 with errno on failure.
[4cf8ca6]810 */
[3f466c33]811int posix_clock_gettime(posix_clockid_t clock_id, struct posix_timespec *tp)
812{
813 assert(tp != NULL);
814
815 switch (clock_id) {
816 case CLOCK_REALTIME:
817 ;
818 struct timeval tv;
819 gettimeofday(&tv, NULL);
820 tp->tv_sec = tv.tv_sec;
821 tp->tv_nsec = tv.tv_usec * 1000;
822 return 0;
823 default:
824 errno = EINVAL;
825 return -1;
826 }
827}
828
[55b1efd]829/**
830 * Set time on a specified clock. As HelenOS doesn't support this yet,
831 * this function always fails.
[4cf8ca6]832 *
[e6165be]833 * @param clock_id ID of the clock to set.
834 * @param tp Time to set.
835 * @return 0 on success, -1 with errno on failure.
[4cf8ca6]836 */
[3f466c33]837int posix_clock_settime(posix_clockid_t clock_id,
838 const struct posix_timespec *tp)
839{
840 assert(tp != NULL);
841
842 switch (clock_id) {
843 case CLOCK_REALTIME:
844 // TODO: setting clock
845 // FIXME: HelenOS doesn't actually support hardware
846 // clock yet
847 errno = EPERM;
848 return -1;
849 default:
850 errno = EINVAL;
851 return -1;
852 }
853}
854
[55b1efd]855/**
856 * Sleep on a specified clock.
[4cf8ca6]857 *
[e6165be]858 * @param clock_id ID of the clock to sleep on (only CLOCK_REALTIME supported).
859 * @param flags Flags (none supported).
860 * @param rqtp Sleep time.
861 * @param rmtp Remaining time is written here if sleep is interrupted.
862 * @return 0 on success, -1 with errno set on failure.
[4cf8ca6]863 */
[3f466c33]864int posix_clock_nanosleep(posix_clockid_t clock_id, int flags,
865 const struct posix_timespec *rqtp, struct posix_timespec *rmtp)
866{
867 assert(rqtp != NULL);
868 assert(rmtp != NULL);
869
870 switch (clock_id) {
871 case CLOCK_REALTIME:
872 // TODO: interruptible sleep
873 if (rqtp->tv_sec != 0) {
874 sleep(rqtp->tv_sec);
875 }
876 if (rqtp->tv_nsec != 0) {
877 usleep(rqtp->tv_nsec / 1000);
878 }
879 return 0;
880 default:
881 errno = EINVAL;
882 return -1;
883 }
884}
885
[55b1efd]886/**
887 * Get CPU time used since the process invocation.
[06cb827]888 *
889 * @return Consumed CPU cycles by this process or -1 if not available.
[823a929]890 */
891posix_clock_t posix_clock(void)
892{
[06cb827]893 posix_clock_t total_cycles = -1;
894 stats_task_t *task_stats = stats_get_task(task_get_id());
895 if (task_stats) {
896 total_cycles = (posix_clock_t) (task_stats->kcycles + task_stats->ucycles);
[a12f7f1]897 free(task_stats);
898 task_stats = 0;
[06cb827]899 }
900
901 return total_cycles;
[823a929]902}
903
[2fc5072]904/** @}
905 */
Note: See TracBrowser for help on using the repository browser.