简体   繁体   中英

Are adding weeks, months or years to a date/time independent from time zone?

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 without the time zone influencing the result.

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

Would the results in #1 and #2 always be the same?

My money is for YES . I don't see any reason why they wouldn't be the same.

From DateTime.ToLocalTime

The local time is equal to the Coordinated Universal Time (UTC) time plus the UTC offset.

The value returned by the conversion is a DateTime whose Kind property always returns Local. Consequently, a valid result is returned even if ToLocalTime is applied repeatedly to the same DateTime .

From DateTime.ToUniversalTime

The Coordinated Universal Time (UTC) is equal to the local time minus the UTC offset.

Since AddDays , AddYears and AddMonths doesn't change based on TimeZone, there will be no problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM