Parse timestamp without knowing the timezone - c#

I am parsing Timestamps from a JSON into C#. Usually these timestamps look like this: "2017-05-12T14:45:42.7529468Z". However I might receive timestamps from other timezones, e.g. a timestamp from GMT+2 - I assume it would look like this "2017-05-12T16:45:42.7529468B".
I used to parse the timestamps with
DateTime myTimestamp = myJSON.SourceTimestamp
This method doesn't seem to work for timestamps which do not have Z at the end.
Am I assuming the wrong timestamp format for other timezones like GMT+2?
Or do I need a special parser again?
I tried using
DateTime myTimestampExact = DateTime.ParseExact(myJSON.SourceTimestamp, "yyyy'-'MM'-'dd'T'HH':'mm':'ss.ffffffK", null);
however the latter returns me an error:
The best overloaded method match for 'System.DateTime.ParseExact(string, string, System.IFormatProvider)' has some invalid arguments

Related

How to strip timezone from DateTime in .NET Core

I have a use case that I'm not sure how to solve in a nice way.
I'm currently developing a .Net Core WebApi that is receiving data from various current systems, from a cross the whole world. Which I then process and lastly I commit it to SAP through oData endpoint.
The problem I'm having is on of parameters I'm receiving in the body payload, is a DateTime. Previous I have not have any issues. But not long ago I started getting data from a other system which deliverers it in a slightly differently way.
Previously this was the format I got: 2020-09-16T16:30:00 not problem with it. But the new system looks like this: 2020-09-16T16:00:00 -05:00 Could also end in +08:00.
The problem I'm facing is that SAP needs to get in local time. But in the my code it converts this: 2020-09-16T16:00:00 -05:00 to 2020-09-16T23:00:00 when I see the incoming payload in the controller.
I have searched quite a bit to find a solution. But 99% only suggest using UTC time, which is not a option for me.
Another option is to use DateTimeOffset, which I have tried but can't the time conversion to use localTime.
My question is. Are it not possible to custom convert to strip the timezone before it hits the controller?
Generarally when you're working with datetime data that includes offsets for time zone the DateTimeOffset type is a good place to start. The sample string 2020-09-16T16:00:00 -05:00 can be passed to DateTimeOffset.Parse() to get a correct DTO value with timezone information attached. From there you can get the local time, UTC time or a DateTime value with the timezone stripped.
string source = "2020-09-16T16:00:00 -05:00";
string fmt = #"yyyy-MM-dd\THH:mm:ss zzz"
// Same as input
Console.WriteLine(DateTimeOffset.Parse(source).ToString(fmt));
// Adjusted to your local timezone
Console.WriteLine(DateTimeOffset.Parse(source).ToLocalTime().ToString(fmt));
// DateTime portion of the source, timezone offset ignored
Console.WriteLine(DateTimeOffset.Parse(source).DateTime.ToString());
Getting the UTC time is simple too via the UtcDateTime property.
It sounds like what you want is the last one - just the date and time from the inputt string with the timezone offset stripped. If you just want the corresponding local time then DateTime.Parse should give that to you directly.
The JsonSerializer class doesn't support this format for DateTimeOffset so you might have some trouble getting it converted before hitting your controller. In that case you'd need to accept a string and do the conversion by hand in your code. You also might need to investigate the TryParseExact method.
Use DateTime.Parse() , for example
string timeInString1 = "2020-09-16T16:00:00 -05:00";
DateTime moment1 = DateTime.Parse(timeInString1);
string timeInString2 = "2020-09-16T16:00:00 +08:00";
DateTime moment2 = DateTime.Parse(timeInString2);
string timeInString3 = "2020-09-16T16:30:00";
DateTime moment3 = DateTime.Parse(timeInString3);
but momen1, momen2, or moment3 is non-timezone awareness value.

Failed to convert from a String to a TimeSpan error

I am attempting to post to my Database using a SqlCommand like so
queryLogResults.Parameters.Add("#executionTime", SqlDbType.Time).Value = executionTime;
This is the string I am passing in as executionTime
string performanceTime = _stopWatch.Elapsed.ToString("mm':'ss':'fff");
I am assuming it has something to do with the milleseconds because it works only sometimes, and if I post just minutes and seconds it works every time. Am I not using the right type of SqlDbType? In my Database ExecutionTime is a time(7) value.
If you're trying to set a parameter value that's of type SqlDbType.Time, then you shouldn't need to convert it to a string. Just use
TimeSpan performanceTime = _stopWatch.Elapsed;
queryLogResults.Parameters.Add("#executionTime", SqlDbType.Time).Value = performanceTime;
Looks to me like it's the single quotes in format string:
mm':'ss':'fff
Additionally, you need to include Hours as part of the string. Finally, milliseconds are traditionally separated with a decimal point, rather than a colon.
So a better format string looks like this:
HH:mm:ss.fff
Since ADO.Net wants to convert this string value back into a TimeSpan, it needs to be able to the parse the string, and those formatting differences are throwing it off.
Moreover, since ADO.Net will already use a TimeSpan value, I suggest changing the type of the method argument from string to TimeSpan. Then you can pass _stopWatch.Elapsed to the method without converting to a string.
This will also improve performance. Because of the complexity and variety of localization/culture options, converting Date and numeric values to and from strings is surprisingly expensive. You can save your computer some work by avoiding the two conversions in this code.

