dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.26k stars 4.73k forks source link

[feature request] add `TimezoneInfo.Current` property #54802

Closed John0King closed 3 years ago

John0King commented 3 years ago

Background and Motivation

In a multple lanauge/region website, there is a problem for timezone handling, the problem describe here https://github.com/dotnet/aspnetcore/issues/33510. In fact, that also effect timezone handling for single region service/website.

this will help for example , Json time deserialize , time model binding etc.

will this new api , we can add Json options like

JsonOptions.TimeZoneHandle =  
TimeZoneHandle.CurrentTimeZone  //  use current timezone  time (it output is for current timezone), for example , convert to current time zone if it can detect timezone (GMT or ISO formate ) and  default to current timezone when it can not detect timezone.
TimeZoneHandle.UTCTimeZone //   the output will be utc time,  and default to utc time when it can not detect timezone.
TimeZoneHandle.Default    //  the output will be the the timezone that it detect; and if it can not detect tiemzone , then use `Unspecified` for DateTime  and use `Current` TimeZone offset for `DateTimeOffset`

Proposed API

namespace System
{
   public sealed  class TimeZoneInfo
{
+    TimeZoneInfo Current {get;set} // default to Local timezone
}

Usage Examples

@{

TimeZoneInfo.Current = TimeZoneInfo.UTC;
//Model.CreateTime is DateTimeOffset type.
// and the value is a local timezone time eg.  `2021-6-28 09:30:00 +08:00`
// razor view
}
<input asp-for="CreateTime" ></input>
@{
// output     2021/6/28 1:30:00 +00:00
}

Risks

dotnet-issue-labeler[bot] commented 3 years ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

svick commented 3 years ago

What would be the relationship between the proposed TimezoneInfo.Current and the existing get-only TimezoneInfo.Local?

John0King commented 3 years ago

Timezoneinfo.Current is about thread(async) level Timezone , and TimezoneInfo.Local is about the device/os 's local timezone , and CurrentTimezoneinfo.Current is equal to TimezoneInfo.Local at beginning

获取 Outlook for Androidhttps://aka.ms/ghei36


From: Petr Onderka @.> Sent: Monday, June 28, 2021 8:11:48 PM To: dotnet/runtime @.> Cc: john @.>; Author @.> Subject: Re: [dotnet/runtime] [feature request] add TimezoneInfo.Current property (#54802)

What would be the relationship between the proposed TimezoneInfo.Current and the existing get-only TimezoneInfo.Local?

― You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/dotnet/runtime/issues/54802#issuecomment-869631284, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADIB32XCPASNAWMKNJDHHV3TVBRIJANCNFSM47M7Z4DA.

KalleOlaviNiemitalo commented 3 years ago

If TimeZoneInfo.ClearCachedData changes TimeZoneInfo.Local, then does it change TimeZoneInfo.Current as well?

DateTimeOffset create should use the new CurrentTimezone to create new value when there is no TimeSpan parameter.

I think you mean the DateTimeOffset(DateTime) constructor, because all other constructors of DateTimeOffset have a TimeSpan parameter.

Currently, that constructor uses DateTime.Kind for choosing between UTC and local time. There is no DateTimeKind.Current constant for TimeZoneInfo.Current. Adding DateTimeKind.Current would make DateTime not fit in 64 bits, and it seems unlikely that an application would create a DateTime with DateTimeKind.Current rather than use DateTimeOffset.

Reading TimeZoneInfo.Current would be slower than TimeZoneInfo.Local, because of the AsyncLocal lookup.

John0King commented 3 years ago

then does it change TimeZoneInfo.Current as well

both is accepable. because when you set a new value in a new thread(async) , that will not be effect.

There is no DateTimeKind.Current constant for TimeZoneInfo.Current

I'm saying , if your DateTime.Kind is Local or Unspecified , then it will use TimeZoneInfo.Current as the "Local", and it do not need DateTimeKind.Current . A simple understand is that TimeZoneInfo.Current is thread local time, and TimeZoneInfo.Local is OS/(browser) local time.

Reading TimeZoneInfo.Current would be slower than TimeZoneInfo.Local, because of the AsyncLocal lookup

we already using CultureInfo.CurrentCulture for years.

and please also read my issue in dotnet/aspnetcore#33510, to see what problem is reslove.

Clockwork-Muse commented 3 years ago

I'm saying , if your DateTime.Kind is Local or Unspecified

Frankly, DateTime.Kind is a trap, and always has been. You'd be far better off getting, setting, and using DateTimeOffset for almost all of this (and even this isn't always correct, because in many cases you want a DateTimeZoned equivalent).

will this new api , we can add Json options like

This is not the correct way to handle this. For serialization, date-time values should be either:

