pull/1/merge
Daniel Richman 2012-08-20 15:36:53 +01:00
rodzic cce6d3f2fa
commit 6ed70acd99
7 zmienionych plików z 732 dodań i 8 usunięć

3
.gitignore vendored
Wyświetl plik

@ -1,4 +1 @@
cpp_connector
cpp_connector_threaded
extractor
*.o

Wyświetl plik

@ -6,15 +6,19 @@ jsoncpp_cflags := $(shell pkg-config --cflags jsoncpp)
jsoncpp_libs := $(shell pkg-config --libs jsoncpp)
CFLAGS = -pthread -O2 -Wall -Werror -pedantic -Wno-long-long \
-Wno-variadic-macros -Isrc $(jsoncpp_cflags)
-Wno-variadic-macros -Isrc $(jsoncpp_cflags)
upl_libs = -pthread $(jsoncpp_libs) -lcurl -lssl
ext_libs = $(jsoncpp_libs)
rfc_libs = $(jsoncpp_libs)
test_py_files = tests/test_uploader.py tests/test_extractor.py
headers = src/CouchDB.h src/EZ.h src/Uploader.h src/UploaderThread.h \
headers = src/CouchDB.h src/EZ.h src/RFC3339.h \
src/Uploader.h src/UploaderThread.h \
src/Extractor.h src/UKHASExtractor.h \
tests/test_extractor_mocks.h
upl_cxxfiles = src/CouchDB.cxx src/EZ.cxx src/Uploader.cxx
tests/test_extractor_mocks.h
rfc_cxxfiles = src/RFC3339.cxx tests/test_rfc3339_main.cxx
rfc_binary = tests/rfc3339
upl_cxxfiles = src/CouchDB.cxx src/EZ.cxx src/RFC3339.cxx src/Uploader.cxx
upl_thr_cflags = -DTHREADED
upl_nrm_binary = tests/cpp_connector
upl_nrm_objects = tests/test_uploader_main.o
@ -28,6 +32,7 @@ ext_mock_cflags = -include tests/test_extractor_mocks.h
CXXFLAGS = $(CFLAGS)
upl_objects = $(patsubst %.cxx,%.o,$(upl_cxxfiles))
ext_objects = $(patsubst %.cxx,%.ext_mock.o,$(ext_cxxfiles))
rfc_objects = $(patsubst %.cxx,%.o,$(rfc_cxxfiles))
%.o : %.cxx $(headers)
g++ -c $(CXXFLAGS) -o $@ $<
@ -47,7 +52,11 @@ $(upl_thr_binary) : $(upl_objects) $(upl_thr_objects)
$(ext_binary) : $(ext_objects)
g++ $(CXXFLAGS) -o $@ $(ext_objects) $(ext_libs)
test : $(upl_nrm_binary) $(upl_thr_binary) $(ext_binary) $(test_py_files)
$(rfc_binary) : $(rfc_objects)
g++ $(CXXFLAGS) -o $@ $(rfc_objects) $(rfc_libs)
test : $(upl_nrm_binary) $(upl_thr_binary) $(ext_binary) $(rfc_binary) \
$(test_py_files)
nosetests
clean :

324
src/RFC3339.cxx 100644
Wyświetl plik

