MYNEWT-4: Need the concept of wallclock time Add shell command "date" to set and get wallclock time.
The wallclock time is specified as per RFC 3339. For example: date 2016-03-02T22:44:00 # set time to Mar 2 2016 10:44 PM UTC date 2016-03-02T22:44:00Z # with explicit Zulu time zone (UTC + 0) date 2016-03-02T22:44:00z # with explicit Zulu time zone (UTC + 0) date 2016-03-02T22:44:00-08:00 # pacific standard time (UTC - 0800) date 2016-03-02T22:44:00+05:30 # indian standard time (UTC + 0530) date 2016-03-02T22:44:00.000031 # time is 22:44:00 + 31 usec date 2016-03-02T22:44:00.1 # time is 22:44:00 + 100 msec date 2016-03-02T22:44:00.013 # time is 22:44:00 + 13 msec 'os_settimeofday()' and 'os_gettimeofday()' can be used to set and get the time of day. 'parse_datetime()' and 'format_datetime()' are available to parse and format the RFC 3339 compliant datetime strings. Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/commit/70511652 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/70511652 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/70511652 Branch: refs/heads/master Commit: 7051165241fd498e6024f800344de1b420330a7f Parents: 4e2c53c Author: neel <n...@neels-air.attlocal.net> Authored: Wed Mar 2 23:01:16 2016 -0800 Committer: wes3 <w...@micosa.io> Committed: Thu Mar 3 14:48:59 2016 -0800 ---------------------------------------------------------------------- LICENSE | 5 + libs/os/include/os/os_time.h | 65 +++++ libs/os/src/os_time.c | 80 +++++++ libs/shell/src/shell.c | 9 + libs/shell/src/shell_os.c | 35 +++ libs/shell/src/shell_priv.h | 1 + libs/util/include/util/datetime.h | 50 ++++ libs/util/src/datetime.c | 420 +++++++++++++++++++++++++++++++++ 8 files changed, 665 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/70511652/LICENSE ---------------------------------------------------------------------- diff --git a/LICENSE b/LICENSE index 8f71f43..0c99734 100644 --- a/LICENSE +++ b/LICENSE @@ -200,3 +200,8 @@ See the License for the specific language governing permissions and limitations under the License. + +This product partly derives from FreeBSD, which is available under the +"3-clause BSD" license. For details, see: + * libs/os/include/os/os_time.h + * libs/util/src/datetime.c http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/70511652/libs/os/include/os/os_time.h ---------------------------------------------------------------------- diff --git a/libs/os/include/os/os_time.h b/libs/os/include/os/os_time.h index b3c95b3..d3cb028 100644 --- a/libs/os/include/os/os_time.h +++ b/libs/os/include/os/os_time.h @@ -17,6 +17,38 @@ * under the License. */ +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)time.h 8.5 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + #ifndef _OS_TIME_H #define _OS_TIME_H @@ -39,4 +71,37 @@ void os_time_delay(int32_t osticks); #define OS_TIME_TICK_GT(__t1, __t2) ((int32_t) ((__t1) - (__t2)) > 0) #define OS_TIME_TICK_GEQ(__t1, __t2) ((int32_t) ((__t1) - (__t2)) >= 0) +struct os_timeval { + int64_t tv_sec; /* seconds since Jan 1 1970 */ + int32_t tv_usec; /* microseconds within the second */ +}; + +struct os_timezone { + int16_t tz_minuteswest; /* with respect to GMT */ + int16_t tz_dsttime; /* daylight savings time correction (if any) */ +}; + +#define os_timeradd(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) + +#define os_timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) + +int os_settimeofday(struct os_timeval *utctime, struct os_timezone *tz); +int os_gettimeofday(struct os_timeval *utctime, struct os_timezone *tz); + #endif /* _OS_TIME_H */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/70511652/libs/os/src/os_time.c ---------------------------------------------------------------------- diff --git a/libs/os/src/os_time.c b/libs/os/src/os_time.c index 783dc14..28aaced 100644 --- a/libs/os/src/os_time.c +++ b/libs/os/src/os_time.c @@ -20,8 +20,31 @@ #include "os/os.h" #include "os/queue.h" +#define OS_USEC_PER_TICK (1000000 / OS_TICKS_PER_SEC) + os_time_t g_os_time = 0; +/* + * Time-of-day collateral. + */ +static struct { + os_time_t ostime; + struct os_timeval uptime; + struct os_timeval utctime; + struct os_timezone timezone; +} basetod; + +static void +os_deltatime(os_time_t delta, const struct os_timeval *base, + struct os_timeval *result) +{ + struct os_timeval tvdelta; + + tvdelta.tv_sec = delta / OS_TICKS_PER_SEC; + tvdelta.tv_usec = (delta % OS_TICKS_PER_SEC) * OS_USEC_PER_TICK; + os_timeradd(base, &tvdelta, result); +} + os_time_t os_time_get(void) { @@ -37,9 +60,21 @@ void os_time_tick(void) { os_sr_t sr; + os_time_t delta; OS_ENTER_CRITICAL(sr); ++g_os_time; + + /* + * Update 'basetod' when the lowest 31 bits of 'g_os_time' are all zero, + * i.e. at 0x00000000 and 0x80000000. + */ + if ((g_os_time << 1) == 0) { /* XXX use __unlikely() here */ + delta = g_os_time - basetod.ostime; + os_deltatime(delta, &basetod.uptime, &basetod.uptime); + os_deltatime(delta, &basetod.utctime, &basetod.utctime); + basetod.ostime = g_os_time; + } OS_EXIT_CRITICAL(sr); } @@ -61,3 +96,48 @@ os_time_delay(int32_t osticks) os_sched(NULL, 0); } } + +int +os_settimeofday(struct os_timeval *utctime, struct os_timezone *tz) +{ + os_sr_t sr; + os_time_t delta; + + OS_ENTER_CRITICAL(sr); + if (utctime != NULL) { + /* + * Update all time-of-day base values. + */ + delta = os_time_get() - basetod.ostime; + os_deltatime(delta, &basetod.uptime, &basetod.uptime); + basetod.utctime = *utctime; + basetod.ostime += delta; + } + + if (tz != NULL) { + basetod.timezone = *tz; + } + OS_EXIT_CRITICAL(sr); + + return (0); +} + +int +os_gettimeofday(struct os_timeval *tv, struct os_timezone *tz) +{ + os_sr_t sr; + os_time_t delta; + + OS_ENTER_CRITICAL(sr); + if (tv != NULL) { + delta = os_time_get() - basetod.ostime; + os_deltatime(delta, &basetod.utctime, tv); + } + + if (tz != NULL) { + *tz = basetod.timezone; + } + OS_EXIT_CRITICAL(sr); + + return (0); +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/70511652/libs/shell/src/shell.c ---------------------------------------------------------------------- diff --git a/libs/shell/src/shell.c b/libs/shell/src/shell.c index 1773eab..8a26129 100644 --- a/libs/shell/src/shell.c +++ b/libs/shell/src/shell.c @@ -60,6 +60,10 @@ static struct shell_cmd g_shell_os_mpool_display_cmd = { .sc_cmd = "mempools", .sc_cmd_func = shell_os_mpool_display_cmd }; +static struct shell_cmd g_shell_os_date_cmd = { + .sc_cmd = "date", + .sc_cmd_func = shell_os_date_cmd +}; static struct os_task shell_task; static struct os_eventq shell_evq; @@ -553,6 +557,11 @@ shell_task_init(uint8_t prio, os_stack_t *stack, uint16_t stack_size, goto err; } + rc = shell_cmd_register(&g_shell_os_date_cmd); + if (rc != 0) { + goto err; + } + rc = os_task_init(&shell_task, "shell", shell_task_func, NULL, prio, OS_WAIT_FOREVER, stack, stack_size); if (rc != 0) { http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/70511652/libs/shell/src/shell_os.c ---------------------------------------------------------------------- diff --git a/libs/shell/src/shell_os.c b/libs/shell/src/shell_os.c index 43ff4dc..283d429 100644 --- a/libs/shell/src/shell_os.c +++ b/libs/shell/src/shell_os.c @@ -20,12 +20,15 @@ #include "os/os.h" #include "os/queue.h" +#include "os/os_time.h" #include "console/console.h" #include "shell/shell.h" #include "shell_priv.h" +#include <assert.h> #include <string.h> +#include <util/datetime.h> int shell_os_tasks_display_cmd(int argc, char **argv) @@ -119,3 +122,35 @@ shell_os_mpool_display_cmd(int argc, char **argv) return (0); } + +int +shell_os_date_cmd(int argc, char **argv) +{ + struct os_timeval tv; + struct os_timezone tz; + char buf[DATETIME_BUFSIZE]; + int rc; + + argc--; argv++; /* skip command name */ + + if (argc == 0) { + /* Display the current datetime */ + rc = os_gettimeofday(&tv, &tz); + assert(rc == 0); + rc = format_datetime(&tv, &tz, buf, sizeof(buf)); + assert(rc == 0); + console_printf("%s\n", buf); + } else if (argc == 1) { + /* Set the current datetime */ + rc = parse_datetime(*argv, &tv, &tz); + if (rc == 0) { + rc = os_settimeofday(&tv, &tz); + } else { + console_printf("Invalid datetime\n"); + } + } else { + rc = -1; + } + + return (rc); +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/70511652/libs/shell/src/shell_priv.h ---------------------------------------------------------------------- diff --git a/libs/shell/src/shell_priv.h b/libs/shell/src/shell_priv.h index 67ac47c..4e035e0 100644 --- a/libs/shell/src/shell_priv.h +++ b/libs/shell/src/shell_priv.h @@ -22,5 +22,6 @@ int shell_os_tasks_display_cmd(int argc, char **argv); int shell_os_mpool_display_cmd(int argc, char **argv); +int shell_os_date_cmd(int argc, char **argv); #endif /* __SHELL_PRIV_H_ */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/70511652/libs/util/include/util/datetime.h ---------------------------------------------------------------------- diff --git a/libs/util/include/util/datetime.h b/libs/util/include/util/datetime.h new file mode 100644 index 0000000..220c9bf --- /dev/null +++ b/libs/util/include/util/datetime.h @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef __UTIL_DATETIME_H +#define __UTIL_DATETIME_H + +struct os_timeval; +struct os_timezone; + +#define DATETIME_BUFSIZE 33 + +/* + * Format the time specified by 'utctime' and 'tz' as per RFC 3339 in + * the 'output' string. The size of the buffer pointed to by 'output' + * is specified by 'olen'. + * + * Returns 0 on success and non-zero on failure. + */ +int format_datetime(const struct os_timeval *utctime, + const struct os_timezone *tz, char *output, int olen); + +/* + * Parse 'input' in the Internet date/time format specified by RFC 3339. + * https://www.ietf.org/rfc/rfc3339.txt + * + * We deviate from the RFC in that if the 'time offset' is left unspecified + * then we default to UTC time. + * + * Return 0 if 'input' could be parsed successfully and non-zero otherwise. + * 'utctime' and 'tz' are updated if 'input' was parsed successfully. + */ +int parse_datetime(const char *input, struct os_timeval *utctime, + struct os_timezone *tz); + +#endif /* __UTIL_DATETIME_H */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/70511652/libs/util/src/datetime.c ---------------------------------------------------------------------- diff --git a/libs/util/src/datetime.c b/libs/util/src/datetime.c new file mode 100644 index 0000000..403fd63 --- /dev/null +++ b/libs/util/src/datetime.c @@ -0,0 +1,420 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*- + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah $Hdr: clock.c 1.18 91/01/21$ + * from: @(#)clock.c 8.2 (Berkeley) 1/12/94 + * from: NetBSD: clock_subr.c,v 1.6 2001/07/07 17:04:02 thorpej Exp + * and + * from: src/sys/i386/isa/clock.c,v 1.176 2001/09/04 + */ + +#include <os/os_time.h> + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <util/datetime.h> + +struct clocktime { + int year; /* year (4 digit year) */ + int mon; /* month (1 - 12) */ + int day; /* day (1 - 31) */ + int hour; /* hour (0 - 23) */ + int min; /* minute (0 - 59) */ + int sec; /* second (0 - 59) */ + int dow; /* day of week (0 - 6; 0 = Sunday) */ + int usec; /* micro seconds */ +}; + +#define days_in_year(y) (leapyear(y) ? 366 : 365) + +#define FEBRUARY 2 +#define days_in_month(y, m) \ + (month_days[(m) - 1] + (m == FEBRUARY ? leapyear(y) : 0)) + +/* Day of week. Days are counted from 1/1/1970, which was a Thursday */ +#define day_of_week(days) (((days) + 4) % 7) + +static const int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +#define POSIX_BASE_YEAR 1970 +#define SECDAY (24 * 60 * 60) + +/* + * This inline avoids some unnecessary modulo operations + * as compared with the usual macro: + * ( ((year % 4) == 0 && + * (year % 100) != 0) || + * ((year % 400) == 0) ) + * It is otherwise equivalent. + */ +static int +leapyear(int year) +{ + int rv = 0; + + if ((year & 3) == 0) { + rv = 1; + if ((year % 100) == 0) { + rv = 0; + if ((year % 400) == 0) + rv = 1; + } + } + return (rv); +} + +static int +clocktime_to_timeval(const struct clocktime *ct, struct os_timeval *tv) +{ + int i, year, days; + + year = ct->year; + + /* Sanity checks. */ + if (year < POSIX_BASE_YEAR || + ct->mon < 1 || ct->mon > 12 || + ct->day < 1 || ct->day > days_in_month(year, ct->mon) || + ct->hour < 0 || ct->hour > 23 || + ct->min < 0 || ct->min > 59 || + ct->sec < 0 || ct->sec > 59 || + ct->usec < 0 || ct->usec > 999999) { + return (-1); + } + + /* + * Compute days since start of time + * First from years, then from months. + */ + days = 0; + for (i = POSIX_BASE_YEAR; i < year; i++) + days += days_in_year(i); + + /* Months */ + for (i = 1; i < ct->mon; i++) + days += days_in_month(year, i); + days += (ct->day - 1); + + tv->tv_sec = (((int64_t)days * 24 + ct->hour) * 60 + ct->min) * 60 + + ct->sec; + tv->tv_usec = ct->usec; + + return (0); +} + +static int +timeval_to_clocktime(const struct os_timeval *tv, const struct os_timezone *tz, + struct clocktime *ct) +{ + int i, year, days; + int64_t rsec; /* remainder seconds */ + int64_t secs; + + secs = tv->tv_sec; + if (tz != NULL) { + /* Convert utctime to localtime */ + secs -= tz->tz_minuteswest * 60; + secs += tz->tz_dsttime ? 3600 : 0; + } + + if (secs < 0 || tv->tv_usec < 0 || tv->tv_usec > 999999) { + return (-1); + } + + days = secs / SECDAY; + rsec = secs % SECDAY; + + ct->dow = day_of_week(days); + + /* Subtract out whole years, counting them in i. */ + for (year = POSIX_BASE_YEAR; days >= days_in_year(year); year++) + days -= days_in_year(year); + ct->year = year; + + /* Subtract out whole months, counting them in i. */ + for (i = 1; days >= days_in_month(year, i); i++) + days -= days_in_month(year, i); + ct->mon = i; + + /* Days are what is left over (+1) from all that. */ + ct->day = days + 1; + + /* Hours, minutes, seconds are easy */ + ct->hour = rsec / 3600; + rsec = rsec % 3600; + ct->min = rsec / 60; + rsec = rsec % 60; + ct->sec = rsec; + ct->usec = tv->tv_usec; + + return (0); +} + +static const char * +parse_number(const char *str, int digits, int *val) +{ + const char *cp; + const char *end; + + *val = 0; + cp = str; + end = str + digits; + while (cp < end) { + if (!isdigit(*cp)) { + return (NULL); + } + *val *= 10; + *val += (*cp - '0'); + cp++; + } + return (end); +} + +int +parse_datetime(const char *input, struct os_timeval *tv, struct os_timezone *tz) +{ + int digits, sign; + int off_hour, off_min; + const char *cp; + const char *ep; + struct clocktime ct; + + bzero(&ct, sizeof(struct clocktime)); + bzero(tv, sizeof(struct os_timeval)); + bzero(tz, sizeof(struct os_timezone)); /* default to UTC time */ + + cp = input; + cp = parse_number(cp, 4, &ct.year); + if (cp == NULL || *cp != '-') { + goto err; + } + + cp = parse_number(cp + 1, 2, &ct.mon); + if (cp == NULL || *cp != '-') { + goto err; + } + + cp = parse_number(cp + 1, 2, &ct.day); + if (cp == NULL || *cp != 'T') { + goto err; + } + + cp = parse_number(cp + 1, 2, &ct.hour); + if (cp == NULL || *cp != ':') { + goto err; + } + + cp = parse_number(cp + 1, 2, &ct.min); + if (cp == NULL || *cp != ':') { + goto err; + } + + cp = parse_number(cp + 1, 2, &ct.sec); + if (cp == NULL) { + goto err; + } + + /* parse fractional seconds if specified */ + if (*cp == '.') { + ep = ++cp; + while (isdigit(*ep)) { + ep++; + } + digits = ep - cp; + if (digits <= 0 || digits > 6) { + goto err; + } + + cp = parse_number(cp, digits, &ct.usec); + if (cp == NULL) { + goto err; + } + + /* + * The number of digits in the fractional seconds determines + * the resolution. + * + * .1 1 part out of 10 100000 usec + * .01 1 part out of 100 10000 usec + * .001 1 part out of 1000 1000 usec + * .0001 1 part out of 10000 100 usec + * .00001 1 part out of 100000 10 usec + * .000001 1 part out of 1000000 1 usec + */ + while (digits++ < 6) { + ct.usec *= 10; + } + } + + if (*cp == 'Z' || *cp == 'z') { + cp++; + } else if (*cp == '+' || *cp == '-') { + sign = (*cp == '+') ? +1 : -1; + cp = parse_number(cp + 1, 2, &off_hour); + if (cp == NULL || *cp != ':') { + goto err; + } + + cp = parse_number(cp + 1, 2, &off_min); + if (cp == NULL) { + goto err; + } + + if (off_hour < 0 || off_hour > 23 || off_min < 0 || off_min > 59) { + goto err; + } + + /* + * Allow time zone offsets of up to 18 hours from the GMT. + * https://docs.oracle.com/javase/8/docs/api/java/time/ZoneOffset.html + */ + tz->tz_minuteswest = off_hour * 60 + off_min; + if (tz->tz_minuteswest > 18 * 60) { + goto err; + } + + /* + * Positive GMT offsets (i.e. timezones to the east of GMT) are + * represented with a negative 'tz_minuteswest' value. + */ + if (sign > 0) { + tz->tz_minuteswest = -tz->tz_minuteswest; + } + } else { + /* + * 'time offset' is not specified so date/time defaults to UTC. + */ + } + + if (*cp != '\0') { + goto err; + } + + if (clocktime_to_timeval(&ct, tv) != 0) { + goto err; + } + + /* Convert localtime to utctime */ + tv->tv_sec += tz->tz_minuteswest * 60; + tv->tv_sec -= tz->tz_dsttime ? 3600 : 0; + return (0); +err: + return (-1); +} + +int +format_datetime(const struct os_timeval *tv, const struct os_timezone *tz, + char *ostr, int olen) +{ + char *cp; + int rc, rlen, minswest; + int off_hour, off_min, sign; + struct clocktime ct; + + rc = timeval_to_clocktime(tv, tz, &ct); + if (rc != 0) { + goto err; + } + + cp = ostr; + rlen = olen; + + rc = snprintf(cp, rlen, "%04d-%02d-%02dT%02d:%02d:%02d", + ct.year, ct.mon, ct.day, ct.hour, ct.min, ct.sec); + cp += rc; + rlen -= rc; + if (rc < 0 || rlen <= 0) { + goto err; + } + + if (ct.usec != 0) { + rc = snprintf(cp, rlen, ".%06d", ct.usec); + cp += rc; + rlen -= rc; + if (rc < 0 || rlen <= 0) { + goto err; + } + } + + if (tz != NULL) { + minswest = tz->tz_minuteswest; + if (tz->tz_dsttime) { + minswest -= 60; + } + } else { + minswest = 0; + } + + if (minswest < 0) { + sign = '+'; + minswest = -minswest; + } else { + sign = '-'; + } + + off_hour = minswest / 60; + off_min = minswest % 60; + if (off_hour || off_min) { + rc = snprintf(cp, rlen, "%c%02d:%02d", sign, off_hour, off_min); + cp += rc; + rlen -= rc; + if (rc < 0 || rlen <= 0) { + goto err; + } + } + return (0); + +err: + return (-1); +}