web-dev-qa-db-ja.com

UTC DateTimeをWeb API HttpGetメソッドに渡すと、現地時間になります

UTC日付をクエリ文字列パラメーターとしてWeb APIメソッドに渡そうとしています。 URLは次のようになります

_/api/order?endDate=2014-04-01T00:00:00Z&zoneId=4
_

メソッドの署名は次のようになります

_[HttpGet]
public object Index(int zoneId, DateTime? endDate = null)
_

日付は_31/03/2014 8:00:00 PM_として入っていますが、_01/04/2014 12:00:00 AM_として入ってほしい

私の_JsonFormatter.SerializerSettings_は次のようになります

_new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    DateTimeZoneHandling = DateTimeZoneHandling.Utc,
    DateFormatHandling = DateFormatHandling.IsoDateFormat
};
_

編集#1:私はPOST _2014-04-01T00:00:00Z_がC#でUTC DateTime種類にシリアル化されることに気づきました。しかしendDate.Value.ToUniversalTime()変換するために、POST GETではなく、それがどのように動作するか奇妙に感じますが。

39
Ryan

_2014-04-01T00:00:00Z_を送信しているクエリ文字列パラメーター値はUTC時間です。そのため、同じことがローカルクロックに基づいて時間に変換され、ToUniversalTime()を呼び出すと、UTCに変換されます。

それで、質問は正確に何ですか?質問がクエリ本文として送信された場合にこれが発生する理由であるが、リクエストボディに投稿されたときではない場合、その質問に対する答えは、ASP.NET Web APIがURIパス、クエリ文字列などをmodel bindingおよびparameter bindingを使用する本文。後者の場合、メディアフォーマッタを使用します。 JSONを送信する場合、JSONメディアフォーマッタが使用され、JSON.NETに基づいています。

_DateTimeZoneHandling.Utc_を指定しているため、その設定が使用され、希望する日時の種類が取得されます。ところで、この設定を_DateTimeZoneHandling.Local_に変更すると、モデルバインディングと同じ動作が見られます。

35
Badri

変換を透過的にする場合は、カスタムTypeConverterを使用できます。

public sealed class UtcDateTimeConverter : DateTimeConverter
{
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return ((DateTime)base.ConvertFrom(context, culture, value)).ToUniversalTime();
    }
}

以下を使用して接続します。

TypeDescriptor.AddAttributes(typeof(DateTime), new TypeConverterAttribute(typeof(UtcDateTimeConverter)));

その後、クエリ文字列パラメーターはDateTimeKind.Utcとしてインスタンス化されます。

26
Sean Fausett

最終的には、パラメーターが入力されるときにToUniversalTime()メソッドを使用することになりました。

13
Ryan

したがって、アプリケーション全体で文字列から日付への変換をオーバーライドしたくない、また日付パラメータを受け取るすべてのメソッドを変更することを忘れたくない場合は、次のようにしますWeb APIプロジェクト。

最終的に、一般的な指示はここから来ます:

https://docs.Microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#model-binders

この場合の特別な手順は次のとおりです。

  1. 「WebApiConfig」クラスで、次を追加します。

        var provider = new SimpleModelBinderProvider(typeof(DateTime),new UtcDateTimeModelBinder());
        config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
    
  2. UtcDateTimeModelBinderという新しいクラスを作成します。

    public class UtcDateTimeModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext,
            ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(DateTime)) return false;
    
            var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (val == null)
            {
                return false;
            }
    
            var key = val.RawValue as string;
            if (key == null)
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName,
                    "Wrong value type");
                return false;
            }
    
            DateTime result;
            if (DateTime.TryParse(key, out result))
            {
                bindingContext.Model = result.ToUniversalTime();
                return true;
            }
    
            bindingContext.ModelState.AddModelError(bindingContext.ModelName,
                "Cannot convert value to Utc DateTime");
            return false;
        }
    }
    
1
Reginald Blue