Merge PR #27799 into master

* refs/pull/27799/head:
	common/utime: don't pass %z to utime if there is a 'Z'
	test: test json encode/decode of utime_t
	mgr/ssh: parse new datetime
	mgr/devicehealth: parse new datetime
	mgr/crash: parse both old and new timestamp formats
	mgr/telemetry: use cluster-provided timestamp unmolested
	mon/MonMap: dump timestamps in UTC
	qa/tasks/ceph: tolerate 'T' or ' ' as date and time separator
	PendingReleaseNotes: note about change to ISO 8601 throughout
	test/cli: update regexs for timestamps
	log/LogClock: render timestamp in ISO 8601 format
	common/ceph_time: stringify in ISO 8601 format
	unittest_utime: add tests
	common/utime: make parse() handle (our) ISO 8601 output
	include/utime: make default string rendering ISO 8601 conformant
	include/utime: remove unused s[n]printf methods
	include/utime: make gmtime() output conformant ISO 8601

Reviewed-by: Kanika Murarka <kmurarka@redhat.com>
Reviewed-by: Adam C. Emerson <aemerson@redhat.com>
This commit is contained in:
Sage Weil 2019-05-29 15:27:52 -05:00
commit 5686d1ccd5
25 changed files with 243 additions and 89 deletions

View File

@ -70,3 +70,19 @@
discovery. Update of zabbix_template.xml is needed
to receive per-pool (read/write throughput, diskspace usage)
and per-osd (latency, status, pgs) statistics
* The format of all date + time stamps has been modified to fully
conform to ISO 8601. The old format (``YYYY-MM-DD
HH:MM:SS.ssssss``) excluded the ``T`` separator between the date and
time and was rendered using the local time zone without any explicit
indication. The new format includes the separator as well as a
``+nnnn`` or ``-nnnn`` suffix to indicate the time zone, or a ``Z``
suffix if the time is UTC. For example,
``2019-04-26T18:40:06.225953+0100``.
Any code or scripts that was previously parsing date and/or time
values from the JSON or XML structure CLI output should be checked
to ensure it can handle ISO 8601 conformant values. Any code
parsing date or time values from the unstructured human-readable
output should be modified to parse the structured output instead, as
the human-readable output may change without notice.

View File

@ -1291,7 +1291,9 @@ def osd_scrub_pgs(ctx, config):
loop = False
thiscnt = 0
for (pgid, tmval) in timez:
pgtm = time.strptime(tmval[0:tmval.find('.')], '%Y-%m-%d %H:%M:%S')
t = tmval[0:tmval.find('.')]
t.replace(' ', 'T')
pgtm = time.strptime(t, '%Y-%m-%dT%H:%M:%S')
if pgtm > check_time_now:
thiscnt += 1
else:

View File

@ -111,19 +111,22 @@ namespace ceph {
char oldfill = m.fill();
m.fill('0');
// localtime. this looks like an absolute time.
// aim for http://en.wikipedia.org/wiki/ISO_8601
// conform to http://en.wikipedia.org/wiki/ISO_8601
struct tm bdt;
time_t tt = Clock::to_time_t(t);
localtime_r(&tt, &bdt);
char tz[32] = { 0 };
strftime(tz, sizeof(tz), "%z", &bdt);
m << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
<< '-' << std::setw(2) << (bdt.tm_mon+1)
<< '-' << std::setw(2) << bdt.tm_mday
<< ' '
<< 'T'
<< std::setw(2) << bdt.tm_hour
<< ':' << std::setw(2) << bdt.tm_min
<< ':' << std::setw(2) << bdt.tm_sec
<< "." << std::setw(6) << duration_cast<microseconds>(
t.time_since_epoch() % seconds(1));
t.time_since_epoch() % seconds(1)).count()
<< tz;
m.fill(oldfill);
m.unsetf(std::ios::right);
return m;

View File