Parse date with time zone, skip timezone

I try to parse date from string which contains time zone information. Input string is 2014-12-17T08:05:39+00:00.
I use DateTime.Parse() method which return me 2014-12-17 09:05:39 (one hour was added). I live in UTC+1:00 (Warsaw), so .NET adopt this date to my local time.
My question is how to use the parse method while skipping time zone, for example for 2014-12-17T08:05:39+00:00 I want to get 2014-12-17 08:05:39.
I would recommend parsing it as a DateTimeOffset instead of as a DateTime. You can then get the DateTime out of that, but it separates the "parsing the data you've been given" step from the "only using the bits I want from that" step.
It's possible that there are ways to make DateTime.Parse behave the way you want using DateTimeStyles - and I'm surprised it's converting to a "local" kind automatically anyway - but using DateTimeOffset will make it clearer.
(Of course I'd really recommend using Noda Time instead, parsing to an OffsetDateTime and then getting the LocalDateTime out of that, but that's a different matter...)
If you remove the part specifying time zone in input string then it parses directly without adjusting to local time. The date.Kind is then Unspecified.
var input = "2014-12-17T08:05:39";
var date = DateTime.Parse(fixedInput);
Although this works you might want to have a look on NodaTime as well.
You should try using DateTimeOffset instead of the DateTime
DateTimeOffset result = DateTimeOffset.Parse("2014-12-17T08:05:39+00:00", CultureInfo.InvariantCulture);
it gives you : 12/17/2014 8:05:39 AM +00:00

Get and format UTC Time corresponding to NodaTime ZonedDateTime value?

I have ZonedDateTime value. I need to obtain corresponding UTC time and to format it as ISO8601 string (without Time Zone).
What's the right "NodaTime way" of doing it?
I understand that I can use ZonedDateTime.ToDateTimeUtc() method to get .Net DateTime Kind of Utc. Should I do it and then just use ToString()?
Like
var myresult = resZonedDateTime.ToDateTimeUtc().ToString("s")
Should I really use "s" there?
There are a few different ways, depending on exactly what you're looking for.
First, you need to decide whether you want a value like 2014-10-30T16:46:49 or one like 2014-10-30T16:46:49Z. Both are allowed by ISO8601, but the trailing Z at the end is specifically used when the value is UTC. If you send the string elsewhere, the receiving party will not have to guess what the basis for the value is.
If you want the Z, then convert your ZonedDateTime to an Instant using the .ToInstant() method.
Instant instant = zdt.ToInstant();
If you don't want the Z, but still want the value to reflect UTC, then adjust your ZonedDateTime to UTC, then strip it down to the LocalDateTime using it's .LocalDateTime property.
LocalDateTime ldt = zdt.WithZone(DateTimeZone.Utc).LocalDateTime;
Next, you need to decide whether you want to format the string inline with the BCL-based API, or whether you want to use the pattern-based API. Noda Time supports both, which you can read about in the user guide.
The BCL-based API allows you to use methods you're already familiar with, such as .ToString() and .ToString("some format", someCulture) For example:
string s = instant.ToString();
or
string s = ldt.ToString("s", CultureInfo.InvariantCulture);
The pattern-based API separates the work of parsing the format string from doing the actual formatting, as two steps:
var pattern = InstantPattern.ExtendedIsoPattern;
var s = pattern.Format(instant);
It is much more efficient if you are going to be formatting many items. I typically use it when I'm working with LINQ. For example:
var pattern = InstantPattern.GeneralPattern;
var strings = instants.Select(pattern.Format);
or
var pattern = LocalDateTimePattern.GeneralIsoPattern;
var strings = ldts.Select(pattern.Format);
And finally, you need to think about the precision you want. In the pattern API, the "general" formats are precise to whole seconds. The "extended" formats include fractional seconds up to 7 decimal places. You could also create your own pattern using the Create... static methods on the various pattern classes.
If you are just using ToString, then keep in mind that the default format may or may not be what you are looking for. Again, you can pass a format string. For Instant values, "g" is precise to whole seconds, but you would use a custom string of "yyyy'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFF'Z'" if you need precision. For LocalDateTime values, "s" is precise to whole seconds, and "o" is precise to 7 decimals.
Again, all of this is in the "Text" chapter of the user guide, which I would encourage you to read.

current date format

Is there any way to find the current format of date in the time zone? I am retrieving date in the form of string from database and in case the current datetime format does not match, crash comes, "String was not recognized as valid datetime"
It sounds like what's important isn't the current format of the date as your code understand it, but as it gets it from the database. Why is it in the database as a string to start with? If at all possible you should make it an appropriate date/time related field in the database and make the driver do the conversion.
If that's not possible, you should perform the conversion in your code using a custom date/time format which matches what the server gives you, and in an appropriate culture (quite possibly the invariant one).
The DateTime.Parse method uses the format that is set on the executing thread. See the Thread.CurrentCulture to retrieve the CultureInfo that use used when parsing. The CultureInfo.DateTimeFormat returns the format you are looking for.
If you know the format, you should use the DateTime.ParseExact method to parse the input string with a known format.
Sound like you need to use the DateTime.TryParse-method:
http://msdn.microsoft.com/en-us/library/system.datetime.tryparse.aspx
If you don't know in which format the date is passed and .NET can't figure it out I think your out of luck. You could of cause try to see if you could figure out the format by yourself by using regex.

Categories

Resources