@ -0,0 +1,324 @@
/* Copyright 2012 (C) Daniel Richman. License: GNU GPL 3; see LICENSE. */
#include "RFC3339.h"
#include <string>
#include <sstream>
#include <ctime>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <stdexcept>
#include <iomanip>
using namespace std;
namespace RFC3339 {
bool validate_rfc3339(const string &rfc3339)
{
try
{
rfc3339_to_timestamp(rfc3339);
}
catch (InvalidFormat e)
{
return false;
}
return true;
}
/* Class to be used when extracting from an istream that consumes a single
* delimiter character */
class Delim
{
char expect;
public:
Delim(char _e) : expect(_e) {};
~Delim() {};
void extract(istream &in);
};
/* Delim can't be a reference here because in its intended use it is
* constructed on the spot like this ... >> Delim('-') >> ... and therefore
* it does not have an address */
istream & operator>>(istream &in, Delim delim)
{
delim.extract(in);
return in;
}
void Delim::extract(istream &in)
{
if (!in.good() || in.get() != expect)
in.setstate(ios_base::badbit);
}
/* Extracts an integer of particular length, containing the digits 0-9 only */
class StrictInt
{
size_t length;
int &target;
bool check_range;
int min, max;
public:
StrictInt(size_t _l, int &_t)
: length(_l), target(_t), check_range(false), min(0), max(0) {};
StrictInt(size_t _l, int &_t, int _min, int _max)
: length(_l), target(_t), check_range(true), min(_min), max(_max) {};
~StrictInt() {};
void extract(istream &in);
};
istream & operator>>(istream &in, StrictInt tgt)
{
tgt.extract(in);
return in;
}
void StrictInt::extract(istream &in)
{
if (!in.good())
{
in.setstate(ios_base::badbit);
return;
}
char *temp = new char[length + 1];
in.read(temp, length);
temp[length] = 0;
if (in.fail() || strlen(temp) != length)
return;
istringstream temp_ss(temp);
temp_ss >> target;
if (temp_ss.fail() || temp_ss.peek() != EOF)
in.setstate(ios_base::badbit);
if (check_range && (target < min || target > max))
in.setstate(ios_base::badbit);
}
static int mdays[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static int mydays[] = {0, 0, 31, 59, 90, 120, 151, 181, 212,
243, 273, 304, 334};
/* Returns the number of multiples of n in [a,b] */
static int multiples_between(int n, int a, int b)
{
if (a % n != 0)
a += n - (a % n);
b -= (b % n);
return ((b - a) / n) + 1;
}
/* Not provided on all platforms :-(. */
static long long int my_timegm(int year, int month, int mday,
int hour, int min, int sec)
{
/* I don't know the best way to get everything promoted to 64bit
* integers in the final line, this might do it */
long long int epoch_days = 0;
bool leap_year = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
if (year > 1970)
{
int leap_years = multiples_between(4, 1970, year - 1)
- multiples_between(100, 1970, year - 1)
+ multiples_between(400, 1970, year - 1);
epoch_days = ((year - 1970) * 365) + leap_years;
}
else if (year < 1970)
{
int leap_years = multiples_between(4, year, 1970 - 1)
- multiples_between(100, year, 1970 - 1)
+ multiples_between(400, year, 1970 - 1);
epoch_days = -(((1970 - year) * 365) + leap_years);
}
epoch_days += mydays[month];
if (month > 2 && leap_year)
epoch_days++;
epoch_days += mday - 1;
return (((((epoch_days * 24) + hour) * 60) + min) * 60) + sec;
}
static long long int my_timegm(struct tm tm)
{
return my_timegm(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
/* Thin wrappers adding exceptions and range checks */
static struct tm my_localtime(long long int timestamp)
{
time_t timestamp_s = timestamp;
if (timestamp_s != timestamp)
throw out_of_range("timestamp too large for time_t");
struct tm tm;
if (localtime_r(&timestamp_s, &tm) == NULL)
throw runtime_error("localtime_r failed");
return tm;
}
static struct tm my_gmtime(long long int timestamp)
{
time_t timestamp_s = timestamp;
if (timestamp_s != timestamp)
throw out_of_range("timestamp too large for time_t");
struct tm tm;
if (gmtime_r(&timestamp_s, &tm) == NULL)
throw runtime_error("gmtime_r failed");
return tm;
}
long long int rfc3339_to_timestamp(const string &rfc3339)
{
istringstream temp(rfc3339);
int year, month, mday, hour, min, sec;
temp >> StrictInt(4, year) >> Delim('-')
>> StrictInt(2, month, 1, 12) >> Delim('-')
>> StrictInt(2, mday, 1, 31) >> Delim('T')
>> StrictInt(2, hour, 0, 23) >> Delim(':')
>> StrictInt(2, min, 0, 59) >> Delim(':')
>> StrictInt(2, sec, 0, 59);
if (temp.fail() || temp.eof() || temp.tellg() != 19)
throw InvalidFormat();
bool leap_year = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
int this_mdays = mdays[month];
if (leap_year && month == 2)
this_mdays++;
if (mday > this_mdays)
throw InvalidFormat();
if (temp.peek() == '.')
{
/* discard seconds part */
do
temp.get();
while (temp.good() && temp.peek() >= '0' && temp.peek() <= '9');
}
int offset = 0;
int offset_first_char = temp.get();
if (offset_first_char == 'Z')
{
/* UTC offset, 0 */
}
else if (offset_first_char == '+' || offset_first_char == '-')
{
int offset_hours, offset_minutes;
temp >> StrictInt(2, offset_hours, 0, 23) >> Delim(':')
>> StrictInt(2, offset_minutes, 0, 59);
if (temp.fail())
throw InvalidFormat();
offset = offset_hours * 3600 + offset_minutes * 60;
if (offset_first_char == '-')
offset = -offset;
}
else
{
throw InvalidFormat();
}
if (temp.peek() != EOF)
throw InvalidFormat();
return my_timegm(year, month, mday, hour, min, sec) - offset;
}
static string make_datestring_start(int year, int month, int mday,
int hour, int min, int sec)
{
ostringstream temp;
temp.fill('0');
temp << setw(4) << year << '-'
<< setw(2) << month << '-'
<< setw(2) << mday << 'T'
<< setw(2) << hour << ':'
<< setw(2) << min << ':'
<< setw(2) << sec;
return temp.str();
}
static string make_datestring_start(struct tm tm)
{
return make_datestring_start(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
string timestamp_to_rfc3339_utcoffset(long long int timestamp)
{
struct tm tm = my_gmtime(timestamp);
string ret = make_datestring_start(tm) + "Z";
#ifndef NDEBUG
if (rfc3339_to_timestamp(ret) != timestamp)
throw runtime_error("reparse sanity check failed");
#endif
return ret;
}
string timestamp_to_rfc3339_localoffset(long long int timestamp)
{
struct tm tm = my_localtime(timestamp);
struct tm gm_tm = my_gmtime(timestamp);
int offset = my_timegm(tm) - my_timegm(gm_tm);
if (abs(offset) % 60 != 0)
throw runtime_error("Your local offset is not a whole minute");
int offset_minutes = abs(offset) / 60;
int offset_hours = offset_minutes / 60;
offset_minutes %= 60;
ostringstream temp;
temp << make_datestring_start(tm);
temp.fill('0');
temp << (offset < 0 ? '-' : '+')
<< setw(2) << offset_hours << ':'
<< setw(2) << offset_minutes;
string ret = temp.str();
#ifndef NDEBUG
if (rfc3339_to_timestamp(ret) != timestamp)
throw runtime_error("reparse sanity check failed");
#endif
return ret;
}
string now_to_rfc3339_utcoffset()
{
return timestamp_to_rfc3339_utcoffset(time(NULL));
}
string now_to_rfc3339_localoffset()
{
return timestamp_to_rfc3339_localoffset(time(NULL));
}
} /* namespace RFC3339 */

38
src/RFC3339.h 100644
Wyświetl plik

@ -0,0 +1,38 @@
/* Copyright 2012 (C) Daniel Richman. License: GNU GPL 3; see LICENSE. */
#ifndef HABITAT_RFC3339_H
#include <string>
#include <ctime>
#include <stdexcept>
using namespace std;
namespace RFC3339 {
class InvalidFormat : public invalid_argument
{
public:
InvalidFormat() : invalid_argument("RFC3339::InvalidFormat") {};
InvalidFormat(const string &what) : invalid_argument(what) {};
};
/*
* This is basically a clone of habitat.utils.rfc3339, except all timestamps
* are time_t (may have restrictions on 32 bit, and they cannot be floats
* like they can in python)
*
* You should call tzset() before using either _localoffset function, since it
* calls localtime_r().
*/
bool validate_rfc3339(const string &rfc3339);
long long int rfc3339_to_timestamp(const string &rfc3339);
string timestamp_to_rfc3339_utcoffset(long long int timestamp);
string timestamp_to_rfc3339_localoffset(long long int timestamp);
string now_to_rfc3339_utcoffset();
string now_to_rfc3339_localoffset();
} /* namespace RFC3339 */
#endif /* HABITAT_RFC3339_H */

5
tests/.gitignore vendored
Wyświetl plik

@ -1,2 +1,7 @@
test_extractor.pyc
test_uploader.pyc
test_rfc3339.pyc
extractor
cpp_connector
cpp_connector_threaded
rfc3339

Wyświetl plik

@ -0,0 +1,276 @@
# Copyright 2012 (C) Adam Greig, Daniel Richman
#
# This file is part of habitat.
#
# habitat is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# habitat is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with habitat. If not, see <http://www.gnu.org/licenses/>.
import os
import time
import subprocess
import json
from nose import SkipTest
# Identical to habitat's rfc3339 tests, except with a class that proxies
# calls to an executable and without the float tests.
class ProxyFunction(object):
def __init__(self, process, name):
self.p = process
self.name = name
def __call__(self, *args):
args = [self.name] + list(args)
self.p.stdin.write(json.dumps(args))
self.p.stdin.write("\n")
return json.loads(self.p.stdout.readline())
class ProxyFunctionModule(object):
# i.e., a module that contains functions only
def __init__(self, command, environ=None):
self.closed = False
self.p = subprocess.Popen(command, env=environ,
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
for n in ["validate_rfc3339", "rfc3339_to_timestamp",
"timestamp_to_rfc3339_utcoffset",
"timestamp_to_rfc3339_localoffset",
"now_to_rfc3339_utcoffset",
"now_to_rfc3339_localoffset"]:
setattr(self, n, ProxyFunction(self.p, n))
def close(self, quiet=False):
self.p.stdin.close()
ret = self.p.wait()
if not quiet:
assert ret == 0
def __del__(self):
if not self.closed:
self.close(True)
class TestValidateRFC3339(object):
def setup(self):
self.mod = ProxyFunctionModule("tests/rfc3339")
self.validate = self.mod.validate_rfc3339
def teardown(self):
self.mod.close()
def test_rejects_bad_format(self):
assert self.validate("asdf") == False
assert self.validate("24822") == False
assert self.validate("123-345-124T123:453:213") == False
assert self.validate("99-09-12T12:42:21Z") == False
assert self.validate("99-09-12T12:42:21+00:") == False
assert self.validate("99-09-12T12:42:21+00:00") == False
assert self.validate("2012-09-12T21:-1:21") == False
def test_rejects_no_offset(self):
assert self.validate("2012-09-12T12:42:21") == False
def test_rejects_out_of_range(self):
assert self.validate("2012-00-12T12:42:21Z") == False
assert self.validate("2012-13-12T12:42:21Z") == False
assert self.validate("2012-09-00T12:42:21Z") == False
assert self.validate("2012-09-31T12:42:21Z") == False # Sep
assert self.validate("2012-08-31T12:42:21Z") == True # Aug
assert self.validate("2012-08-32T12:42:21Z") == False
assert self.validate("2012-09-12T24:00:00Z") == False
assert self.validate("2012-09-12T12:60:21Z") == False
assert self.validate("2012-09-12T12:42:99Z") == False
assert self.validate("2012-09-12T12:42:21+24:00") == False
assert self.validate("2012-09-12T12:42:21-24:00") == False
assert self.validate("2012-09-12T12:42:21+02:60") == False
def test_rejects_leap_seconds(self):
# with regret :-(
assert self.validate("2012-06-30T23:59:60Z") == False
assert self.validate("2012-03-21T09:21:60Z") == False
def test_handles_leapyear(self):
assert self.validate("2012-02-29T12:42:21Z") == True
assert self.validate("2012-02-30T12:42:21Z") == False
assert self.validate("2000-02-29T12:42:21Z") == True
assert self.validate("2000-02-30T12:42:21Z") == False
assert self.validate("2100-02-28T12:42:21Z") == True
assert self.validate("2100-02-29T12:42:21Z") == False
assert self.validate("2011-02-28T12:42:21Z") == True
assert self.validate("2011-02-29T12:42:21Z") == False
def test_accepts_good(self):
assert self.validate("1994-03-14T17:00:00Z") == True
assert self.validate("2011-06-23T17:12:00+05:21") == True
assert self.validate("1992-03-14T17:04:00-01:42") == True
class TestRFC3339toTimestamp(object):
def setup(self):
self.mod = ProxyFunctionModule("tests/rfc3339")
self.func = self.mod.rfc3339_to_timestamp
def teardown(self):
self.mod.close()
def test_simple_cases(self):
assert self.func("1996-12-19T16:39:57-08:00") == 851042397
assert self.func("2012-08-08T21:30:36+01:00") == 1344457836
assert self.func("1994-03-14T17:00:00Z") == 763664400
assert self.func("1970-01-01T00:00:00Z") == 0
assert self.func("1970-01-01T01:20:34Z") == 4834
assert self.func("1969-12-31T23:59:59Z") == -1
assert self.func("1969-12-31T22:51:12Z") == -4128
def test_y2038(self):
assert self.func("2100-01-01T00:00:00Z") == 4102444800
assert self.func("1900-01-01T00:00:00Z") == -2208988800
def test_leap_year(self):
assert self.func("2012-02-29T12:42:21Z") == 1330519341
assert self.func("2000-02-29T23:59:59Z") == 951868799
assert self.func("2011-02-28T04:02:12Z") == 1298865732
assert self.func("2100-02-28T00:00:00Z") == 4107456000
assert self.func("1900-02-28T00:00:00Z") == -2203977600
def test_dst_transition(self):
assert self.func("2012-03-25T00:59:59+00:00") == 1332637199
assert self.func("2012-03-25T02:00:00+01:00") == 1332637200
assert self.func("2012-10-28T01:00:00+01:00") == 1351382400
assert self.func("2012-10-28T01:00:00+00:00") == 1351386000
def test_wacky_offset(self):
assert self.func("1996-12-19T16:39:57-02:52") == 851042397 - 18480
assert self.func("2012-08-08T21:30:36+23:11") == 1344457836 - 79860
def test_float(self):
assert self.func("1996-12-19T16:39:57.1234-08:00") == 851042397
assert self.func("1996-12-20T00:39:57.004Z") == 851042397
class TestTimestampToRFC3339UTCOffset(object):
def setup(self):
self.mod = ProxyFunctionModule("tests/rfc3339")
self.func = self.mod.timestamp_to_rfc3339_utcoffset
def teardown(self):
self.mod.close()
def test_simple_cases(self):
assert self.func(851042397) == "1996-12-20T00:39:57Z"
assert self.func(1344457836) == "2012-08-08T20:30:36Z"
assert self.func(763664400) == "1994-03-14T17:00:00Z"
assert self.func(0) == "1970-01-01T00:00:00Z"
assert self.func(4834) == "1970-01-01T01:20:34Z"
assert self.func(-1) == "1969-12-31T23:59:59Z"
assert self.func(-4128) == "1969-12-31T22:51:12Z"
def test_y2038(self):
try:
assert self.func(4102444800) == "2100-01-01T00:00:00Z"
assert self.func(-2208988800) == "1900-01-01T00:00:00Z"
except ValueError as e:
if str(e) == "timestamp out of range for platform time_t":
raise SkipTest("Can't run this test on 32 bit")
else:
raise
def test_leap_year(self):
assert self.func(1330519341) == "2012-02-29T12:42:21Z"
assert self.func(951868799) == "2000-02-29T23:59:59Z"
assert self.func(1298865732) == "2011-02-28T04:02:12Z"
try:
assert self.func(4107456000) == "2100-02-28T00:00:00Z"
assert self.func(-2203977600) == "1900-02-28T00:00:00Z"
except ValueError as e:
if str(e) == "timestamp out of range for platform time_t":
raise SkipTest("Can't run this test on 32 bit")
else:
raise
def test_now(self):
s = self.mod.now_to_rfc3339_utcoffset()
assert s[-1] == "Z"
assert len(s) == 20
d = int(time.time()) - self.mod.rfc3339_to_timestamp(s)
assert d == 0 or d == 1
class TestTimestampToRFC3339LocalOffsetLondon(object):
def setup(self):
environ = os.environ.copy()
environ["TZ"] = "Europe/London"
self.mod = ProxyFunctionModule("tests/rfc3339", environ=environ)
self.func = self.mod.timestamp_to_rfc3339_localoffset
def teardown(self):
self.mod.close()
def test_simple_cases(self):
assert self.func(851042397) == "1996-12-20T00:39:57+00:00"
assert self.func(1344457836) == "2012-08-08T21:30:36+01:00"
assert self.func(763664400) == "1994-03-14T17:00:00+00:00"
assert self.func(1340280000) == "2012-06-21T13:00:00+01:00"
assert self.func(1234) == "1970-01-01T01:20:34+01:00"
assert self.func(-7728) == "1969-12-31T22:51:12+01:00"
def test_dst_transition(self):
assert self.func(1332637199) == "2012-03-25T00:59:59+00:00"
assert self.func(1332637200) == "2012-03-25T02:00:00+01:00"
assert self.func(1351382400) == "2012-10-28T01:00:00+01:00"
assert self.func(1351386000) == "2012-10-28T01:00:00+00:00"
def test_now(self):
s = self.mod.now_to_rfc3339_localoffset()
w = self.mod.rfc3339_to_timestamp(s)
assert s[-6:] == ["+00:00", "+01:00"][time.localtime(w).tm_isdst]
d = int(time.time()) - w
assert d == 0 or d == 1
class TestTimestampToRFC3339LocalOffsetNewYork(object):
def setup(self):
environ = os.environ.copy()
environ["TZ"] = "America/New_York"
self.mod = ProxyFunctionModule("tests/rfc3339", environ=environ)
self.func = self.mod.timestamp_to_rfc3339_localoffset
def teardown(self):
self.mod.close()
def test_simple_cases(self):
assert self.func(851042397) == "1996-12-19T19:39:57-05:00"
assert self.func(1344457836) == "2012-08-08T16:30:36-04:00"
assert self.func(761245200) == "1994-02-14T12:00:00-05:00"
assert self.func(1340280000) == "2012-06-21T08:00:00-04:00"
assert self.func(19234) == "1970-01-01T00:20:34-05:00"
assert self.func(-4128) == "1969-12-31T17:51:12-05:00"
def test_dst_transition(self):
assert self.func(1331449199) == "2012-03-11T01:59:59-05:00"
assert self.func(1331449200) == "2012-03-11T03:00:00-04:00"
assert self.func(1352005200) == "2012-11-04T01:00:00-04:00"
assert self.func(1352008800) == "2012-11-04T01:00:00-05:00"
def test_now(self):
s = self.mod.now_to_rfc3339_localoffset()
w = self.mod.rfc3339_to_timestamp(s)
assert s[-6:] == ["-05:00", "-04:00"][time.localtime(w).tm_isdst]
d = int(time.time()) - w
assert d == 0 or d == 1

Wyświetl plik

@ -0,0 +1,75 @@
/* Copyright 2012 (C) Daniel Richman. License: GNU GPL 3; see LICENSE. */
#include <iostream>
#include <stdexcept>
#include <json/json.h>
#include <ctime>
#include "RFC3339.h"
using namespace std;
void handle_command(const Json::Value &command);
int main(int argc, char **argv)
{
tzset();
for (;;)
{
char line[1024];
cin.getline(line, 1024);
if (line[0] == '\0')
break;
Json::Reader reader;
Json::Value command;
if (!reader.parse(line, command, false))
throw runtime_error("JSON parsing failed");
if (!command.isArray() || !command[0u].isString())
throw runtime_error("Invalid JSON input");
handle_command(command);
}
}
void reply(const Json::Value &what)
{
Json::FastWriter writer;
cout << writer.write(what);
}
void handle_command(const Json::Value &command)
{
string command_name = command[0u].asString();
string string_arg;
long long int int_arg = 0;
if (command_name == "validate_rfc3339" ||
command_name == "rfc3339_to_timestamp")
{
string_arg = command[1u].asString();
}
else
{
int_arg = command[1u].asLargestInt();
}
if (command_name == "validate_rfc3339")
reply(RFC3339::validate_rfc3339(string_arg));
else if (command_name == "rfc3339_to_timestamp")
reply(RFC3339::rfc3339_to_timestamp(string_arg));
else if (command_name == "timestamp_to_rfc3339_utcoffset")
reply(RFC3339::timestamp_to_rfc3339_utcoffset(int_arg));
else if (command_name == "timestamp_to_rfc3339_localoffset")
reply(RFC3339::timestamp_to_rfc3339_localoffset(int_arg));
else if (command_name == "now_to_rfc3339_utcoffset")
reply(RFC3339::now_to_rfc3339_utcoffset());
else if (command_name == "now_to_rfc3339_localoffset")
reply(RFC3339::now_to_rfc3339_localoffset());
else
throw runtime_error("Command not found");
}