@ -241,14 +241,14 @@ public:
out << (long)sec() << "." << std::setw(6) << usec();
} else {
// this looks like an absolute time.
// aim for http://en.wikipedia.org/wiki/ISO_8601
// conform to http://en.wikipedia.org/wiki/ISO_8601
struct tm bdt;
time_t tt = sec();
gmtime_r(&tt, &bdt);
out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
<< '-' << std::setw(2) << (bdt.tm_mon+1)
<< '-' << std::setw(2) << bdt.tm_mday
<< ' '
<< 'T'
<< std::setw(2) << bdt.tm_hour
<< ':' << std::setw(2) << bdt.tm_min
<< ':' << std::setw(2) << bdt.tm_sec;
@ -270,14 +270,14 @@ public:
out << (long)sec() << "." << std::setw(6) << usec();
} else {
// this looks like an absolute time.
// aim for http://en.wikipedia.org/wiki/ISO_8601
// conform to http://en.wikipedia.org/wiki/ISO_8601
struct tm bdt;
time_t tt = sec();
gmtime_r(&tt, &bdt);
out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
<< '-' << std::setw(2) << (bdt.tm_mon+1)
<< '-' << std::setw(2) << bdt.tm_mday
<< ' '
<< 'T'
<< std::setw(2) << bdt.tm_hour
<< ':' << std::setw(2) << bdt.tm_min
<< ':' << std::setw(2) << bdt.tm_sec;
@ -299,7 +299,6 @@ public:
out << (long)sec() << "." << std::setw(6) << usec();
} else {
// this looks like an absolute time.
// aim for http://en.wikipedia.org/wiki/ISO_8601
struct tm bdt;
time_t tt = sec();
gmtime_r(&tt, &bdt);
@ -325,50 +324,32 @@ public:
out << (long)sec() << "." << std::setw(6) << usec();
} else {
// this looks like an absolute time.
// aim for http://en.wikipedia.org/wiki/ISO_8601
// conform to http://en.wikipedia.org/wiki/ISO_8601
struct tm bdt;
time_t tt = sec();
localtime_r(&tt, &bdt);
out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
<< '-' << std::setw(2) << (bdt.tm_mon+1)
<< '-' << std::setw(2) << bdt.tm_mday
<< ' '
<< 'T'
<< std::setw(2) << bdt.tm_hour
<< ':' << std::setw(2) << bdt.tm_min
<< ':' << std::setw(2) << bdt.tm_sec;
out << "." << std::setw(6) << usec();
//out << '_' << bdt.tm_zone;
char buf[32] = { 0 };
strftime(buf, sizeof(buf), "%z", &bdt);
out << buf;
}
out.fill(oldfill);
out.unsetf(std::ios::right);
return out;
}
int sprintf(char *out, int outlen) const {
struct tm bdt;
time_t tt = sec();
localtime_r(&tt, &bdt);
return ::snprintf(out, outlen,
"%04d-%02d-%02d %02d:%02d:%02d.%06ld",
bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday,
bdt.tm_hour, bdt.tm_min, bdt.tm_sec, usec());
}
static int snprintf(char *out, int outlen, time_t tt) {
struct tm bdt;
localtime_r(&tt, &bdt);
return ::snprintf(out, outlen,
"%04d-%02d-%02d %02d:%02d:%02d",
bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday,
bdt.tm_hour, bdt.tm_min, bdt.tm_sec);
}
static int invoke_date(const std::string& date_str, utime_t *result) {
char buf[256];
SubProcess bin_date("/bin/date", SubProcess::CLOSE, SubProcess::PIPE, SubProcess::KEEP);
SubProcess bin_date("/bin/date", SubProcess::CLOSE, SubProcess::PIPE,
SubProcess::KEEP);
bin_date.add_cmd_args("-d", date_str.c_str(), "+%s %N", NULL);
int r = bin_date.spawn();
@ -402,17 +383,45 @@ public:
const char *p = strptime(date.c_str(), "%Y-%m-%d", &tm);
if (p) {
if (*p == ' ') {
if (*p == ' ' || *p == 'T') {
p++;
p = strptime(p, " %H:%M:%S", &tm);
if (!p)
// strptime doesn't understand fractional/decimal seconds, and
// it also only takes format chars or literals, so we have to
// get creative.
char fmt[32] = {0};
strncpy(fmt, p, sizeof(fmt) - 1);
fmt[0] = '%';
fmt[1] = 'H';
fmt[2] = ':';
fmt[3] = '%';
fmt[4] = 'M';
fmt[6] = '%';
fmt[7] = 'S';
const char *subsec = 0;
char *q = fmt + 8;
if (*q == '.') {
++q;
subsec = p + 9;
q = fmt + 9;
while (*q && isdigit(*q)) {
++q;
}
}
// look for tz...
if (*q == '-' || *q == '+') {
*q = '%';
*(q+1) = 'z';
*(q+2) = 0;
}
p = strptime(p, fmt, &tm);
if (!p) {
return -EINVAL;
if (nsec && *p == '.') {
++p;
}
if (nsec && subsec) {
unsigned i;
char buf[10]; /* 9 digit + null termination */
for (i = 0; (i < sizeof(buf) - 1) && isdigit(*p); ++i, ++p) {
buf[i] = *p;
for (i = 0; (i < sizeof(buf) - 1) && isdigit(*subsec); ++i, ++subsec) {
buf[i] = *subsec;
}
for (; i < sizeof(buf) - 1; ++i) {
buf[i] = '0';
@ -439,10 +448,19 @@ public:
*nsec = (uint64_t)usec * 1000;
}
}
// apply the tm_gmtoff manually below, since none of mktime,
// gmtime, and localtime seem to do it. zero it out here just in
// case some other libc *does* apply it. :(
auto gmtoff = tm.tm_gmtoff;
tm.tm_gmtoff = 0;
time_t t = internal_timegm(&tm);
if (epoch)
*epoch = (uint64_t)t;
*epoch -= gmtoff;
if (out_date) {
char buf[32];
strftime(buf, sizeof(buf), "%F", &tm);

View File

@ -131,18 +131,20 @@ inline int append_time(const log_time& t, char *out, int outlen) {
auto tv = log_clock::to_timeval(t);
std::tm bdt;
localtime_r(&tv.tv_sec, &bdt);
char tz[32] = { 0 };
strftime(tz, sizeof(tz), "%z", &bdt);
int r;
if (coarse) {
r = std::snprintf(out, outlen, "%04d-%02d-%02d %02d:%02d:%02d.%03ld",
r = std::snprintf(out, outlen, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld%s",
bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday,
bdt.tm_hour, bdt.tm_min, bdt.tm_sec,
static_cast<long>(tv.tv_usec / 1000));
static_cast<long>(tv.tv_usec / 1000), tz);
} else {
r = std::snprintf(out, outlen, "%04d-%02d-%02d %02d:%02d:%02d.%06ld",
r = std::snprintf(out, outlen, "%04d-%02d-%02dT%02d:%02d:%02d.%06ld%s",
bdt.tm_year + 1900, bdt.tm_mon + 1, bdt.tm_mday,
bdt.tm_hour, bdt.tm_min, bdt.tm_sec,
static_cast<long>(tv.tv_usec));
static_cast<long>(tv.tv_usec), tz);
}
// Since our caller just adds the return value to something without
// checking it…

View File

@ -268,7 +268,7 @@ TEST(Log, TimeFormat)
ceph::logging::append_time(t, buf, buflen);
auto c = std::strrchr(buf, '.');
ASSERT_NE(c, nullptr);
ASSERT_EQ(3u, strlen(c + 1));
ASSERT_EQ(8u, strlen(c + 1));
}
{
clock.refine();
@ -276,7 +276,7 @@ TEST(Log, TimeFormat)
ceph::logging::append_time(t, buf, buflen);
auto c = std::strrchr(buf, '.');
ASSERT_NE(c, nullptr);
ASSERT_EQ(6u, std::strlen(c + 1));
ASSERT_EQ(11u, std::strlen(c + 1));
}
}

View File

@ -331,8 +331,8 @@ void MonMap::dump(Formatter *f) const
{
f->dump_unsigned("epoch", epoch);
f->dump_stream("fsid") << fsid;
f->dump_stream("modified") << last_changed;
f->dump_stream("created") << created;
last_changed.gmtime(f->dump_stream("modified"));
created.gmtime(f->dump_stream("created"));
f->dump_unsigned("min_mon_release", ceph::to_integer<unsigned>(min_mon_release));
f->dump_string("min_mon_release_name", ceph::to_string(min_mon_release));
f->open_object_section("features");

View File

@ -6,7 +6,8 @@ import six
from collections import defaultdict
DATEFMT = '%Y-%m-%d %H:%M:%S.%f'
DATEFMT = '%Y-%m-%dT%H:%M:%S.%f'
OLD_DATEFMT = '%Y-%m-%d %H:%M:%S.%f'
class Module(MgrModule):
@ -36,7 +37,10 @@ class Module(MgrModule):
def time_from_string(timestr):
# drop the 'Z' timezone indication, it's always UTC
timestr = timestr.rstrip('Z')
return datetime.datetime.strptime(timestr, DATEFMT)
try:
return datetime.datetime.strptime(timestr, DATEFMT)
except ValueError:
return datetime.datetime.strptime(timestr, OLD_DATEFMT)
def timestamp_filter(self, f):
"""
@ -171,10 +175,14 @@ class Module(MgrModule):
def self_test(self):
# test time conversion
timestr = '2018-06-22 20:35:38.058818Z'
timestr = '2018-06-22T20:35:38.058818Z'
old_timestr = '2018-06-22 20:35:38.058818Z'
dt = self.time_from_string(timestr)
if dt != datetime.datetime(2018, 6, 22, 20, 35, 38, 58818):
raise RuntimeError('time_from_string() failed')
dt = self.time_from_string(old_timestr)
if dt != datetime.datetime(2018, 6, 22, 20, 35, 38, 58818):
raise RuntimeError('time_from_string() (old) failed')
COMMANDS = [
{

View File

@ -478,11 +478,11 @@ class Module(MgrModule):
dev['life_expectancy_max'] == '0.000000':
continue
# life_expectancy_(min/max) is in the format of:
# '%Y-%m-%d %H:%M:%S.%f', e.g.:
# '2019-01-20 21:12:12.000000'
# '%Y-%m-%dT%H:%M:%S.%f%z', e.g.:
# '2019-01-20T21:12:12.000000Z'
life_expectancy_max = datetime.strptime(
dev['life_expectancy_max'],
'%Y-%m-%d %H:%M:%S.%f')
'%Y-%m-%dT%H:%M:%S.%f%z')
self.log.debug('device %s expectancy max %s', dev,
life_expectancy_max)

View File

@ -18,7 +18,7 @@ except ImportError as e:
remoto = None
remoto_import_error = str(e)
DATEFMT = '%Y-%m-%d %H:%M:%S.%f'
DATEFMT = '%Y-%m-%dT%H:%M:%S.%f%z'
# high-level TODO:
# - bring over some of the protections from ceph-deploy that guard against
@ -183,8 +183,6 @@ class SSHOrchestrator(MgrModule, orchestrator.Orchestrator):
@staticmethod
def time_from_string(timestr):
# drop the 'Z' timezone indication, it's always UTC
timestr = timestr.rstrip('Z')
return datetime.datetime.strptime(timestr, DATEFMT)
def _get_cluster_fsid(self):

View File

@ -122,10 +122,6 @@ class Module(MgrModule):
self.get_module_option(opt['name']))
self.log.debug(' %s = %s', opt['name'], getattr(self, opt['name']))
@staticmethod
def parse_timestamp(timestamp):
return datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S.%f')
def load(self):
self.last_upload = self.get_store('last_upload', None)
if self.last_upload is not None:
@ -205,7 +201,7 @@ class Module(MgrModule):
df = self.get('df')
report['report_id'] = self.report_id
report['created'] = self.parse_timestamp(mon_map['created']).isoformat()
report['created'] = mon_map['created']
report['mon'] = {
'count': len(mon_map['mons']),

View File

@ -860,6 +860,12 @@ add_executable(unittest_ipaddr
add_ceph_unittest(unittest_ipaddr)
target_link_libraries(unittest_ipaddr mon global)
# unittest_utime
add_executable(unittest_utime
test_utime.cc)
add_ceph_unittest(unittest_utime)
target_link_libraries(unittest_utime mon global)
# unittest_texttable
add_executable(unittest_texttable
test_texttable.cc

View File

@ -18,8 +18,8 @@
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)
0: v1:2.3.4.5:6789/0 mon.foo

View File

@ -21,8 +21,8 @@
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)
0: v1:2.3.4.5:6789/0 mon.foo
1: [v2:172.21.15.68:6791/0,v1:172.21.15.68:6792/0] mon.fiz

View File

@ -15,8 +15,8 @@
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)
0: v1:2.3.4.5:6789/0 mon.foo
@ -35,6 +35,6 @@
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)

View File

@ -7,14 +7,14 @@
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)
$ monmaptool --print -- mymonmap
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)

View File

@ -7,7 +7,7 @@
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)
0: v1:2.3.4.5:6789/0 mon.foo

View File

@ -16,8 +16,8 @@
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)
0: v1:2.3.4.5:6789/0 mon.foo

View File

@ -14,8 +14,8 @@
monmaptool: monmap file mymonmap
epoch 0
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
last_changed \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
min_mon_release 0 (unknown)
$ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)"

View File

@ -16,8 +16,8 @@
osdmaptool: osdmap file 'myosdmap'
epoch 1
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
modified \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
modified \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
flags
crush_version 1
full_ratio 0
@ -42,8 +42,8 @@
osdmaptool: osdmap file 'myosdmap'
epoch 1
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
modified \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
modified \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
flags
crush_version 1
full_ratio 0

View File

@ -76,8 +76,8 @@
osdmaptool: osdmap file 'myosdmap'
epoch 1
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
modified \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
modified \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
flags
crush_version 1
full_ratio 0

View File

@ -788,8 +788,8 @@
osdmaptool: osdmap file 'om'
epoch 1
fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re)
created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
modified \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re)
created \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
modified \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+.\d\d\d\d (re)
flags
crush_version 1
full_ratio 0

View File

@ -16,6 +16,7 @@
#include <gtest/gtest.h>
#include "common/ceph_json.h"
#include "common/Clock.h"
#include <sstream>
@ -56,3 +57,25 @@ TEST(formatter, bug_37706) {
ASSERT_EQ(pgs.back(), "1.0");
}
TEST(formatter, utime)
{
JSONFormatter formatter;
utime_t input = ceph_clock_now();
input.gmtime_nsec(formatter.dump_stream("timestamp"));
bufferlist bl;
formatter.flush(bl);
JSONParser parser;
EXPECT_TRUE(parser.parse(bl.c_str(), bl.length()));
cout << input << " -> '" << std::string(bl.c_str(), bl.length())
<< std::endl;
utime_t output;
decode_json_obj(output, &parser);
cout << " -> " << output << std::endl;
EXPECT_EQ(input.sec(), output.sec());
EXPECT_EQ(input.nsec(), output.nsec());
}

View File

@ -18,6 +18,7 @@
#include "common/ceph_time.h"
#include "include/rados.h"
#include "gtest/gtest.h"
#include "include/stringify.h"
using ceph::real_clock;
@ -176,3 +177,18 @@ TEST(TimePoints, SignedSubtraciton) {
ASSERT_GT(cmtb - cmta, ceph::signedspan::zero());
ASSERT_GT((cmtb - cmta).count(), 0);
}
TEST(TimePoints, stringify) {
ceph::real_clock::time_point tp(seconds(1556122013) + nanoseconds(39923122));
string s = stringify(tp);
ASSERT_EQ(s.size(), strlen("2019-04-24T11:06:53.039923-0500"));
ASSERT_TRUE(s[26] == '-' || s[26] == '+');
ASSERT_EQ(s.substr(0, 9), "2019-04-2");
ceph::coarse_real_clock::time_point ctp(seconds(1556122013) +
nanoseconds(399000000));
s = stringify(ctp);
ASSERT_EQ(s.size(), strlen("2019-04-24T11:06:53.399000-0500"));
ASSERT_TRUE(s[26] == '-' || s[26] == '+');
ASSERT_EQ(s.substr(0, 9), "2019-04-2");
}

66
src/test/test_utime.cc Normal file
View File

@ -0,0 +1,66 @@
#include "include/utime.h"
#include "global/global_context.h"
#include "gtest/gtest.h"
#include "include/stringify.h"
#include "common/ceph_context.h"
TEST(utime_t, localtime)
{
utime_t t(1556122013, 839991182);
string s = stringify(t);
cout << s << std::endl;
// time zone may vary where unit test is run, so be cirsumspect...
ASSERT_EQ(s.size(), strlen("2019-04-24T11:06:53.839991-0500"));
ASSERT_TRUE(s[26] == '-' || s[26] == '+');
ASSERT_EQ(s.substr(0, 9), "2019-04-2");
}
TEST(utime_t, gmtime)
{
utime_t t(1556122013, 39991182);
{
ostringstream ss;
t.gmtime(ss);
ASSERT_EQ(ss.str(), "2019-04-24T16:06:53.039991Z");
}
{
ostringstream ss;
t.gmtime_nsec(ss);
ASSERT_EQ(ss.str(), "2019-04-24T16:06:53.039991182Z");
}
}
TEST(utime_t, asctime)
{
utime_t t(1556122013, 839991182);
ostringstream ss;
t.asctime(ss);
string s = ss.str();
ASSERT_EQ(s, "Wed Apr 24 16:06:53 2019");
}
const char *v[][2] = {
{ "2019-04-24T16:06:53.039991Z", "2019-04-24T16:06:53.039991Z" },
{ "2019-04-24 16:06:53.039991Z", "2019-04-24T16:06:53.039991Z" },
{ "2019-04-24 16:06:53.039991+0000", "2019-04-24T16:06:53.039991Z" },
{ "2019-04-24 16:06:53.039991-0100", "2019-04-24T17:06:53.039991Z" },
{ "2019-04-24 16:06:53.039991+0430", "2019-04-24T11:36:53.039991Z" },
{ "2019-04-24 16:06:53+0000", "2019-04-24T16:06:53.000000Z" },
{ "2019-04-24T16:06:53-0100", "2019-04-24T17:06:53.000000Z" },
{ "2019-04-24 16:06:53+0430", "2019-04-24T11:36:53.000000Z" },
{ "2019-04-24", "2019-04-24T00:00:00.000000Z" },
{ 0, 0 },
};
TEST(utime_t, parse_date)
{
for (unsigned i = 0; v[i][0]; ++i) {
cout << v[i][0] << " -> " << v[i][1] << std::endl;
utime_t t;
bool r = t.parse(string(v[i][0]));
ASSERT_TRUE(r);
ostringstream ss;
t.gmtime(ss);
ASSERT_EQ(ss.str(), v[i][1]);
}
}