summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_subr/time.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2013-06-18 02:07:41 +0000
committerpeter <peter@FreeBSD.org>2013-06-18 02:07:41 +0000
commitd25dac7fcc6acc838b71bbda8916fd9665c709ab (patch)
tree135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_subr/time.c
downloadFreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.zip
FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.tar.gz
Import trimmed svn-1.8.0-rc3
Diffstat (limited to 'subversion/libsvn_subr/time.c')
-rw-r--r--subversion/libsvn_subr/time.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/time.c b/subversion/libsvn_subr/time.c
new file mode 100644
index 0000000..ccd6269
--- /dev/null
+++ b/subversion/libsvn_subr/time.c
@@ -0,0 +1,277 @@
+/*
+ * time.c: time/date utilities
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <apr_pools.h>
+#include <apr_time.h>
+#include <apr_strings.h>
+#include "svn_io.h"
+#include "svn_time.h"
+#include "svn_utf.h"
+#include "svn_error.h"
+#include "svn_private_config.h"
+
+
+
+/*** Code. ***/
+
+/* Our timestamp strings look like this:
+ *
+ * "2002-05-07Thh:mm:ss.uuuuuuZ"
+ *
+ * The format is conformant with ISO-8601 and the date format required
+ * by RFC2518 for creationdate. It is a direct conversion between
+ * apr_time_t and a string, so converting to string and back retains
+ * the exact value.
+ */
+#define TIMESTAMP_FORMAT "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ"
+
+/* Our old timestamp strings looked like this:
+ *
+ * "Tue 3 Oct 2000 HH:MM:SS.UUU (day 277, dst 1, gmt_off -18000)"
+ *
+ * The idea is that they are conventionally human-readable for the
+ * first part, and then in parentheses comes everything else required
+ * to completely fill in an apr_time_exp_t: tm_yday, tm_isdst,
+ * and tm_gmtoff.
+ *
+ * This format is still recognized on input, for backward
+ * compatibility, but no longer generated.
+ */
+#define OLD_TIMESTAMP_FORMAT \
+ "%3s %d %3s %d %02d:%02d:%02d.%06d (day %03d, dst %d, gmt_off %06d)"
+
+/* Our human representation of dates looks like this:
+ *
+ * "2002-06-23 11:13:02 +0300 (Sun, 23 Jun 2002)"
+ *
+ * This format is used whenever time is shown to the user. It consists
+ * of a machine parseable, almost ISO-8601, part in the beginning -
+ * and a human explanatory part at the end. The machine parseable part
+ * is generated strictly by APR and our code, with a apr_snprintf. The
+ * human explanatory part is generated by apr_strftime, which means
+ * that its generation can be affected by locale, it can fail and it
+ * doesn't need to be constant in size. In other words, perfect to be
+ * converted to a configuration option later on.
+ */
+/* Maximum length for the date string. */
+#define SVN_TIME__MAX_LENGTH 80
+/* Machine parseable part, generated by apr_snprintf. */
+#define HUMAN_TIMESTAMP_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %+.2d%.2d"
+/* Human explanatory part, generated by apr_strftime as "Sat, 01 Jan 2000" */
+#define human_timestamp_format_suffix _(" (%a, %d %b %Y)")
+
+const char *
+svn_time_to_cstring(apr_time_t when, apr_pool_t *pool)
+{
+ apr_time_exp_t exploded_time;
+
+ /* We toss apr_status_t return value here -- for one thing, caller
+ should pass in good information. But also, where APR's own code
+ calls these functions it tosses the return values, and
+ furthermore their current implementations can only return success
+ anyway. */
+
+ /* We get the date in GMT now -- and expect the tm_gmtoff and
+ tm_isdst to be not set. We also ignore the weekday and yearday,
+ since those are not needed. */
+
+ apr_time_exp_gmt(&exploded_time, when);
+
+ /* It would be nice to use apr_strftime(), but APR doesn't give a
+ way to convert back, so we wouldn't be able to share the format
+ string between the writer and reader. */
+ return apr_psprintf(pool,
+ TIMESTAMP_FORMAT,
+ exploded_time.tm_year + 1900,
+ exploded_time.tm_mon + 1,
+ exploded_time.tm_mday,
+ exploded_time.tm_hour,
+ exploded_time.tm_min,
+ exploded_time.tm_sec,
+ exploded_time.tm_usec);
+}
+
+
+static apr_int32_t
+find_matching_string(char *str, apr_size_t size, const char strings[][4])
+{
+ apr_size_t i;
+
+ for (i = 0; i < size; i++)
+ if (strings[i] && (strcmp(str, strings[i]) == 0))
+ return (apr_int32_t) i;
+
+ return -1;
+}
+
+
+svn_error_t *
+svn_time_from_cstring(apr_time_t *when, const char *data, apr_pool_t *pool)
+{
+ apr_time_exp_t exploded_time;
+ apr_status_t apr_err;
+ char wday[4], month[4];
+ char *c;
+
+ /* Open-code parsing of the new timestamp format, as this
+ is a hot path for reading the entries file. This format looks
+ like: "2001-08-31T04:24:14.966996Z" */
+ exploded_time.tm_year = (apr_int32_t) strtol(data, &c, 10);
+ if (*c++ != '-') goto fail;
+ exploded_time.tm_mon = (apr_int32_t) strtol(c, &c, 10);
+ if (*c++ != '-') goto fail;
+ exploded_time.tm_mday = (apr_int32_t) strtol(c, &c, 10);
+ if (*c++ != 'T') goto fail;
+ exploded_time.tm_hour = (apr_int32_t) strtol(c, &c, 10);
+ if (*c++ != ':') goto fail;
+ exploded_time.tm_min = (apr_int32_t) strtol(c, &c, 10);
+ if (*c++ != ':') goto fail;
+ exploded_time.tm_sec = (apr_int32_t) strtol(c, &c, 10);
+ if (*c++ != '.') goto fail;
+ exploded_time.tm_usec = (apr_int32_t) strtol(c, &c, 10);
+ if (*c++ != 'Z') goto fail;
+
+ exploded_time.tm_year -= 1900;
+ exploded_time.tm_mon -= 1;
+ exploded_time.tm_wday = 0;
+ exploded_time.tm_yday = 0;
+ exploded_time.tm_isdst = 0;
+ exploded_time.tm_gmtoff = 0;
+
+ apr_err = apr_time_exp_gmt_get(when, &exploded_time);
+ if (apr_err == APR_SUCCESS)
+ return SVN_NO_ERROR;
+
+ return svn_error_create(SVN_ERR_BAD_DATE, NULL, NULL);
+
+ fail:
+ /* Try the compatibility option. This does not need to be fast,
+ as this format is no longer generated and the client will convert
+ an old-format entries file the first time it reads it. */
+ if (sscanf(data,
+ OLD_TIMESTAMP_FORMAT,
+ wday,
+ &exploded_time.tm_mday,
+ month,
+ &exploded_time.tm_year,
+ &exploded_time.tm_hour,
+ &exploded_time.tm_min,
+ &exploded_time.tm_sec,
+ &exploded_time.tm_usec,
+ &exploded_time.tm_yday,
+ &exploded_time.tm_isdst,
+ &exploded_time.tm_gmtoff) == 11)
+ {
+ exploded_time.tm_year -= 1900;
+ exploded_time.tm_yday -= 1;
+ /* Using hard coded limits for the arrays - they are going away
+ soon in any case. */
+ exploded_time.tm_wday = find_matching_string(wday, 7, apr_day_snames);
+ exploded_time.tm_mon = find_matching_string(month, 12, apr_month_snames);
+
+ apr_err = apr_time_exp_gmt_get(when, &exploded_time);
+ if (apr_err != APR_SUCCESS)
+ return svn_error_create(SVN_ERR_BAD_DATE, NULL, NULL);
+
+ return SVN_NO_ERROR;
+ }
+ /* Timestamp is something we do not recognize. */
+ else
+ return svn_error_create(SVN_ERR_BAD_DATE, NULL, NULL);
+}
+
+
+const char *
+svn_time_to_human_cstring(apr_time_t when, apr_pool_t *pool)
+{
+ apr_time_exp_t exploded_time;
+ apr_size_t len, retlen;
+ apr_status_t ret;
+ char *datestr, *curptr, human_datestr[SVN_TIME__MAX_LENGTH];
+
+ /* Get the time into parts */
+ ret = apr_time_exp_lt(&exploded_time, when);
+ if (ret)
+ return NULL;
+
+ /* Make room for datestring */
+ datestr = apr_palloc(pool, SVN_TIME__MAX_LENGTH);
+
+ /* Put in machine parseable part */
+ len = apr_snprintf(datestr,
+ SVN_TIME__MAX_LENGTH,
+ HUMAN_TIMESTAMP_FORMAT,
+ exploded_time.tm_year + 1900,
+ exploded_time.tm_mon + 1,
+ exploded_time.tm_mday,
+ exploded_time.tm_hour,
+ exploded_time.tm_min,
+ exploded_time.tm_sec,
+ exploded_time.tm_gmtoff / (60 * 60),
+ (abs(exploded_time.tm_gmtoff) / 60) % 60);
+
+ /* If we overfilled the buffer, just return what we got. */
+ if (len >= SVN_TIME__MAX_LENGTH)
+ return datestr;
+
+ /* Calculate offset to the end of the machine parseable part. */
+ curptr = datestr + len;
+
+ /* Put in human explanatory part */
+ ret = apr_strftime(human_datestr,
+ &retlen,
+ SVN_TIME__MAX_LENGTH - len,
+ human_timestamp_format_suffix,
+ &exploded_time);
+
+ /* If there was an error, ensure that the string is zero-terminated. */
+ if (ret || retlen == 0)
+ *curptr = '\0';
+ else
+ {
+ const char *utf8_string;
+ svn_error_t *err;
+
+ err = svn_utf_cstring_to_utf8(&utf8_string, human_datestr, pool);
+ if (err)
+ {
+ *curptr = '\0';
+ svn_error_clear(err);
+ }
+ else
+ apr_cpystrn(curptr, utf8_string, SVN_TIME__MAX_LENGTH - len);
+ }
+
+ return datestr;
+}
+
+
+void
+svn_sleep_for_timestamps(void)
+{
+ svn_io_sleep_for_timestamps(NULL, NULL);
+}
OpenPOWER on IntegriCloud