  1. UTC explicitly.
  2. A zoned date-time, with all of the following: local date-time, the offset in affect at that local time, and the (tzdb) time zone id, and used to reconstruct a DateTimeOffset/DateTimeZoned.

Anything else will cause weird ambiguities and application errors. You cannot just "translate" a date-time value in one zone (not offset!) to another, because the knowledge about Daylight Savings Time is important. It is occasionally acceptable to display in a localized timezone, but the actual date/time data cannot be trivially modified.

ghost commented 3 years ago

Tagging subscribers to this area: @dotnet/area-system-runtime See info in area-owners.md if you want to be subscribed.

Issue Details
## Background and Motivation In a multple lanauge/region website, there is a problem for timezone handling, the problem describe here https://github.com/dotnet/aspnetcore/issues/33510. In fact, that also effect timezone handling for single region service/website. this will help for example , Json time deserialize , time model binding etc. will this new api , we can add Json options like ```C# JsonOptions.TimeZoneHandle = TimeZoneHandle.CurrentTimeZone // use current timezone time (it output is for current timezone), for example , convert to current time zone if it can detect timezone (GMT or ISO formate ) and default to current timezone when it can not detect timezone. TimeZoneHandle.UTCTimeZone // the output will be utc time, and default to utc time when it can not detect timezone. TimeZoneHandle.Default // the output will be the the timezone that it detect; and if it can not detect tiemzone , then use `Unspecified` for DateTime and use `Current` TimeZone offset for `DateTimeOffset` ``` ## Proposed API ```diff namespace System { public sealed class TimeZoneInfo { + TimeZoneInfo Current {get;set} // default to Local timezone } ``` ## Usage Examples ``` razor @{ TimeZoneInfo.Current = TimeZoneInfo.UTC; //Model.CreateTime is DateTimeOffset type. // and the value is a local timezone time eg. `2021-6-28 09:30:00 +08:00` // razor view } @{ // output 2021/6/28 1:30:00 +00:00 } ``` ## Risks - Json and Model binding and TagHelper need new options for fit the new API. - `DateTimeOffset` create should use the new `CurrentTimezone` to create new value when there is no `TimeSpan` parameter. - `TimeZone` class has a `CurrentTimeZone` get only property.
Author: John0King
Assignees: -
Labels: `api-suggestion`, `area-System.Runtime`, `untriaged`
Milestone: -
John0King commented 3 years ago

DateTime.Kind is a trap, and always has been.

agree. a better DateTime should like Javascript Date , it's a UTC value , but show local time .

and this TimeZoneInfo.Current is force on DateTimeOffset , and DateTime is just related. in fact, it will be incorrect(changed) for share a DateTime in multiple Thread if datetime use "current thread local" for DateTimeKind.Local ,
so, with this feature request, to share a DateTime in multiple thread must using Unspecified or UTC or the app don't set thread level timezone at all. but I think this is still acceptable.

For serialization, date-time values should be either

this doesn't effect serialization at all !!!! it only effect DeSerialization (eg. convert string to DateTimeOffset) , it will have the ability to give us a "local"ed time, instead of just UTC, and need to be handle by user code .

for example:

TimeString : 2021-02-01T00:00:00.0+08:00 CurrentTimeZone: -08:00

JsonOption.TimeZoneHandle DateTimeOffset DateTime
CurrentTimeZone new DateTimeOffset(2021,1,31, 8,0,0, TimeSpan.FromHours(-8)) new DateTime(2021,1,31,8,0,0, Local)
UTCTimeZone new DateTimeOffset(2021,1,31, 16,0,0, TimeSpan.FromHours(0)) new DateTime(2021,1,31,16,0,0, UTC)
Default new DateTimeOffset(2021,2,1, 8,0,0, TimeSpan.FromHours(8)) new DateTime(2021,2,1, 0,0,0,Unspecified) **[***]**

and if TimeString is : 2021-01-31 16:00:00Z then

JsonOption.TimeZoneHandle DateTimeOffset DateTime
CurrentTimeZone new DateTimeOffset(2021,1,31, 8,0,0, TimeSpan.FromHours(-8)) new DateTime(2021,1,31,8,0,0, Local)
UTCTimeZone new DateTimeOffset(2021,1,31, 16,0,0, TimeSpan.FromHours(0)) new DateTime(2021,1,31,16,0,0, UTC)
Default new DateTimeOffset(2021,1,31, 16,0,0, TimeSpan.FromHours(0)) new DateTime(2021,1,31,16,0,0,Unspecified) **[***]**

and if TimeString is : 2021-02-1 00:00:00 (no timezone info) then

JsonOption.TimeZoneHandle DateTimeOffset DateTime
CurrentTimeZone new DateTimeOffset(2021,2,1, 0,0,0, TimeSpan.FromHours(-8)) new DateTime(2021,2,1,0,0,0, Local)
UTCTimeZone new DateTimeOffset(2021,2,1, 0,0,0, TimeSpan.FromHours(0)) new DateTime(2021,2,1,0,0,0, UTC)
Default new DateTimeOffset(2021,2,1, 0,0,0, TimeSpan.FromHours(-8)) **[***]** new DateTime(2021,2,1,0,0,0,Unspecified)

so you can see, most is easy , we need talk about how we deal with the *** cells . and note , this is a example of Json , but the model binding is facing the same problem !

Clockwork-Muse commented 3 years ago

agree. a better DateTime should like Javascript Date , it's a UTC value , but show local time .

This is actually worse. You do not always want UTC, and having the value always be UTC behind the scenes makes certain types of work (anything dealing with DST especially) difficult or impossible. Always displaying "local" time is also not correct.

this doesn't effect serialization at all !!!! it only effect DeSerialization (eg. convert string to DateTimeOffset) , it will have the ability to give us a "local"ed time, instead of just UTC, and need to be handle by user code .

You should not convert to a "local" value, including for UTC. Persisted zones and offsets are actually important. Deserialization is the other half of this coin, and the same rules apply. You must retain this source data, or your code will deal with incorrect values.

While there are a few places where you may display a "local" value, it should not be done at de/serialization time, and care must be taken with which values you decide to do so, it's not appropriate for all values.

and if TimeString is : 2021-02-1 00:00:00 (no timezone info) then

If the field is not explicitly (by documentation, say) in UTC, nor is there some other field(s) with the offset+zone, please berate whoever is serializing them, because they've given you junk data. Translating to "local" is only going to make the problem worse. In fact, Local itself is actually the source of the problem.

John0King commented 3 years ago

You should not convert to a "local" value, including for UTC. Persisted zones and offsets are actually important.

I can not do that, because javascript's Json.stringfy() will give your a utc'ed iso time format. and EF's MySql provider using utc DateTime to store DateTimeOffset and plus , user won't write/chose a UTC time in a timezone recognizable format.

In my understand, DateTimeOffset store a unique time point, it's the same as JavaScript's Date , but more powerful , and have more information. So MySql EF Provider using utc DateTime to store a unique time point have no problem.

because they've given you junk data. Translating to "local" is only going to make the problem worse.

It's normally a China culture/timezone only app, so people write / understand in +08:00 timezone. and without settable "Local" timezone or with only a "UTC" timezone , it's much harder to dealing with Time (even I use DateTimeOffset). In my understand, since DateTimeOffset is a time point, we should convert it to user's local timezone before show it, no matter what offset value in original DateTimeOffset.

for example: Chine user's 2021-2-1 00:00 (+08:00) is Japanese user's (+09:00) 2021-2-1 01:00, and when they work together, only the time point info is valuable in the system.
and this topic/issue is asking for follow features:

