问题
I am trying to parse an ISO8601 formatted date/time string with .NET's DateTime structure.
In order for me to give a full underdstanding of the problem, I am going to perform tests using both .NET and JavaScript. I am currently in Britain (British Summer Time, which is UTC+01:00).
My understanding of ISO8601 is that:
- When the string is suffixed with "Z", the time is expressed in UTC.
- When the string is suffixed with "+/-hh:mm", the time is expressed in Local time, where the "+/-hh:mm" expresses the offset from UTC.
Consider the following ISO8601 date/time format string:
"1987-01-05T08:45:30.500+0100"
Given my points above, this string expresses a local time of "08:45:30", and a UTC time of "07:45:30"
Current TimeZone Test (.NET)
DateTime now = DateTime.Now;
Console.WriteLine(now.ToLocalTime()); // 27/08/2013 12:02:43
Console.WriteLine(now.ToUniversalTime()); // 27/08/2013 11:02:43
Current TimeZone Test (JavaScript)
var now = new Date(Date.now());
now.toString(); // Tue Aug 27 2013 12:03:46 GMT+0100 (GMT Daylight Time)
now.toUTCString(); // Tue, 27 Aug 2013 11:03:46 GMT
Aside from the minor (minute/second) differences between the two examples, they are returning exactly what I would expect for British Summer Time (UTC+01:00). The minute/second differences are because I can't run both a .NET test and a JavaScript test at EXACTLY the same time.
So now lets use my ISO8601 date/time formatted string:
Parsing ISO8601 Format String (.NET)
DateTime dt = DateTime.Parse("1987-01-05T08:45:30.500+0100");
Console.WriteLine(dt.ToLocalTime()); // 05/01/1987 07:45:30
Console.WriteLine(dt.ToUniversalTime()); // 05/01/1987 07:45:30
Parsing ISO8601 Format String (JavaScript)
var dt = new Date("1987-01-05T08:45:30.500+0100");
dt.toString(); // Mon Jan 05 1987 07:45:30 GMT+0000 (GMT Standard Time)
dt.toUTCString(); //Mon, 05 Jan 1987 07:45:30 GMT
This does not seem consistent with the example which displays the date/time "now". Why is this displaying as though I am in British Winter time (UTC+00:00), when the "now" examples display in British Summer Time (UTC+01:00)?
If I change my time zone settings, I get the expected results for Local/Universal time, but if they are set to my current time/zone, this seems to give inconsisteent results.
Edit: In short, it's as if both .NET and JavaScript are ignoring daylight saving/british summer time (UTC+01:00) when I try to parse the string. The result would be correct for winter, and also correct when I physically change my time/zone...but as is stands this is not correct on "any" machine I have tested on in Britain.
回答1:
Britain uses summer time (advancing their clocks by one hour) in the summer (27 August, your "now" in the examples), but not in the winter (5 January, from your parsing of "1987-01-05T08:45:30.500+0100").
Actually, in the winter Britain uses UTC. Your machine seems to have a British TimeZoneInfo
. You can check with TimeZoneInfo.Local.DisplayName
(since .NET 3.5) or TimeZone.CurrentTimeZone.StandardName
(old).
You can check with dt.IsDaylightSavingTime()
.
Addition:
My answer is about .NET only (but maybe the same applies to JavaScript?). The examples you provide work entirely as expected. A date and time in January 1987 will be converted into your local zone, which is supposedly British, and in January 1987 the United Kingdom was at +0000 since it was winter. The time string you gave was stamped with +0100 (as if it was from Germany or some other country one hour to the east of Britain), and this was acknowledged when the string was parsed. A date in the summer of 1987 would correctly have converted differently because in the summer of 1987 Britain (and all of the EEC) observed summer time (daylight saving time).
To sum up: The offset indicator or zone specifier +0100
is considered when interpreting the time. This is converted into British time on your computer. If you want conversion to UTC instead of conversion to the local time of your machine, use the overload that takes a DateTimeStyles
enum and include the flag DateTimeStyles.AdjustToUniversal
.
If you want a value that better represents time and absolute zone, consider using the struct DateTimeOffset
instead of DateTime
. You can also consider NODA time instead of the .NET types.
回答2:
ToLocalTime and ToUniversalTime do take into account Daylight saving time and do a very good job at it. At least in .Net, the Javascript Date object has flaws and I would recommend using moment.js and moment-timezone.js for conversions in js.
Here is my unit test and the results:
var now = DateTime.Now;
Console.WriteLine(now.ToLocalTime());
Console.WriteLine(now.ToUniversalTime());
//Test 1 TimeZone UTC+1 London, etc..
//current day 20th July = BST
/* 07/20/2015 01:06:43
07/20/2015 00:06:43*/
//set day to 20th Januray UK winter time
/* 01/20/2015 01:07:55
01/20/2015 01:07:55*/
For you to get the results described above in .Net you would need to a) set the time zone to UTC+2 b) run into a glitch in the ToLocalTime() which has since be fixed, MSDN mentions flaws on XP when converting dates.
Finally DateTime.Now returns the local time so using DateTime.Now.ToLocalTime() is a bit redundant and you may well get unexpected results.
来源:https://stackoverflow.com/questions/18454631/parsing-iso8601-date-time-with-datetime-struct