How do I construct an ISO 8601 datetime in C++?
C++QtAzureC++ Problem Overview
I'm working with the Azure REST API and they are using this to create the request body for table storage:
DateTime.UtcNow.ToString("o")
Which produces: >2012-03-02T04:07:34.0218628Z
It is called "round-trip" and apparently it's an ISO standard (see http://en.wikipedia.org/wiki/ISO_8601) but I have no idea how to replicate it after reading the wiki article.
Does anyone know if Boost has support for this, or possibly Qt?
C++ Solutions
Solution 1 - C++
If the time to the nearest second is precise enough, you can use strftime
:
#include <ctime>
#include <iostream>
int main() {
time_t now;
time(&now);
char buf[sizeof "2011-10-08T07:07:09Z"];
strftime(buf, sizeof buf, "%FT%TZ", gmtime(&now));
// this will work too, if your compiler doesn't support %F or %T:
//strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
std::cout << buf << "\n";
}
If you need more precision, you can use Boost:
#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
int main() {
using namespace boost::posix_time;
ptime t = microsec_clock::universal_time();
std::cout << to_iso_extended_string(t) << "Z\n";
}
Solution 2 - C++
Using the date library (C++11):
template <class Precision>
string getISOCurrentTimestamp()
{
auto now = chrono::system_clock::now();
return date::format("%FT%TZ", date::floor<Precision>(now));
}
Example usage:
cout << getISOCurrentTimestamp<chrono::seconds>();
cout << getISOCurrentTimestamp<chrono::milliseconds>();
cout << getISOCurrentTimestamp<chrono::microseconds>();
Output:
2017-04-28T15:07:37Z
2017-04-28T15:07:37.035Z
2017-04-28T15:07:37.035332Z
Solution 3 - C++
I should point out I am a C++ newb.
I needed string with a UTC ISO 8601 formatted date and time that included milliseconds. I did not have access to boost.
This is more of a hack than a solution, but it worked well enough for me.
std::string getTime()
{
timeval curTime;
gettimeofday(&curTime, NULL);
int milli = curTime.tv_usec / 1000;
char buf[sizeof "2011-10-08T07:07:09.000Z"];
char *p = buf + strftime(buf, sizeof buf, "%FT%T", gmtime(&curTime.tv_sec));
sprintf(p, ".%dZ", milli);
return buf;
}
The output looks like: 2016-04-13T06:53:15.485Z
Solution 4 - C++
With C++20, time point formatting (to string) is available in the (chrono) standard library. https://en.cppreference.com/w/cpp/chrono/system_clock/formatter
#include <chrono>
#include <format>
#include <iostream>
int main()
{
const auto now = std::chrono::system_clock::now();
std::cout << std::format("{:%FT%TZ}", now) << '\n';
}
Output
2021-11-02T15:12:46.0173346Z
It works in Visual Studio 2019 with the latest C++ language version (/std:c++latest).
Solution 5 - C++
Boost has a library for this.
I.e. posix_time has the from_iso_string()
and to_iso_string()
functions.
Solution 6 - C++
In Qt, that would be:
QDateTime dt = QDateTime::currentDateTime();
dt.setTimeSpec(Qt::UTC); // or Qt::OffsetFromUTC for offset from UTC
qDebug() << QDateTime::currentDateTime().toString(Qt::ISODate);
Solution 7 - C++
OK so I've modified a few solutions that I've found as came up with the following :
static QString getTimeZoneOffset()
{
QDateTime dt1 = QDateTime::currentDateTime();
QDateTime dt2 = dt1.toUTC();
dt1.setTimeSpec(Qt::UTC);
int offset = dt2.secsTo(dt1) / 3600;
if (offset >= 0)
return QString("%1").arg(offset).rightJustified(2, '0',true).prepend("+");
return QString("%1").arg(offset).rightJustified(2, '0',true);
}
Then to easily format a date ( yyyy-MM-dd'T'HH:mm:ss.SSSZ ) :
static QString toISO8601ExtendedFormat(QDateTime date)
{
QString dateAsString = date.toString(Qt::ISODate);
QString timeOffset = Define::getTimeZoneOffset();
qDebug() << "dateAsString :" << dateAsString;
qDebug() << "timeOffset :" << timeOffset;
timeOffset = QString(".000%1%2").arg(timeOffset).arg("00");
qDebug() << "timeOffset replaced :" << timeOffset;
if(dateAsString.contains("Z",Qt::CaseInsensitive))
dateAsString = dateAsString.replace("Z",timeOffset);
else
dateAsString = dateAsString.append(timeOffset);
qDebug() << "dateAsString :" << dateAsString;
return dateAsString;
}
For example GMT +2 would look like this : 2013-10-14T00:00:00.000+0200
Solution 8 - C++
You can use this function which uses std::put_time
with a std::ostringstream
to generate the resulting std::string
.
#include <iostream>
#include <chrono>
#include <iomanip>
#include <sstream>
/**
* Generate a UTC ISO8601-formatted timestamp
* and return as std::string
*/
std::string currentISO8601TimeUTC() {
auto now = std::chrono::system_clock::now();
auto itt = std::chrono::system_clock::to_time_t(now);
std::ostringstream ss;
ss << std::put_time(gmtime(&itt), "%FT%TZ");
return ss.str();
}
// Usage example
int main() {
std::cout << currentISO8601TimeUTC() << std::endl;
}
Reference: https://techoverflow.net/2018/03/30/iso8601-utc-time-as-stdstring-using-c11-chrono/
Solution 9 - C++
Tested in Visual C++, GNU C++, Emscripten
#include <ctime>
#include <chrono>
#include <iostream>
#include <locale>
#if defined (_WIN32)
#define WINDOWSLIB 1
#elif defined (__APPLE__)//iOS, Mac OS
#define MACOSLIB 1
#elif defined (__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__linux) || defined(linux)//_Ubuntu - Fedora - Centos - RedHat
#define LINUXLIB 1
#elif defined (__EMSCRIPTEN__)
#define EMSCRIPTENLIB 1
#endif
#define WriteLine(data)std::cout<< data <<std::endl;
typedef std::string String;
String CurrentISO8601DateTime(bool toUTC=true)
{
using namespace std::chrono;
system_clock::time_point now = system_clock::now();
time_t timet = system_clock::to_time_t(now);
std::tm tm{};
String localeStr = setlocale(LC_ALL, nullptr);
setlocale(LC_ALL, u8"");
String format = String(u8"%FT%T.").append(std::to_string(duration_cast<milliseconds>(now.time_since_epoch()).count() % static_cast<long long>(1000)));
if (toUTC)
{
#ifdef WINDOWSLIB
gmtime_s(&tm, &timet);
#elif LINUXLIB
gmtime_r(&timet, &tm);
#elif EMSCRIPTENLIB
gmtime_r(&timet, &tm);
#endif
format = format.append(u8"Z");
}
else
{
#ifdef WINDOWSLIB
localtime_s(&tm, &timet);
#elif LINUXLIB
localtime_r(&timet, &tm);
#elif EMSCRIPTENLIB
localtime_r(&timet, &tm);
#endif
format.append(u8"%z");
}
String result = String(255, 0);
const size_t length = std::strftime(&result[0], result.size(), format.c_str(), &tm);
result.resize(length);
setlocale(LC_ALL, localeStr.c_str());
return result;
}
#define ConsoleWriteLn(data) std::cout<< data <<std::endl;
int main()
{
ConsoleWriteLn(u8"UTC : " + CurrentISO8601DateTime());
ConsoleWriteLn(u8"LOCAL: " + CurrentISO8601DateTime(false));
}
Results
UTC : 2020-04-12T17:00:18.632Z
LOCAL: 2020-04-12T12:00:18.633-0500
You can deserialize normally with Json.NET
Solution 10 - C++
You can get local or UTC time:
#include <ctime>
#include <iostream>
int main(){
std::time_t time = std::time(0); // Get current time
// Construct local time
char loc[sizeof("2021-03-01T10:44:10Z")];
strftime(loc, sizeof(loc), "%FT%TZ", localtime(&time));
// Construct UTC time
char utc[sizeof("2021-03-01T10:44:10Z")];
strftime(utc, sizeof(utc), "%FT%TZ", gmtime(&time));
// Print local and UTC time
std::cout << "Local time: " << loc << std::endl;
std::cout << "UTC time: " << utc << std::endl;
return 0;
}
Solution 11 - C++
Did it like this:
using namespace boost::posix_time;
ptime t = microsec_clock::universal_time();
qDebug() << QString::fromStdString(to_iso_extended_string(t) + "0Z"); // need 7 digits