  1. Add an official property to store the timezone info for current user/request/session
  2. Use this property to make handle time and timezone easier
  3. third party library can take advantage of this property and make dev use thire library easier.

And if you want deal with UTC, you still can, for example in JSON DeSerialization , you can use JsonOption.TimeZoneHandle = TimeZoneHandle.UTCTimeZone. it's just a suggestion for System.Text.Json and Newtonsoft.Json , not the topic of this issue.

we should talk about will TimezoneInfo.Current reasonable for the BCL, because even we use "User profile timezone setting" , we still need somewhere to store this info.

Clockwork-Muse commented 3 years ago

You should not convert to a "local" value, including for UTC. Persisted zones and offsets are actually important.

I can not do that, because javascript's Json.stringfy() will give your a utc'ed iso time format. and EF's MySql provider using utc DateTime to store DateTimeOffset and plus , user won't write/chose a UTC time in a timezone recognizable format.

For javascript, you need to either get a better type, or write a manual stringification helper. That it's the API behavior doesn't make it the correct behavior. For EF, you'd probably need manual conversion methods regardless.

In my understand, DateTimeOffset store a unique time point, it's the same as JavaScript's Date , but more powerful , and have more information. So MySql EF Provider using utc DateTime to store a unique time point have no problem.

Emphasis mine. That is, literally, the problem I'm referring to. Yes, from an "absolute instant" point of view the two values may be equivalent, and in some cases it is fine to do this translation. However in most of them if your application is working with such a value it's going to need to keep the offset (if not the zone, too).

because they've given you junk data. Translating to "local" is only going to make the problem worse.

It's normally a China culture/timezone only app, so people write / understand in +08:00 timezone. and without settable "Local" timezone or with only a "UTC" timezone , it's much harder to dealing with Time (even I use DateTimeOffset). In my understand, since DateTimeOffset is a time point, we should convert it to user's local timezone before show it, no matter what offset value in original DateTimeOffset.

for example: Chine user's 2021-2-1 00:00 (+08:00) is Japanese user's (+09:00) 2021-2-1 01:00, and when they work together, only the time point info is valuable in the system.

Sure, there are some cases (not always) when you can/should translate such values when they're displayed (and not before). The problem that I'm referring to here is that if the Japanese user gives you a value without an offset and the user in China deserializes it, the value is going to be wrong (... unless the value turns out to be in UTC and it was deserialized with TimeZoneHandle.UTCZone, but for clarity it should still have been serialized with 'Z' anyways).

As an example of when you can't change times to the "local" zone, reporting for many stores doesn't make sense if you translate it into the zone of the viewer (especially if the viewer happens to be on the other side of the world) - you get sales numbers that appear to be in the middle of the night, or on holidays they're supposed to be closed, or other oddities. Instead the times must be reported with respect to the store's time zone.

and this topic/issue is asking for follow features:

  1. Add an official property to store the timezone info for current user/request/session
  2. Use this property to make handle time and timezone easier
  3. third party library can take advantage of this property and make dev use thire library easier.

That's potentially fine (although I personally dislike these sorts of implicit contexts, and prefer them to be passed around explicitly). Note that while what you're asking for can be used to solve the issue in the aspnetcore repo, it's not necessarily the way they're going to handle it.

And if you want deal with UTC, you still can, for example in JSON DeSerialization , you can use JsonOption.TimeZoneHandle = TimeZoneHandle.UTCTimeZone. it's just a suggestion for System.Text.Json and Newtonsoft.Json , not the topic of this issue.

This is what I'm objecting to, because it shows a misunderstanding of how to deal with date and time.

John0King commented 3 years ago

I'm referring to here is that if the Japanese user gives you a value without an offset and the user in China deserializes it, the value is going to be wrong (... unless the value turns out to be in UTC and it was deserialized with TimeZoneHandle.UTCZone, but for clarity it should still have been serialized with 'Z' anyways As an example of when you can't change times to the "local" zone, reporting for many stores doesn't make sense if you translate it into the zone of the viewer

I'm starting understand why you arguing me with this. This is because you and me understand the DateTimeOffset and DateTime in different way. you understand them in "string" value : 2021-7-1 09:00 , I understand them in "time point" : 1625101200000 (Unix Time Milliseconds)

and in my understand

> var t1 = new DateTimeOffset(2021, 7, 1, 9, 0, 0, TimeSpan.FromHours(8));
> var t2 = new DateTimeOffset(2021, 7, 1, 1, 0, 0, TimeSpan.Zero);
> t1 == t2
true

and t1 is the "Local" time to me. and In my computer (+08:00).

> var t1 = new DateTime(2021, 7, 1, 9, 0, 0,DateTimeKind.Local);
> var t2 = new DateTime(2021, 7, 1, 1, 0, 0, DateTimeKind.Utc);
> t1.ToUniversalTime()  == t2.ToUniversalTime();
true
> t1.ToLocalTime() == t2.ToLocalTime()
true

the only time that not make sence is "DateTimeKind.Unspecified" , mostly the "[***]" cells in above table and in javascript , you can give a time in any timezone (but with a timezone info), and they are also equals.

in such case , Json de/serialize and model binding take advantage of the TimeZoneInfo.Current that make sence. and provide simplest way to create single culture/timezone website/app/service.

  1. de/serialize and model binding bind to current timezone time (note, with different offset, the time should be equal)
  2. store to db , and db/orm chose a offset to store your time. for example: Sqlserver store DateTimeOffset with your offset, and MySql store DateTime with UTC
  3. query the data from db, and orm convert back to your "Current" timezone ,
  4. send to user with
    1. use json , and json serialize it using iso format, with your local offset or utc offset
    2. use server render like MVC, TagHelper/HtmlHelper/Utils convert to your "Local" timezone, and display (because the "thread Local" eg. "Current" is detected by some timezone detect method , user profile/ culture map with timezone / cookies)

and this is what I understand , how Json/model bind take advantage of the TimeZoneInfo.Currnet , and they should have options for users using "Current“ or "UTC".

and the model bind option and json option will make follow input reasonable: 2021-7-1 09:00 (without timezone , and the option give you one, (current local or UTC))

Clockwork-Muse commented 3 years ago

I'm starting understand why you arguing me with this. This is because you and me understand the DateTimeOffset and DateTime in different way. you understand them in "string" value : 2021-7-1 09:00 , I understand them in "time point" : 1625101200000 (Unix Time Milliseconds)

Almost - I'm arguing that, in many cases, the "string" value is the more important and relevant one, and you can't just toss the original offset/zone.

The problem with your JSON deserialization idea is that it would apply to the entire document, which is too coarse. Even were it to apply per-field, applying it to a future field would silently alter the original data.

in such case , Json de/serialize and model binding take advantage of the TimeZoneInfo.Current that make sence. and provide simplest way to create single culture/timezone website/app/service.

Although certain user-facing views may only need to work with a single timezone at a time, no modern app only has a single timezone to worry about, even for China - in a simple case, if your app is accessed from outside China the local timezone may make an appearance.

  1. store to db , and db/orm chose a offset to store your time. for example: Sqlserver store DateTimeOffset with your offset, and MySql store DateTime with UTC

If the offset was relevant for the original value, it doesn't magically become irrelevant simply because your solution domain doesn't automatically support it. You'd need to manually store the offset and reconstruct the original value on reading from the database. Note that NO rdbms properly handles a DateTimeZoned type (which is distinct from DateTimeOffset), so if you're dealing with future dates you have to account for the zone id anyways.

and the model bind option and json option will make follow input reasonable: 2021-7-1 09:00 (without timezone , and the option give you one, (current local or UTC))

If the passed value is from an "unknown" or otherwise undiscoverable (to whoever deserializes) timezone, the value is unusable. It would be far better to be specific about the totality of the value (Adding offset, and potentially zone id).

John0King commented 3 years ago

so far, Can we agree that the TimeZoneInfo.Current is a useful property ?

and base on that,

If the value is in the future and the local time was relevant (which is most cases)

I'm confused , why future time is different ? Please just think like javascript Date , and forget C# 's DateTime + DateTimeKind.Unspecified , Local Time is the same as UTC time, they represent the same "Time Point" on Earth.

I guess , you'r think DateTime.ToString() will loss the "Local timezone" information. but when you use DateTime.Now.ToString("o") and look at what the time is !

> DateTime.Now.ToString("o")
"2021-07-02T08:58:06.3467375+08:00"

and I think the DateTime.Now.ToString("u") have a bug, because it change the time point that it represent before. and only DateTimeKind.Unspecified should be ignore the timezone(eg. offset) handling.

and please don't think about DateTime for now, only think DateTimeOffset,

I can not imagine what's the case that the Offset is so important .

The problem with your JSON deserialization idea is that it would apply to the entire document, which is too coarse.

