How to Construct an Iso 8601 Datetime in C++

How do I construct an ISO 8601 datetime in 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";
}

Outputting date in ISO 8601 format

Documentation is your friend:

std::time_t t
= std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::cout << std::put_time( std::localtime( &t ), "%FT%T%z" );

in my system yields

2016-04-29T02:48:56+0200

How to parse and generate DateTime objects in ISO 8601 format

The format you're describing is ISO 8601.

Since you're working with timestamps that inclulde a time zone component, I'd strongly recommend using DateTimeOffset instead of DateTime. It makes things so much easier!

To create a DateTimeOffset for a given date, time, and time zone offset, use this syntax:

var date = new DateTimeOffset(2016, 3, 29, 12, 20, 35, 93, TimeSpan.FromHours(-5));
// March 29, 2016 at 12:20:35.93 GMT-5

This code will format a DateTimeOffset as ISO 8601:

public static string FormatIso8601(DateTimeOffset dto)
{
string format = dto.Offset == TimeSpan.Zero
? "yyyy-MM-ddTHH:mm:ss.fffZ"
: "yyyy-MM-ddTHH:mm:ss.fffzzz";

return dto.ToString(format, CultureInfo.InvariantCulture);
}

And, to parse a string back to a DateTimeOffset:

public static DateTimeOffset ParseIso8601(string iso8601String)
{
return DateTimeOffset.ParseExact(
iso8601String,
new string[] { "yyyy-MM-dd'T'HH:mm:ss.FFFK" },
CultureInfo.InvariantCulture,
DateTimeStyles.None);
}

If you must get back to a DateTime you can get this from the DateTimeOffset.UtcDateTime property.

How to create a .NET DateTime from ISO 8601 format

This solution makes use of the DateTimeStyles enumeration, and it also works with Z.

DateTime d2 = DateTime.Parse("2010-08-20T15:00:00Z", null, System.Globalization.DateTimeStyles.RoundtripKind);

This prints the solution perfectly.

how do I parse an iso 8601 date (with optional milliseconds) to a struct tm in C++?

You can use C's sscanf (http://www.cplusplus.com/reference/cstdio/sscanf/) to parse it:

const char *dateStr = "2014-11-12T19:12:14.505Z";
int y,M,d,h,m;
float s;
sscanf(dateStr, "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &s);

If you have std::string it can be called like this (http://www.cplusplus.com/reference/string/string/c_str/):

std::string dateStr = "2014-11-12T19:12:14.505Z";
sscanf(dateStr.c_str(), "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &s);

If it should handle different timezones you need to use sscanf return value - number of parsed arguments:

int tzh = 0, tzm = 0;
if (6 < sscanf(dateStr.c_str(), "%d-%d-%dT%d:%d:%f%d:%dZ", &y, &M, &d, &h, &m, &s, &tzh, &tzm)) {
if (tzh < 0) {
tzm = -tzm; // Fix the sign on minutes.
}
}

And then you can fill tm (http://www.cplusplus.com/reference/ctime/tm/) struct:

tm time = { 0 };
time.tm_year = y - 1900; // Year since 1900
time.tm_mon = M - 1; // 0-11
time.tm_mday = d; // 1-31
time.tm_hour = h; // 0-23
time.tm_min = m; // 0-59
time.tm_sec = (int)s; // 0-61 (0-60 in C++11)

It also can be done with std::get_time (http://en.cppreference.com/w/cpp/io/manip/get_time) since C++11 as @Barry mentioned in comment how do I parse an iso 8601 date (with optional milliseconds) to a struct tm in C++?

c# convert datetime object to iso 8601 string

Observe:

// Your input
DateTime dt = new DateTime(2017, 6, 26, 20, 45, 0, 70, DateTimeKind.Utc);

// ISO8601 with 7 decimal places
string s1 = dt.ToString("o", CultureInfo.InvariantCulture);
//=> "2017-06-26T20:45:00.0700000Z"

// ISO8601 with 3 decimal places
string s2 = dt.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", CultureInfo.InvariantCulture);
//=> "2017-06-26T20:45:00.070Z"

A few things:

  • Don't use Z in the format string. That's not a valid format specifier, so it is treated as just a character to output. It will be in every string, regardless of .Kind setting of the input datetime.

  • With DateTime, use K - which properly conveys the .Kind by appending a Z to the output for DateTimeKind.Utc, or an offset from UTC for DateTimeKind.Local, or nothing at all for DateTimeKind.Unspecified.

  • Though T will output as a character because it's not a valid format specifier, I suggest always being explicit about those things, so prefer 'T'.

  • Using fff will always give you back three decimals (milliseconds). If you want the decimals omitted when zero, use FFF instead. Your use of sss is not valid.

  • Passing CultureInfo.InvariantCulture is a good practice, as it helps you avoid problems where the current culture might use a different calendar system. For example ar-SA uses the UmAlQuraCalendar, rather than the proleptic Gregorian calendar required by ISO 8601.

  • In your code you tried, you had called theTime.UtcNow - that won't compile. UtcNow is a static property, not an instance property.

  • Also in your code you called theTime.Date.ToUniveralTime() - Don't do that either. .Date will set the time components to zero, and .ToUniversalTime() will have no effect since the input value already has DateTimeKind.Utc.

C# Datetime ISO 8601 format gives incorrect value

Let me see if my understanding is correct (I'm still not entirely sure when the SOAP comes into play in the question).

You have a UTC DateTime:

var dt = new DateTime(2021, 10, 13, 18, 0, 0, DateTimeKind.Utc);

And you'd like to format that in CST -- which, on October 13, is actually CDT (-05:00)?

If that is correct, then you want to utilise DateTimeOffset, which has a better understanding of.. well, time offsets.

// Your original value. This will have an offset of "00:00" 
// as the DateTime above was created as `DateTimeKind.Utc`
var timeAtUtc = new DateTimeOffset(dt);

// Alternatively:
// var timeAtUtc = new DateTimeOffset(2021, 10, 13, 18, 0, 0, TimeSpan.Zero);

// Find out the correct offset on that day (-5:00)
var cstOffsetAtTheTime = TimeZoneInfo
.FindSystemTimeZoneById("Central Standard Time")
.GetUtcOffset(timeAtUtc);

// And now apply the offset to your original value
var timeAtCst = timeAtUtc.ToOffset(cstOffsetAtTheTime);

// You will now get "2021-10-13T13:00:00-05:00"
// no matter where you run this from.
Console.WriteLine(timeAtCst.ToString("yyyy-MM-ddTHH:mm:sszzz"));

// Edit: If you pass it a date in Feb, you'll see that it will correctly be at -06:00.


Edit 2022-03-07 based on comment below. If you don't need to care about the offset value, you can simply do:

var timeAtUtc = ...;
var timeAtCst = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(
timeAtUtc,
"Central Standard Time");


Related Topics



Leave a reply



Submit