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);
+}

Reply via email to