  1. it shoud (must) have a option that do no change it , that is the Default option.
  2. the timezone handling will apply anyway , it just there no way to change to another today . (that's saying it will always be the "defualt" option today )

and I think my "TimeZoneHandding.Default" have some issue, it should be DefautOrAsLocal DefaultOrAsUTC, and that will eliminate every "[***]" cells in above table.

If the passed value is from an "unknown" or otherwise undiscoverable (to whoever deserializes) timezone, the value is unusable.

I don't know how to input time in a <input /> control in US region, but In China, we use this "no timezoned format" many yeas, and a bunch of time/date picker use this "no timezoned format", for example bootstrap date picker . so I think, in US , people still use local time to edit/change/pick time. and the only different is , it either be handled by user(developer) or it handled by Model Binding or simply handled by DateTimeOffset.Parse().

You'd need to manually store the offset and reconstruct the original value on reading from the database. Note that NO rdbms properly handles a DateTimeZoned type

I test is in SqlServer and same time point is the same.

屏幕截图 2021-07-02 091742

and with different offset(eg. timezone) , if you pass it to javascript framework(Angular/React/Vue) , there no problem, to re-edit the date (because the json, it be became UTC +00:00 offset when it post back to server, but the time point is the same) , and the problem is when you pass it to InputTagHelper , and it do not handle the timezone for you.

KalleOlaviNiemitalo commented 3 years ago

Future time can be affected by changes in time zone rules. If you have booked an appointment for 9 AM local time, and the nation then abolishes daylight saving time, this can change the UTC time of your appointment. To support changes like that, your database would need to store the time zone identifier too, not only the offset.

Clockwork-Muse commented 3 years ago

Future time can be affected by changes in time zone rules. If you have booked an appointment for 9 AM local time, and the nation then abolishes daylight saving time, this can change the UTC time of your appointment. To support changes like that, your database would need to store the time zone identifier too, not only the offset.

This (and this is the exact example I would have used). Note that DST rules, globally, change multiple times a year, and some countries (at least historically) have changed their rules every year, or for unpredictable reasons (including, allegedly, one country due switching a week early due to above-average rainfall).

I don't know how to input time in a <input /> control in US region, but In China, we use this "no timezoned format" many yeas, and a bunch of time/date picker use this "no timezoned format", for example bootstrap date picker . so I think, in US , people still use local time to edit/change/pick time. and the only different is , it either be handled by user(developer) or it handled by Model Binding or simply handled by DateTimeOffset.Parse().

Sure, your users might be inputting local time into an input field, but that doesn't make the timezone unknown - in most cases, it's going to be the time of the user profile or client. However, the moment you want to communicate that value to some other party, who doesn't know the (or have their own settings for "local") timezone, you must also pass the offset+zone id.

I test is in SqlServer and same time point is the same.

That is DateTimeOffset. DateTimeZoned is a different concept - if you add or subtract an amount of time to one, it changes the offset when DST or other changes occur. In most cases, this is the time type people actually think in (DateTimeOffset is somewhat niche).


Outside of a relatively small list of thoughtful libraries, most computer languages and frameworks deal poorly or otherwise insufficiently with anything other than absolute stamps. They mostly get away with it because in many cases they don't end up needing to - you need to be a calendaring or similar scheduling app for it to really matter, otherwise you're dealing with logged dates and can just use the absolute stamps.

KalleOlaviNiemitalo commented 3 years ago

The IANA Time Zone Database mailing list archives are interesting reading if you want to see how nations announce changes so late that there is not much time to deploy updates to devices, e.g. two weeks in South Sudan.

John0King commented 3 years ago

Now I fully understand why you oppose the "local" time. There no DST time in my country , but there do have a similar DST rule for most company , for example : [5-1] --- [10-1] , work time from 8:00 -- 17:30 , and after 10-1 to next year's 5-1, work time from 8:30 --- 18:00 .

So I'm not very understand how DST time work in DateTime and DateTimeOffset . I guess, the time system is as UTC time point , but only effect the time that we get from system. eg.

> var t = DateTime.Now
>  t.AddHour(1) == t
false  // should always be false

//---------------
// but 
>var t = DateTime.Now.AddHour(1);
> await Task.Delay(10000);
> t == DateTime.Now
// this may be `true` , because the DateTime.Now may change because DST rules

and IMO, DST time is really useless in a System/Service, if it only effect the Time been shown , then the "Local" will still point to UTC + Offset, but the Local shown time isn't.

Clockwork-Muse commented 3 years ago

So I'm not very understand how DST time work in DateTime and DateTimeOffset .

Technically they don't. A couple of the constructors/properties access info that's used to get a localized time, but after that neither type really handle DST (you can cause DateTime to be an invalid DateTimeKind.Local time). This, combined with the current API for timezones, make certain things almost impossible.

That aside, it's not just DST rules - you still need to be aware of the relevant timezones for all date/time values, because the base offset can change as well. For instance, a few years ago North Korea changed their timezone from +9 to +8:30 for a couple of years, which would trigger this type of concern as well (they only gave a few weeks notice, too, which would have been really disruptive if they had more international connections).

tannergooding commented 3 years ago

@tarekgh, could you weigh in here since you've driven many of the Date/Time related changes recently.

tarekgh commented 3 years ago

Thanks @Clockwork-Muse and @KalleOlaviNiemitalo for guiding @John0King through the issues.

The proposal as it is not the best to go with:

@John0King, if you still believe your proposal is reasonable, please explain in detail why you think so, how this proposal going to work in general with DateTime/DateTimeOffset, how this proposal is addressing the scenarios you have. Thanks for your report.

John0King commented 3 years ago

I did a small research on other languages, like Java and PHP and python , They both allow set the default Timezone .

The current non settable TimeZoneInfo.Local will have problem is your OS in other Timezone that you do not want. But with the TimeZoneInfo.Current proposal , we can keep this non-settable property (for OS local timezone), and use this new property instead (our app timezone if you only set it at begin of app start , or thread timezone if you only set it at begin of new thread / async ).

And , Yes , I can use my own AsyncLocal<T> , and asp.net core also can add a IRequestTimezoneFeature , but that will have a "cost" on each request , and use other static property is not an "standard" way, that will make other library/framework/ modules processing their own CurrentTimeZone and hard to unify .

If asking to have the TimeZoneInfo.Current to affect DateTime and DateTimeOffset operations

I don't , I live in a country that do not have DST, and i'm glade that so far I never facing DST time or any non-standard time issue. This property is use for show people local time as back-store , you can use that to display or convert to this timezone, otherwise you will need another way to store the timezone. and IMO , we should never use DST time and only allow UTC or standard time offset time to store the time value, I think this is why many lanauge's JSON serializer use unix ms tick to serialize time. but I do not know how DateTime.Now DateTimeOffset.Now work in DST timezone.

and for JSON and Model Binder , They already have their own "default" value for "non-standard" time format. Why refuse to have a option to set it to another "OFFSET" , I mean why "2021-07-07 00:00" must be "2021-07-07 00:00 +00:00" instead of "2021-07-07 00:00 +08:00" , this rule is not reasonable for a China app/service, because DateTimeOffset.Parse("2021-07-07 00:00") is "2021-07-07 00:00 +08:00" instead of "2021-07-07 00:00 +00:00" , and we already use that for many yeas since DateTimeOffset was born. (eg. var createTime = DateTimeOffset.Parse(Context.Request.Query["CreateTime"]))

and current MVC Core's InputTagHelper is not accept TimeZoneInfo nor Offset ,

I still think there is valuable for this TimeZoneInfo.Current exist, it can unify how/where we obtain a TimeZoneInfo in a standard API way.

lat , even we close this , we should figure out follow problem before close.

  1. how timezone and time be handle in other lanague/platfrom
  2. how to unify 3rd party librares to handle your timezone ?
  3. how we handle no offset time format and how we custom that ? (DateTimeOffset.Parse rule or MVC's new DateTimeBinder rule)
tarekgh commented 3 years ago

how timezone and time be handle in other lanague/platfrom

Every platform can handle date/time and time zone differently. In .NET it is not a goal we try to mimic other platforms or languages. We need to think about the scenario need to be supported and how we can support it.

how to unify 3rd party librares to handle your timezone ?

I am not sure what you mean by unify here. time zone handling in .NET is already unified and used consistently.

how we handle no offset time format and how we custom that ? (DateTimeOffset.Parse rule or MVC's new DateTimeBinder rule)

.NET provide the flexibility to format and parse the date and time in the way you like. I don't think this is an issue.

If the proposed TimeZoneInfo.Current not affecting any functionality and it is used just to store instance of time zone object, then it is less useful and not providing much value. I understand your point about have a consistent way across libraries to do that but think about the confusion it adds here if we introduced it. Anyone set Current time zone will expect to see it reflected on the subsequent operations of DateTime and DateTimeOffset. users will be very confused between Local and Current.

We already have another issue https://github.com/dotnet/runtime/issues/36617 which tracking similar ask to have more generic way setting current date/time.

Clockwork-Muse commented 3 years ago

They both allow set the default Timezone . ... that will make other library/framework/ modules processing their own CurrentTimeZone and hard to unify .

Despite what you might think, this actually causes problems. More specifically, since the property is mutable, those same libraries/frameworks/modules *will set it, and potentially not reset it, leading to some rather unwanted behavior.

I don't , I live in a country that do not have DST, and i'm glade that so far I never facing DST time or any non-standard time issue.

DST isn't relevant here, this is strictly a multiple timezone question.

This property is use for show people local time as back-store , you can use that to display or convert to this timezone, otherwise you will need another way to store the timezone. and IMO , we should never use DST time and only allow UTC or standard time offset time to store the time value, I think this is why many lanauge's JSON serializer use unix ms tick to serialize time.

Unfortunately, despite my own personal dislike of DST/Timezones, that's not the world we live in, and as previously discussed, that doesn't work.

Why refuse to have...

DateTimeOffset.Parse actually looks at the current value of TimeZoneInfo.Local, but presumably your service is running set to UTC (like it should be). Note that adding a mutable property won't help, because switching the current Parse to use it would be a major breaking change. The real problem, as we previously discussed, is that the examples shown don't correctly handle user timezones in the first place; you should either be explicitly passing the offset as part of the value, or setting it afterwards based on other data.

tarekgh commented 3 years ago

CC @mattjohnsonpint

John0King commented 3 years ago

The CultureInfo.CurrentCulture is mutable too , and it also effect parsing/displaying/Calendar etc. It'll be the same as TimeZoneInfo.Current.

Unfortunately, despite my own personal dislike of DST/Timezones, that's not the world we live in, and as previously discussed, that doesn't work.

base on context, I'm saying to store (to db or elsewhere) the time , we should store a time point , for example :
a) unix ms tick b) Utc DateTime c) DateTimeOffset d) GMT string

