Consider the code below. I have a list of dates around the point in time when CEST changes from summer to winter time. I need to convert them to UTC. However using this code the midnight gets lost, I can't understand how to fix it.
DateTime firstDate = new DateTime(2020, 10, 24, 21, 0, 0);
DateTime lastDate = new DateTime(2020, 10, 25, 3, 0, 0);
DateTime[] dates = Enumerable.Range(0, (lastDate - firstDate).Hours + 1)
.Select(r => firstDate.AddHours(r))
.ToArray();
TimeZoneInfo tzo = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
List<DateTimeOffset> offsets = new List<DateTimeOffset>();
foreach(var date in dates)
{
var timeSpan = tzo.GetUtcOffset(date);
var offset = new DateTimeOffset(date, timeSpan);
offsets.Add(offset);
}
10/24/2020 09:00:00 PM +02:00 = 19:00Z
10/24/2020 10:00:00 PM +02:00 = 20:00Z
10/24/2020 11:00:00 PM +02:00 = 21:00Z
10/25/2020 12:00:00 AM +02:00 = 22:00Z
10/25/2020 01:00:00 AM +02:00 = 23:00Z
10/25/2020 02:00:00 AM +01:00 = 01:00Z - What happened to 00:00Z?
10/25/2020 03:00:00 AM +01:00 = 02:00Z
You're converting from what I'd call a "local date/time" to a UTC value. Some local date/time values are skipped and some are repeated, due to daylight saving transitions (and other time zone changes).
In the situation you're showing, every local time between 2am inclusive and 3am exclusive happened twice, because at 3am (the first time) the clocks went back to 2am - this line:
10/25/2020 02:00:00 AM +01:00 = 01:00Z
... shows the second mapping of 2am. But at 2020-10-25T00:00:00Z the local time was also 2am due to the clocks going back. Your conversion is ambiguous, in other words.
The TimeZoneInfo documentation states:
If dateTime is ambiguous, or if the converted time is ambiguous, this method interprets the ambiguous time as a standard time.
Here, "standard time" is the second occurrence of any ambiguous time (because it's a transition from daylight time to standard time).
Fundamentally, if you only have local values then you have incomplete information. If you want to treat ambiguous values as daylight time instead of standard time (or flag them up as ambiguous), you can always use TimeZoneInfo.IsAmbiguousTime(DateTime) to detect that.
The problem is that there are two 2AM's on 25th October 2020 - 02:00 AM +02:00 and 02:00 AM +01:00.
Since the source DateTime has no offset available, the GetUtcOffset method has no way to determine which 2AM the value refers to, and so it defaults to using the non-DST offset - 02:00 AM +01:00.
Depending on what you're actually trying to do, you may be able to resolve this by converting the start time to UTC, generating a list of UTC DateTimeOffset values, and then converting them back to the desired time-zone:
DateTime firstDate = new DateTime(2020, 10, 24, 21, 0, 0);
TimeZoneInfo tzo = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
DateTimeOffset firstDateUtc = TimeZoneInfo.ConvertTime(firstDate, tzo, TimeZoneInfo.Utc);
DateTimeOffset[] utcDates = Enumerable.Range(0, 7).Select(r => firstDateUtc.AddHours(r)).ToArray();
DateTimeOffset[] offsets = Array.ConvertAll(utcDates, d => TimeZoneInfo.ConvertTime(d, tzo));
Output:
24/10/2020 21:00:00 +02:00
24/10/2020 22:00:00 +02:00
24/10/2020 23:00:00 +02:00
25/10/2020 00:00:00 +02:00
25/10/2020 01:00:00 +02:00
25/10/2020 02:00:00 +02:00
25/10/2020 02:00:00 +01:00
Alternatively, you might want to look at NodaTime, which provides a much clearer model for working with dates and times.
Related
I am getting this error:
The supplied DateTime represents an invalid time. For example, when
the clock is adjusted forward, any time in the period that is skipped
is invalid
when I try to convert time 2020-03-29 02:30 from Eastern European Time (GMT+2) to UTC time.
According to this site the clock should change at 03:00 for Finland, which means that time 02:30 should be possible to convert to UTC.
But when I run the following code, an exception is thrown.
var timezoneMap = TimeZoneInfo.GetSystemTimeZones().ToDictionary(x => x.Id, x => x);
var timezoneInfo = timezoneMap["E. Europe Standard Time"];
var localTime1 = DateTime.SpecifyKind(new DateTime(2020, 12, 15, 0, 0, 0), DateTimeKind.Unspecified);
var localTime2 = DateTime.SpecifyKind(new DateTime(2020, 3, 29, 2, 30, 0), DateTimeKind.Unspecified);
var utc1 = TimeZoneInfo.ConvertTimeToUtc(localTime1, timezoneInfo); // 2020-12-14 22:00 correct
var utc2 = TimeZoneInfo.ConvertTimeToUtc(localTime2, timezoneInfo); // throws exception
What is wrong with the second conversion, why is there an exception?
The site you're looking at uses the IANA time zone data, which I believe to be accurate.
But your code uses the Windows time zone database, which in this case looks like it has a discrepancy... it seems to think that the transition is at midnight UTC rather than 1am UTC. Here's code to demonstrate that:
using System;
using System.Globalization;
class Program
{
static void Main()
{
var zone = TimeZoneInfo.FindSystemTimeZoneById("E. Europe Standard Time");
// Start at 2020-03-28T23:00Z - definitely before the transition
var utc = new DateTime(2020, 3, 28, 23, 0, 0, DateTimeKind.Utc);
for (int i = 0; i < 8; i++)
{
var local = TimeZoneInfo.ConvertTime(utc, zone);
Console.WriteLine($"{utc:yyyy-MM-dd HH:mm} UTC => {local:yyyy-MM-dd HH:mm} local");
utc = utc.AddMinutes(30);
}
}
}
Output (annotated):
2020-03-28 23:00 UTC => 2020-03-29 01:00 local
2020-03-28 23:30 UTC => 2020-03-29 01:30 local
2020-03-29 00:00 UTC => 2020-03-29 03:00 local <= Note the change here, at midnight UTC
2020-03-29 00:30 UTC => 2020-03-29 03:30 local
2020-03-29 01:00 UTC => 2020-03-29 04:00 local
2020-03-29 01:30 UTC => 2020-03-29 04:30 local
2020-03-29 02:00 UTC => 2020-03-29 05:00 local
2020-03-29 02:30 UTC => 2020-03-29 05:30 local
The current IANA data definitely does say 1am UTC, as shown in this line of the rules. So I believe the web site is correctly interpreting the data, and it's just a matter of the Windows time zone database being "different" (and, I suspect, incorrect).
I have some problems understanding the DateTimeOffset...
I am trying to create a simple-trigger for a Quartz-Job.
There exists a triggerbuilder with which one can create such a trigger like this:
var triggerbuilder =
TriggerBuilder.Create()
.WithIdentity(triggerName, ConstantDefinitions.InternalDefinitions.AdhocJobGroup)
.StartAt(new DateTimeOffset(scheduledTime));
The scheduledTime is a DateTime. Let's say it is new DateTime(2014, 10, 15, 14, 0, 0);
I live in a city which lies in the Central European Time Zone (UTC+01:00).
When I print
var dto = new DateTimeOffset(new DateTime(2014, 10, 15, 14, 0, 0));
Console.WriteLine(dto);
I get the following result:
15.10.2014 14:00:00 +02:00
What does the +02:00 exactly mean? And why is it +2:00 and not +01:00?
Does that mean, that my trigger will be started at 16:00 instead of 14:00?
Thanks in advance
15.10.2014 14:00:00 +02:00 is a datetimeoffset (datetime + timezone) representing 2pm local time in a timezone of +2 UTC
this is equivalent to 15.10.2014 12:00:00 in UTC
With regard to why is it +02:00 rather than +01:00, is daylight savings active?
Converting Between DateTime and DateTimeOffset
For example, a recurring event occurs every Saturday at 08:00 Central European Time (or 08:00 CEST during summers), regardless of whether there is DST or not. How do I come up with a list of DateTimes representing this event?
Are you looking for this?
List<DateTime> Schedule = new List<DateTime>();
DateTime Base = new DateTime(2013, 11, 9, 8, 0, 0);
for (int i = 0; i < 365; i++)
Schedule.Add(Base.AddDays(i));
Here's a way to get a list of DateTimeOffset values that accurately represent what you asked. If you like, you can convert to DateTime with either result.DateTime or result.UtcDateTime depending on what you are looking for. This will return the next N days from today, in the time zone provided, accounting for DST.
public static IEnumerable<DateTimeOffset> GetNextDaysInZone(int count, DayOfWeek dayOfWeek, TimeSpan localTimeOfDay, string timeZoneId)
{
// get today in the time zone specified
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
DateTime today = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz).Date;
// figure out how many days we are away from the target day of week
int adjustment = dayOfWeek - today.DayOfWeek + (dayOfWeek < today.DayOfWeek ? 7 : 0);
// calculate and return the results
return Enumerable.Range(0, count)
.Select(x =>
{
DateTime dt = today.AddDays(x * 7 + adjustment).Add(localTimeOfDay);
TimeSpan offset = tz.GetUtcOffset(dt);
return new DateTimeOffset(dt, offset);
});
}
Example usage:
DayOfWeek dayOfWeek = DayOfWeek.Saturday;
TimeSpan localTimeOfDay = new TimeSpan(8, 0, 0);
// Note: Despite the name, this represents Central European Time, including both CET and CEST.
string tzid = "Central Europe Standard Time";
var results = GetNextDaysInZone(5, dayOfWeek, localTimeOfDay, tzid);
foreach (var result in results)
{
Console.WriteLine("{0:yyyy-MM-dd HH:mm:ss zzz} ({1:yyyy-MM-dd HH:mm:ss} UTC)", result, result.UtcDateTime);
}
Results:
2013-11-16 08:00:00 +01:00 (2013-11-16 07:00:00 UTC)
2013-11-23 08:00:00 +01:00 (2013-11-23 07:00:00 UTC)
2013-11-30 08:00:00 +01:00 (2013-11-30 07:00:00 UTC)
2013-12-07 08:00:00 +01:00 (2013-12-07 07:00:00 UTC)
2013-12-14 08:00:00 +01:00 (2013-12-14 07:00:00 UTC)
And for good measure, if you'd like to ditch the built-in date/time api and go with something much more robust and reliable, I suggest you try Noda Time. Here is how you might do the same thing as above using Noda Time.
public static IEnumerable<ZonedDateTime> GetNextDaysInZone(int count, IsoDayOfWeek dayOfWeek, LocalTime localTimeOfDay, string timeZoneId)
{
// get today in the time zone specified
DateTimeZone tz = DateTimeZoneProviders.Tzdb[timeZoneId];
Instant now = SystemClock.Instance.Now;
LocalDate today = now.InZone(tz).Date;
// figure out how many days we are away from the target day of week
int adjustment = dayOfWeek - today.IsoDayOfWeek + (dayOfWeek < today.IsoDayOfWeek ? 7 : 0);
// calculate and return the results
return Enumerable.Range(0, count)
.Select(x => (today.PlusDays(x * 7 + adjustment) + localTimeOfDay).InZoneLeniently(tz));
}
Example Usage:
IsoDayOfWeek dayOfWeek = IsoDayOfWeek.Saturday;
LocalTime localTimeOfDay = new LocalTime(8, 0, 0);
// This is just one of the zones that follows CET/CEST
string tzid = "Europe/Berlin";
var results = GetNextDaysInZone(5, dayOfWeek, localTimeOfDay, tzid);
LocalDateTimePattern localPattern = LocalDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss");
OffsetPattern offsetPattern = OffsetPattern.CreateWithInvariantCulture("m");
foreach (var result in results)
{
Console.WriteLine("{0} {1} ({2} UTC)",
localPattern.Format(result.LocalDateTime),
offsetPattern.Format(result.Offset),
localPattern.Format(result.WithZone(DateTimeZone.Utc).LocalDateTime));
}
Results:
2013-11-16 08:00:00 +01:00 (2013-11-16 07:00:00 UTC)
2013-11-23 08:00:00 +01:00 (2013-11-23 07:00:00 UTC)
2013-11-30 08:00:00 +01:00 (2013-11-30 07:00:00 UTC)
2013-12-07 08:00:00 +01:00 (2013-12-07 07:00:00 UTC)
2013-12-14 08:00:00 +01:00 (2013-12-14 07:00:00 UTC)
Both of these approaches take the path of assuming a lenient conversion for times that either don't exist or exist twice on the days of daylight saving time transitions. If you'd like it to not be lenient, then you have some more work to do. But I think that's beyond the scope of what you asked for.
My software displays date/time using local time and then send it to server in UTC. On the server-side I want to add months, years, weeks, days etc to this date/time. However, the question is, if I use such methods with UTC date/time and then convert it back to local time, would the result be always the same, as if I use this methods with local time directly?
This is an example in C#:
// #1
var utc = DateTime.Now.ToUtcTime();
utc = utc.AddWeeks(2); // or AddDays, AddYears, AddMonths...
var localtime = utc.ToLocalTime();
// #2
var localtime = DateTime.Now;
localtime = localtime.AddWeeks(2); // or AddDays, AddYears, AddMonths...
Would the results in #1 and #2 always be the same? Or timezone can influence the result?
The answer may surprise you but it is NO. You cannot add days, weeks, months, or years to a UTC timestamp, convert it to a local time zone, and expect to have the same result as if you had added directly to the local time.
The reason is that not all local days have 24 hours. Depending on the time zone, the rules for that zone, and whether DST is transitioning in the period in question, some "days" may have 23, 23.5, 24, 24.5 or 25 hours. (If you are trying to be precise, then instead use the term "standard days" to indicate you mean exactly 24 hours.)
As an example, first set your computer to one of the USA time zones that changes for DST, such as Pacific Time or Eastern Time. Then run these examples:
This one covers the 2013 "spring-forward" transition:
DateTime local1 = new DateTime(2013, 3, 10, 0, 0, 0, DateTimeKind.Local);
DateTime local2 = local1.AddDays(1);
DateTime utc1 = local1.ToUniversalTime();
DateTime utc2 = utc1.AddDays(1);
DateTime local3 = utc2.ToLocalTime();
Debug.WriteLine(local2); // 3/11/2013 12:00:00 AM
Debug.WriteLine(local3); // 3/11/2013 1:00:00 AM
And this one covers the 2013 "fall-back" transition:
DateTime local1 = new DateTime(2013, 11, 3, 0, 0, 0, DateTimeKind.Local);
DateTime local2 = local1.AddDays(1);
DateTime utc1 = local1.ToUniversalTime();
DateTime utc2 = utc1.AddDays(1);
DateTime local3 = utc2.ToLocalTime();
Debug.WriteLine(local2); // 11/4/2013 12:00:00 AM
Debug.WriteLine(local3); // 11/3/2013 11:00:00 PM
As you can see in both examples - the result was an hour off, one direction or the other.
A couple of other points:
There is no AddWeeks method. Multiply by 7 and add days instead.
There is no ToUtcTime method. I think you were looking for ToUniversalTime.
Don't call DateTime.Now.ToUniversalTime(). That is redundant since inside .Now it has to take the UTC time and convert to local time anyway. Instead, use DateTime.UtcNow.
If this code is running on a server, you shouldn't be calling .Now or .ToLocalTime or ever working with DateTime that has a Local kind. If you do, then you are introducing the time zone of the server - not of the user. If your users are not in the same time zone, or if you ever deploy your application somewhere else, you will have problems.
If you want to avoid these kind of problems, then look into NodaTime. It's API will prevent you from making common mistakes.
Here's what you should be doing instead:
// on the client
DateTime local = new DateTime(2013, 3, 10, 0, 0, 0, DateTimeKind.Local);
DateTime utc = local.ToUniversalTime();
string zoneId = TimeZoneInfo.Local.Id;
// send both utc time and zone to the server
// ...
// on the server
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(zoneId);
DateTime theirTime = TimeZoneInfo.ConvertTimeFromUtc(utc, tzi);
DateTime newDate = theirTime.AddDays(1);
Debug.WriteLine(newDate); // 3/11/2013 12:00:00 AM
And just for good measure, here is how it would look if you used Noda Time instead:
// on the client
LocalDateTime local = new LocalDateTime(2013, 3, 10, 0, 0, 0);
DateTimeZone zone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
ZonedDateTime zdt = local.InZoneStrictly(zone);
// send zdt to server
// ...
// on the server
LocalDateTime newDate = zdt.LocalDateTime.PlusDays(1);
Debug.WriteLine(newDate); // 3/11/2013 12:00:00 AM
I'm using TimeZoneInfo.ConvertTime method to convert time from one to another.
While converting the Date Time 1/1/2006 2.00 AM from Perth to Sri Jeyawardenepura its converted to 1/31/2005 11.30pm
While converting the same time back (1/31/2005 11.30pm) from Sri Jeyawardenepura to Perth its converted to 1/1/2006 3.00 AM.
Why there is one hour difference in the Time Zone conversion?
Wow, this is a Double Whammy! I just stumbled across this post and wasn't going to post anything at all since it's so old and the OP didn't show any code. But then curiosity got the best of me so I checked it out.
Using just the .NET BCL:
string tzid1 = "W. Australia Standard Time"; // Perth
TimeZoneInfo tz1 = TimeZoneInfo.FindSystemTimeZoneById(tzid1);
string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
TimeZoneInfo tz2 = TimeZoneInfo.FindSystemTimeZoneById(tzid2);
DateTime dt1 = new DateTime(2006, 1, 1, 2, 0, 0);
Debug.WriteLine(dt1); // 1/1/2006 2:00:00 AM
DateTime dt2 = TimeZoneInfo.ConvertTime(dt1, tz1, tz2);
Debug.WriteLine(dt2); // 12/31/2005 11:30:00 PM
DateTime dt3 = TimeZoneInfo.ConvertTime(dt2, tz2, tz1);
Debug.WriteLine(dt3); // 1/1/2006 3:00:00 AM
Sure enough, there is the discrepancy that the OP described. At first I thought this must be due to some kind of DST issue, so I checked for Sri Lanka and Perth. While both had a transition in 2006, neither were anywhere close to it for this date. Still, I thought I should check using DateTimeOffset to avoid any ambiguity issues:
string tzid1 = "W. Australia Standard Time"; // Perth
TimeZoneInfo tz1 = TimeZoneInfo.FindSystemTimeZoneById(tzid1);
string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
TimeZoneInfo tz2 = TimeZoneInfo.FindSystemTimeZoneById(tzid2);
DateTime dt = new DateTime(2006, 1, 1, 2, 0, 0);
DateTimeOffset dto1 = new DateTimeOffset(dt, tz1.GetUtcOffset(dt));
Debug.WriteLine(dto1); // 1/1/2006 2:00:00 AM +08:00
DateTimeOffset dto2 = TimeZoneInfo.ConvertTime(dto1, tz2);
Debug.WriteLine(dto2); // 12/31/2005 11:30:00 PM +05:30
DateTimeOffset dto3 = TimeZoneInfo.ConvertTime(dto2, tz1);
Debug.WriteLine(dto3); // 1/1/2006 3:00:00 AM +09:00
And it's still off. You can see that it thinks the target time should be at +09:00, but Perth didn't switch to that until December 3rd 2006. In January it was clearly still +08:00.
So then I thought... Noda Time to the rescue!
First let's check using the same Windows .NET BCL time zones.
string tzid1 = "W. Australia Standard Time"; // Perth
DateTimeZone tz1 = DateTimeZoneProviders.Bcl[tzid1];
string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
DateTimeZone tz2 = DateTimeZoneProviders.Bcl[tzid2];
LocalDateTime ldt1 = new LocalDateTime(2006, 1, 1, 2, 0, 0);
ZonedDateTime zdt1 = ldt1.InZoneStrictly(tz1);
Debug.WriteLine(zdt1.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
ZonedDateTime zdt2 = zdt1.WithZone(tz2);
Debug.WriteLine(zdt2.ToDateTimeOffset()); // 12/31/2005 11:30:00 PM +05:30
ZonedDateTime zdt3 = zdt1.WithZone(tz1);
Debug.WriteLine(zdt3.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
Hey, that seems like it fixed it, right? If so, that would mean that the problem isn't with the Windows time zone data, because Noda Time's BCL provider uses the exact same data. So there must be something actually defective in TimeZoneInfo.ConvertTime. There's Whammy #1.
So just to check that it's all good and well, let's try the same thing with IANA TZDB data. It's known to be more accurate after all:
string tzid1 = "Australia/Perth";
DateTimeZone tz1 = DateTimeZoneProviders.Tzdb[tzid1];
string tzid2 = "Asia/Colombo"; // Sri Jeyawardenepura
DateTimeZone tz2 = DateTimeZoneProviders.Tzdb[tzid2];
LocalDateTime ldt1 = new LocalDateTime(2006, 1, 1, 2, 0, 0);
ZonedDateTime zdt1 = ldt1.InZoneStrictly(tz1);
Debug.WriteLine(zdt1.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
ZonedDateTime zdt2 = zdt1.WithZone(tz2);
Debug.WriteLine(zdt2.ToDateTimeOffset()); // 1/1/2006 12:00:00 AM +06:00
ZonedDateTime zdt3 = zdt1.WithZone(tz1);
Debug.WriteLine(zdt3.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
And there, my friends, is Whammy #2. Notice that the middle time is using a +06:00 offset? I thought this was in error, but when I checked once more here it turns out that the TZDB data is correct. Sri Lanka was at +06:00 at that time. It didn't switch to +05:30 until April.
So to recap the Whammys:
The Windows TimeZoneInfo.ConvertTime function appears to be flawed.
The Windows Time Zone data for the "Sri Lanka Standard Time" zone is incorrect.
All the better to just use Noda Time and TZDB always!
UPDATE
Thanks to Jon Skeet for helping identify that the first problem is with the way that the "W. Australia Standard Time" zone is being interpreted by the TimeZoneInfo class.
I dug much deeper into the .NET Framework reference source code, and I believe this is happening in the private static method TimeZoneInfo.GetIsDaylightSavingsFromUtc. I believe that they are not taking into account that DST doesn't always start and stop in the same calendar year.
In this case, they are applying the 2006 adjustment rule with the 2005 year, and getting an endTime of 1/2/2005 before the startTime of 12/4/2005. They do attempt to reconcile that this should be in 2006 (by incorrectly adding a year), but they don't consider that data is in reversed order.
This problem will probably show up for any time zones that start their DST in the winter (such as Australia), and it will show up in one form or another any time the transition rule changes - which it did in 2006.
I've raised an issue on Microsoft Connect here.
The "second whammy" I mentioned is just because the historical data for Sri Lanka doesn't exist in the Windows time zone registry keys.
Just to add a little more information to Matt's answer, it seems that the BCL is very confused about its own data for Perth. It seems to think there were two transitions around the end of 2005 - one at 4pm UTC, and one eight hours later.
Demo:
using System;
class Test
{
static void Main()
{
var id = "W. Australia Standard Time"; // Perth
var zone = TimeZoneInfo.FindSystemTimeZoneById(id);
var utc1 = new DateTime(2005, 12, 31, 15, 59, 0, DateTimeKind.Utc);
var utc2 = new DateTime(2005, 12, 31, 16, 00, 0, DateTimeKind.Utc);
var utc3 = new DateTime(2005, 12, 31, 23, 59, 0, DateTimeKind.Utc);
var utc4 = new DateTime(2006, 1, 1, 0, 0, 0, DateTimeKind.Utc);
Console.WriteLine(zone.GetUtcOffset(utc1));
Console.WriteLine(zone.GetUtcOffset(utc2));
Console.WriteLine(zone.GetUtcOffset(utc3));
Console.WriteLine(zone.GetUtcOffset(utc4));
}
}
Results:
08:00:00 // 3:59pm UTC
09:00:00 // 4:00pm UTC
09:00:00 // 11:59pm UTC
08:00:00 // 12:00am UTC the next day
This is highly bizarre, and may be related to the Libyan time zone breakage - although that doesn't have two transitions, just one misplaced one.
You would have to post the specific code to be certain. There could be an issue e.g. with daylight time being applied by one conversion but not the other.
There are may nuances of timezone management. Suggest you review this Jon Skeet blog for a great overview.
It is in fact so tricky to correctly use the .NET time classes that Jon has undertaken a port of Joda-Time to .NET, called Noda Time.
It's worth seriously considering for any project that supports multiple time zones.
Have you considered Day light savings when converting times ?
Refer following link and you will get your answer. The time displayed is absolutely correct
http://www.timeanddate.com/worldclock/timezone.html?n=196&syear=2000