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
, useK
- which properly conveys the.Kind
by appending aZ
to the output forDateTimeKind.Utc
, or an offset from UTC forDateTimeKind.Local
, or nothing at all forDateTimeKind.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, useFFF
instead. Your use ofsss
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 examplear-SA
uses theUmAlQuraCalendar
, 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 hasDateTimeKind.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
How to Validate That a String Is a Valid Ipv4 Address in C++
C++11 Type Trait to Differentiate Between Enum Class and Regular Enum
Why Do Some People Prefer "T Const&" Over "Const T&"
C++ Integer->Std::String Conversion. Simple Function
Recursive Lambda Functions in C++14
Converting Between C++ Std::Vector and C Array Without Copying
Safely Override C++ Virtual Functions
C++ How to Allocate Memory Dynamically on Stack
Why Is Std::Vector::Operator[] 5 to 10 Times Faster Than Std::Vector::At()
C++11 Range-Based For-Loop Efficiency "Const Auto &I" Versus "Auto I"
How Does =Delete on Destructor Prevent Stack Allocation
What's an Expression and Expression Statement in C++
How to Force Inclusion of an Object File in a Static Library When Linking into Executable
Specification of Source Charset Encoding in Msvc++, Like Gcc "-Finput-Charset=Charset"