if not consider about the DST and DateTime , and only think about DateTimeOffset then a Local DateTimeOffset + Local Offset will be the same time point with UTC on Earth. and That's I'm saying. and with the TimeZoneInfo.Current ORM can store the time in UTC time and query from db and convert back to your "Current" timezone Offset (only DateTimeOffset). In fact, we need do this convert by our self if ORM not do this to us, and with this standard api , ORM can have an option to do this for us. (I'm already using DateTimeOffset anywhere)

and another section is ModelBinding , how to recognize non-standard time format string. (eg. DateTimeOffset.Parse supported format) , and please do not say it's not the best practice again, it's a reality problem, and now with Angualr/Vue/React , the date picker give us javascript Date , and as I say before, It's a UTC value with display to local .

because switching the current Parse to use it would be a major breaking change.

we add this property now , and no api/framework will change it until the framwork take advantage of that , so no breaking change will happen, plus , it will be the same value as "TimeZoneInfo.Local" on app start.

and although it's a AsyncLocal<T> , but I think most people using it just to set default "Local" value at app start.

Clockwork-Muse commented 3 years ago

The CultureInfo.CurrentCulture is mutable too , and it also effect parsing/displaying/Calendar etc. It'll be the same as TimeZoneInfo.Current.

... and that causes problems (which is why I personally dislike implicit context state like this).

it's a reality problem, and now with Angualr/Vue/React , the date picker give us javascript Date , and as I say before, It's a UTC value with display to local .

well, the reality is that your app/api is buggy.

The short observation here is that you have a "local" (as in, specific to you) problem in your solution space, but your proposal is asking for changes in global space. Would it make your program easier? Possibly, but that doesn't negate the specification concerns I outlined, or ultimately change my implementation recommendations. In addition, the proposal would also need to be factored into aspnetcore and possibly other repositories, which would delay delivery to you - at a naive guess I'd say probably six months to a year before things "just work" for you, during which time you'd need to fix your app anyways...

we add this property now , and no api/framework will change it until the framwork take advantage of that , so no breaking change will happen

The very fact that you've changed the property it uses to determine the "current" offset is the breaking change; then, as some libraries adapt to the change breaks would "smear" across the library ecosystem, which ends up being a version of dll hell.

I think most people using it just to set default "Local" value at app start.

If this is the common case, then it isn't any different than TimeZoneInfo.Local. This proposal is really only beneficial when you have multiple async contexts in flight that should have different zones/cultures, which means multi-client (primarily web) services.

ghost commented 3 years ago

This issue has been automatically marked no recent activity because it has been marked as needs author feedback but has not had any activity for 14 days. It will be closed if no further activity occurs within 7 more days. Any new comment (by anyone, not necessarily the author) will remove no recent activity

John0King commented 3 years ago

... and that causes problems (which is why I personally dislike implicit context state like this).

this is a library auth's design issue, JavaScript dont't even have a fixed type , and you still use it to write great and large applications.

I think this information ("Current Timezone") should have a global variable to store it, and as an standard that 3rd party framework/library depend on that.

The reason I called it "TimezoneInfo.Current" instead change the TimezoneInfo.Local is for compatibility, because when change TimezoneInfo.Local then we need a new property to store the OS timezone, and earlier app's may be affect by the modified Local property, and by using an new property , no library/app will be affected (or affected only a little),

and as I said earlier, the Json and Modal Binding can do some handy action base on that , and also can not make it too (I personally want they can have an opt-in option to deal with it)

tarekgh commented 3 years ago

The reason I called it "TimezoneInfo.Current" instead change the TimezoneInfo.Local is for compatibility, because when change TimezoneInfo.Local then we need a new property to store the OS timezone, and earlier app's may be affect by the modified Local property, and by using an new property , no library/app will be affected (or affected only a little),

@John0King I replied before in https://github.com/dotnet/runtime/issues/54802#issuecomment-875734384 we are concerned to introduce such property. If this new property not going affect any functionality (e.g. DateTime.Now), then it wouldn't be useful and will create a confusion for the APIs users to know when to use Local property and when to use Current property. If this proposed property is going to affect any of such functionality (e.g. DateTime.Now), then that will be a big concern for app compatibility. either way this proposal will not fit the framework needs. I mentioned before we have the issue #36617 which discussing how the framework can add some interfaces mocking the system time in general, you may follow up there or propose any addition there.

Also, @Clockwork-Muse showed many concerns with the proposal which not addressed.

By that, I am going to close this issue. Thanks for your report.

John0King commented 3 years ago

@tarekgh
that proposal # #36617 is different than this one , this one indent to modify the way how DateTime/DateTimeOffset been created, and has information to convert to and back to the same "time point" , in fact , I can not understand why we need a ISystemClock to get "Current" time, this one realted to "Timezone Offset" , that one is to detect "Current Time" , I know that in this two may have something cross , but the purpose is entirely different.

as @Clockwork-Muse mention that the modifiable property may cause some problem , but the fact is that we already have "CultureInfo.CurrentCulture" , and library author or framework auth or the app auth(lib consumer) , we don't change it , and we do rely on that . in fact, the DateTime and DateTimeOffset create using the "Local" is can be midify too if we change OS's timezone and time, so why introduce such property can cause such bad things of your team concerned can happen ?

Oh, Do I also mention that, today we must modify the timezone to we want in the docker image, and other language such like PHP, Java can just use one line of code to control it.

and time string "2021-08-10 08:30" be used in China, we use it with PHP, we use it with Java, we use it with Node, we use it with GO, we use it Python, but why it must be so different in .Net ??????? "2021-08-10 08:30" means "2021-08-10 08:30 +08:00" in China, also means "1628555400000" linux milliseconds in China. I don't get why someone always said "you should use it" , does this is what we as a developer can control ? it's the users's choice and how they understand .
Stop saying that, it's the fact , and bootstrap and other time/date picker prove that it a global user choice not just China users. and we already use that format almost 16 years , we must respect the history first, othewise "枪打出头鸟" (translate: The gun hits person who takes the lead)

and WebForm have Date picker and in MVC/Razor have input taghelper for Datetime , and when you use some jquery base Date picker , will it add the offset for you ?

Clockwork-Muse commented 3 years ago

@John0King - You're right about that proposal mostly talking about DateTime(Offset), but really the zone used should probably be included as part of the clock as well (so would cover your use case as well).

Oh, Do I also mention that, today we must modify the timezone to we want in the docker image, and other language such like PHP, Java can just use one line of code to control it.

In almost no case should the timezone of the server be relevant. Instead, the relevant timezone should be picked up during a call.

we use it with PHP, we use it with Java, we use it with Node, we use it with GO, we use it Python, but why it must be so different in .Net ???????

Technically, it's not different in .Net . The actual problem is dealing with different timezones, and the other languages still have the same issues.

bootstrap and other time/date picker prove that it a global user choice not just China users. and we already use that format almost 16 years

Yes, from your user perspective, you should be giving them what appears to be a standard date picker. It's your job (or your web framework, or some library you include) to capture or remember the necessary local timezone and provide it as an additional piece of information for your (especially server) code, so your code can reference timezones that don't come from the current process.

John0King commented 3 years ago

@Clockwork-Muse response from the issue in aspnetcore one

It's an issue with your application not being written to handle timezone passing

Is there any webapp that handle timezone manually, eg. var time = DateTimeOffset.Parse('2021-09-09', TimeZoneSate.MyStaticTimezoneSpan, CultureInfo.CurrentCulture) or ``

back from 2010 to now(2021) , we use DateTimeOffset.Parse() and DateTime.Parse() when we parse time , and there that give us computer local timezone offset's DateTimeOffset and DateTime with Unspecified , and that never have any problem until we use model binding .

and browse always use iso utc formate,

newtonsoft.json have JsonSerializerSettings { DateTimeZoneHandling = DateTimeZoneHandling.Local }
and System.Text.Json will use Kind= local for anytime who have a timezone info, eg. computer timezone = +10:00 , time string = 2021-09-09T00:00+09:00 , the the result is 2021-09-09T02:00+10:00 the only problem is that browse always give you 2021-09-09T00:00Z which will System.Text.Json will give you a UTC time . and this is kind of what I suggestion in https://github.com/dotnet/runtime/issues/54802#issuecomment-870709336

so, I guess no matter how you say it should use UTC and user should manually convert it to "local" , but in reality , everyone use their own local time , and 3rd libraries and 1st libraries are use this pattern too.

Clockwork-Muse commented 3 years ago

@John0King -

Is there any webapp that handle timezone manually

Not sure what you're referring to here. I'm not aware of any specific client or server frameworks that handles date/time the way I would consider to be correct by default. There's libraries that allow you to write the proper behavior, for sure, but I don't know of any integrated frameworks to handle it "just so". Part of the problem is that in many cases desired behavior is field (and/or view) specific, so you'd have to manage most of the conversion yourself regardless.

Part of your current problem is

and browse always use iso utc formate,

... you're not using any client-side libraries or types that can deal with timezones properly. If you're working with a blazor client-side app, you can do it all in C# (although working with NodaTime is far better than the base libraries). If you're working strictly with JS, you want something like moment.js.

John0King commented 3 years ago

@Clockwork-Muse the problem is JSON.stringfy() will always serialize Date to yyyy-MM-ddTHH:mm:ss.fffZ

Clockwork-Muse commented 3 years ago

.... and I'm telling you not to use that. I explicitly told you "look for a library that will enable you to work with timezones (and here's one that does)".