I want to get client timezone so i'm using the code below
function filltime()
{
document.getElementById("hdnTime").value = new Date();
}
conversion
Dim time As Date = DateTime.ParseExact(hdnTime.Value,
"ddd MMM d HH:mm:ss UTCzzzzz yyyy",InvariantCulture)
I'm not getting exact value. Its showing server time only.
But hdnTime.Value contains the correct value("Mon Feb 18 14:46:49 UTC+0530 2013"). I think the problem is with conversion.
What is the problem? How can I solve?
Dates and times are a pain in 1 language, let alone when passing a value between 2.
I'd recommend serializing the JavaScript Date() object into JSON before posting it back to the server. Then de-serializing it into a C# DateTime object using a library such as JSON.NET. There's comprehensive documentation (Serializing Dates in JSON) with regards to what settings can be applied when serializing and de-serializing.
JavaScript
function filltime()
{
document.getElementById("hdnTime").value = JSON.stringify(new Date());
}
JSON isn't native to every browser, so you mean need to manually load it in, for more information you can refer to: Browser-native JSON support (window.JSON)
C# using JSON.NET
DateTime dateTime = JsonConvert.DeserializeObject<DateTime>(hdnTime.Value);
You are confusing the DateTime object with its displaying
it is normal that you see the server time because you are seeing the datetime rappresentation with your current timezone.
What you don't get is how DateTime works...
If you pass a datetime with timezone info then it will be converted to your timezone with the right offset.
If you want to pass a datetime and get it as it is than you have to remove the timezone part.
In you situation, anyway, if you need only to know the client timezone just pass it!
var d = new Date()
var n = d.getTimezoneOffset();
The getTimezoneOffset() method returns the time difference between UTC time and local time, in minutes.
For example, If your time zone is GMT+2, -120 will be returned.
For general discussion:
in my experience the best way to deal with datetime converted as string and passed between different systems, is to use the ISODATE format:
DateTime.Now.ToString("s"); //"2013-02-18T11:17:24"
Related
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.
I am storing datetime in a stringified JSON object in Redis cache like so:
"{\"email\":\"myemail#testdomain.com\", \"expiry\": \"2018-03-19T23:00:03.0658822+00:00\"}"
In C#, when I query this data from Redis and convert it to string, it loses its timezone value and gets automatically stripped off of its timezone information.
RedisValue cookie = GetRedisDatabase().StringGet("sessionhash");
JObject cookieValue = JObject.Parse(cookie.ToString());
var email = JObject.Parse(cookie.ToString())["email"];
var expiry = JObject.Parse(cookie.ToString())["expiry"].ToString();
The "expiry" string above only contains "2018/03/19 23:00:03". It seems like C# is automatically detecting the string to be of datetime format, and is stripping off timezone information from it.
How can I ensure the "expiry" string is "2018-03-19T23:00:03.0658822+00:00"?
DateTime does not know about timezones. Instead it has a DateTimeKind property which tells you if the time is machine local, UTC, or unknown. Methods ToLocalTime will convert a known UTC or unknown time to local time, and do nothing of already local.
You'll need to use something else that keeps the timezone information, i believe DateTimeOffset can track a time with a variable offset, but not the timezone.
NodaTime is a library which understands timezones.
internal class Program
{
private static void Main()
{
string expiry = "2018-03-19T23:00:03.0658822+00:00";
DateTime parsedExpiry = DateTime.Parse(expiry);
Console.WriteLine(parsedExpiry.ToString());
Console.ReadKey();
}
}
This code converts 19/3/2018 23:00 into 20/3/2018 7:00.
The reason it does this is because, as per above answers, DateTime doesn't hold on to any TimeZone information. The only information you have is DateTime.Kind, which in the case of my code, outputs Local. I can use parsedExpirey.ToUniversalTime() to get UTC.
You could do some extra parsing on the string representation and use the TimeZoneInfo class to maintain the timezone, but you'll likely need an extra column / storage space to store that info. You can use the Convert option, but then you'll be storing DateTimes in all different timezones, you'd be better off using ToUniversalTime and storing it all in UTC (best practice), then converting it to Local time for presentation to the user (or leave it UTC, depending on the application).
Your final ToString asked for the time without TZ info. Do this
RedisValue cookie = GetRedisDatabase().StringGet("sessionhash");
JObject cookieValue = JObject.Parse(cookie.ToString());
var email = JObject.Parse(cookie.ToString())["email"];
var expiry = JObject.Parse(cookie.ToString())["expiry"].ToString("O");
I have a few general rules regarding handling DateTimes:
Always store, retrieve and transmit the value in UTC. Windows is pretty good at translating any UTC value to whatever the current user picked as his favourite timezone. You do not want to deal with Timezones if you can avoid it at all.
Never store, retrieve and transmit the value as string.
In case 3 can not work, at least pick a fixed culture and string encoding at all endpoints. You do not want to add those to your worries.
In rare cases (Callendar Apps) it might be beneficial to store the "originally saved timezone".
Unfortunately you cannot determine the time zone from an ISO date/time string. You can only determine the offset. The time zone names and codes are not unique-- for example, "Arabia Standard Time" has an offset of UTC+03, but has the code "AST," which collides with "Atlantic Standard Time" (offset UTC-04). So while you can map in one direction, you can't reliably map in the other.
That being said, getting the offset isn't so bad if you use a DateTimeOffset instead of DateTime. If the field isn't a DateTimeOffset in your object model, you can create a temporary anonymous type as a template and get it that way. Example:
public static void Main()
{
var input = "{\"email\":\"myemail#testdomain.com\", \"expiry\": \"2018-03-19T23:00:03.0658822+01:00\"}";
var template = new { email = "", expiry = DateTimeOffset.Now };
var result = JsonConvert.DeserializeAnonymousType(input, template);
Console.WriteLine("DateTime (GMT): {0:R}\r\nOffset from GMT: {1}", result.expiry, result.expiry.Offset);
}
Output:
DateTime (GMT): Mon, 19 Mar 2018 22:00:03 GMT
Offset from GMT: 01:00:00
Code on DotNetFiddle
I'm working in a EasyPost integration making a class library to make the use of their API simpler and I'm getting this error:
Managed Debugging Assistant 'DateTimeInvalidLocalFormat' has detected a problem in 'C:\Projects\TestClient.vshost.exe'.
Additional information: A UTC DateTime is being converted to text in a format that is only correct for local times. This can happen when calling DateTime.ToString using the 'z' format specifier, which will include a local time zone offset in the output. In that case, either use the 'Z' format specifier, which designates a UTC time, or use the 'o' format string, which is the recommended way to persist a DateTime in text. This can also occur when passing a DateTime to be serialized by XmlConvert or DataSet. If using XmlConvert.ToString, pass in XmlDateTimeSerializationMode.RoundtripKind to serialize correctly. If using DataSet, set the DateTimeMode on the DataColumn object to DataSetDateTime.Utc.
I get this error when I call the Create method in the EasyPost Shipment object. Code below:
Shipment shipment = new Shipment() {
to_address = toAddress,
from_address = fromAddress,
parcel = parcel
};
shipment.Create();
This create function probably makes a call to their REST API and is trying to convert a json response into one of their models.
To solve the error I'm trying to set the UTC as the default of my library so whenever I use DateTime.ToString() I use the DateTime.ToString("o"). I don't know if this would actually solve the problem, but I don't know how to force it (use UTC as the library default). I have tried the piece of code below, but it doesn't work
CultureInfo newCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentCulture = newCulture;
Can you help me?
I'm one of the developers on the EasyPost client libraries.
As far as I can find in some basic research, there's no (easy) way to set a default time zone for a C# application. Most of the blog posts and other SO answers I found suggest using utility functions to convert a UTC datetime object to a local datetime object when trying to display it to a string.
EasyPost's API returns all datetimes in UTC time + timezone information (ex. 2022-10-24T12:37:24-06:00), which is accounted for when the JSON is deserialized into a DateTime object in the C# client library.
My end goal is to get universal time from client with NO offset - just UTC time. I try to accomplish this like so:
Javascript: (new Date()).toUTCString(), the output of which logs: Thu, 17 Mar 2016 15:13:23 GMT, which is exactly what I need.
Then I take it to server and try to convert it to DateTimeOffset:
string dateTimeOffsetPattern = "ddd, dd MMM yyyy HH:mm:ss 'GMT'";
DateTimeOffset clientLoginTime = DateTimeOffset.ParseExact
(timeStringFromClient, dateTimeOffsetPattern, CultureInfo.InvariantCulture);
Which results in:
3/17/2016 3:13:23 PM -04:00
Somehow it adjusts the time for my local (Eastern) offset. I do NOT want this to happen, I want it to just return the UTC time, like so:
3/17/2016 3:13:23 PM +00:00
P.S. I did just ask another question about this, and I apologize, since I feel like it should be easy enough, but I don't get it. This should be really simple, but it looks like offset doesn't have a setter (unless I am completely missing some C# basics as usual):
public TimeSpan Offset { get; }
There's an overload of ParseExact where you can specify a DateTimeStyles. One of the values of DateTimeValues is AssumeUniversal, which says:
If format does not require that input contain an offset value, the returned DateTimeOffset object is given the UTC offset (+00:00).
which basically means "don't assume it's local, assume it's universal." Assuming local is the default, which is why you're seeing the result you are in that it's adjusting to local. Specifying AssumeUniversal should parse it the way you want.
DateTimeOffset clientLoginTime = DateTimeOffset.ParseExact
(timeStringFromClient, dateTimeOffsetPattern, CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal);
I would parse from JS normally, then do the following:
Strip OffSet from DateTimeOffset by returning DateTime
Set OffSetby instantiating another DateTimeOffset having TimeSpan set to ZERO.
In your case:
var clientLoginTime = new DateTimeOffset(clientLoginTime.DateTime, TimeSpan.FromHours(0));
This can be easily converted into an extension method
public static DateTimeOffset SetOffset(this DateTimeOffset dto, int timeZoneDiff)
{
return new DateTimeOffset(dto.DateTime, TimeSpan.FromHours(timeZoneDiff));
}
From JavaScript (or any other client) you should send DateTimes using ISO8601 format which would be yyyy-MM-ddTHH-mm-ss.sssz. The Z indicates that the datetime instance is GMT (UTC). You can also change this to add + or - from GMT. You can do this using the JavaScript Date object using myDate.toISOString()
When creating your WebAPI model(s) you can then use either a DateTime or DateTimeOffset types directly. The JSON.NET serializer for Web API will automatically deserialize the sent ISO8601 datetime string to the correct DateTime or DateTimeOffset type (depending on which one you are using). This means that you do not have to do any manuall parsing in your code which is good. (Imagine if you had to send everything as string and parse everything manually in all your methods?).
So you can now have a method
public async Task<IHttpActionResult> GetRecords(DateTimeOffset? startFrom)
And the startFrom will automatically be populated based on the sent ISO8601 formatted DateTime string.
Finally, the last and most import reason to do this is that your clients will probably not all use the same locale. You could have a user that has their browser set to Spanish so .toUTCString() will not yield an English string or possibly even a string with mm/dd but the reverse (like most countries other than the USA).
Long story short for WebAPI
Use ISO8601 from/to client.
Use DateTimeOffset or DateTime instances directly in your model (no parsing).
I have a datetime in database which I read using SqlDataReader and then cast it to (DateTime). After the cast its Kind property is DateTimeKind.Unspecified.
Then I have another string which I read from some other source. Its format is like this 2016-01-20T22:20:29.055Z. I do DateTime.Parse("2016-01-20T22:20:29.055Z") and its Kind property is DateTimeKind.Local.
How do I properly parse the both date times for comparison? Do I need to use DateTimeOffsets? How should I parse them?
Thanks
Because SQLReader cannot reasonably infer a DateTimeKind, it leaves it as unspecified. You'll want to use DateTime.SpecifyKind to change the DateTimeKind on your output from the SQLReader to the appropriate value. This works ok if you are only dealing with UTC and one consistent local time zone; otherwise, you really should be using DateTimeOffset in both your code and the SQL Database.
The string "2016-01-20T22:20:29.055Z" is ISO 8601 compliant and is a UTC date; however, DateTime.Parse with only 1 argument can end up performing a conversion to local time. Per the documentation:
Generally, the Parse method returns a DateTime object whose Kind
property is DateTimeKind.Unspecified. However, the Parse method may
also perform time zone conversion and set the value of the Kind
property differently, depending on the values of the s and styles
parameters:
If s contains time zone information, the date and time is converted
to the time in the local time zone and the Kind is DateTimeKind.Local.
If s contains time zone information, and styles includes the
AdjustToUniversalflag, the date and time is converted to Coordinated
Universal Time (UTC) and the Kind is DateTimeKind.Utc.
If s contains the Z or GMT time zone designator, and styles includes
the RoundtripKind flag, the date and time are interpreted as UTC and
the Kind is DateTimeKind.Utc.
Also see UTC gotchas in .NET and SQL Server in Derek Fowler's blog for additional coverage on the topic.
In your second example, 2016-01-20T22:20:29.055Z has timezone information provided with it; the 'Z' at the end indicates that the timestamp is intended for Coordinated Universal Time (UTC). However, DateTime.Parse() will default its conversion using DateTimeKind.Local unless a specific timezone is specified. You can use DateTime.ParseExact to be more specific.
As to why the datetime values in your database are coming out as Unspecified, that's likely because they contain no timezone indication at all. Check to see if your database values specify timezone information, either by using 'Z' at the end or specifying an exact timezone, such as 2016-01-20T22:20:29.055-07:00 (UTC-7).
You can use something like this:
string format = "ddd dd MMM h:mm tt yyyy";
DateTime dateTime = DateTime.ParseExact(dateString, format,
CultureInfo.InvariantCulture);
In format variable, you can put the format you want, and pass it to ParseExact function.
Hope it helps.
You are missing the datetime context (offset) in your database. You should persist it either in a datetimeoffset column or in a datetime column but persisting utc datetimes.
And always better compare two utc datetimes.
I coded a quick C# console app that I pasted in below. This converts a UTC date and time to a string (format similar to the ISO 8601 format described in another post with some extra digits of precision), writes it to a file, reads it from the file (as a string) and then converts it back to a UTC date and time.
It then compares the two UTC Date Time objects, which are both of UTC kind, and they match.
class Program
{
// "2016-01-20T22:20:29.055Z" is ISO 8601 compliant and is a UTC date
const string dtf = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
static void Main(string[] args)
{
string file = #"c:\temp\file.txt";
DateTime dt = DateTime.UtcNow;
using (var sw = new System.IO.StreamWriter(file))
{
sw.WriteLine(dt.ToString(dtf, System.Globalization.CultureInfo.InvariantCulture));
}
DateTime dtin;
using (var sr = new System.IO.StreamReader(file))
{
dtin = DateTime.ParseExact(sr.ReadLine(), dtf, System.Globalization.CultureInfo.InvariantCulture);
}
Console.WriteLine(dt.ToString(dtf) + "\r\n" + dtin.ToString(dtf) + "\r\nEquality:" + (dt == dtin));
Console.ReadLine();
}
}