更改 ASP.NET Core 中 DateTime 解析的默认格式

Posted

技术标签:

【中文标题】更改 ASP.NET Core 中 DateTime 解析的默认格式【英文标题】:Change default format for DateTime parsing in ASP.NET Core 【发布时间】:2017-01-13 20:17:46 【问题描述】:

我在 ASP.NET Core 控制器中得到一个日期,如下所示:

public class MyController:Controller
    public IActionResult Test(DateTime date) 

    

框架能够解析日期,但只能是英文格式。当我将 04.12.2017 作为日期参数传递时,我的意思是 2017 年 12 月 4 日。这将被解析为英文日期,因此我的日期对象的值是 2017 年 4 月 12 日。我尝试仅添加德语使用this 文章和this,但没有成功。

需要做什么才能让 ASP.NET Core 自动以正确的德语格式解析日期?

更新 我试图设置 RequestLocalizationOptions

services.Configure<RequestLocalizationOptions>(opts =>

    var supportedCultures = new[]
    
        new CultureInfo("de-DE"),
    ;

    opts.DefaultRequestCulture = new RequestCulture("de-DE");
    // Formatting numbers, dates, etc.
    opts.SupportedCultures = supportedCultures;
    // UI strings that we have localized.
    opts.SupportedUICultures = supportedCultures;
);

还是不行。我调用 example.com/Test?date=12.04.2017 并在我的调试器中得到了这个:

public IActionResult Test(DateTime date) 
    string dateString = date.ToString("d"); // 04.12.2016
    string currentDateString = DateTime.Now.ToString("d"); // 14.01.2016
    return Ok();

【问题讨论】:

你是如何传入要解析的日期,显示你做的URL格式。 @ScottChamberlain 正如我所说:使用 Test?date=12.04.2017 参数的调用导致 04.12.2017 作为 Test 函数内的日期对象。我期待未修改的日期,所以 12.04.2017 而不是 04.12.2017。 将其作为字符串传递,然后使用DateTime.ParseExact完全所需的方式对其进行解析。 为了清楚起见(以帮助讨论)04.12.2017 是 12 月 4 日 英文格式,或者更准确地说是英式英语。 4 月 12 日是美国英语格式。尽管英国会使用 / 而不是 .作为分隔符。 嗨@Lion 我将 Startup.cs 粘贴为 asp.net 核心项目类型并设置文化信息。当我查看控制器中的当前文化时,设置为 DE 。如果这有帮助,请试试这个。我试过它以 DE 格式显示日期。我没有尝试在控制器位 om 立即窗口中使用参数。 【参考方案1】:

遇到了同样的问题。虽然在请求正文中传递 DateTime 可以正常工作(因为 Json 转换器处理此人员),但在查询字符串中作为参数传递 DateTime 存在一些文化问题。

我不喜欢“更改所有请求文化”的方法,因为这可能会影响其他类型的解析,这是不可取的。

所以我的选择是使用 IModelBinder 覆盖默认的 DateTime 模型绑定:https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding

我做了什么:

1) 定义自定义绑定器(使用'out'参数的c# 7语法):

public class DateTimeModelBinder : IModelBinder

    public Task BindModelAsync(ModelBindingContext bindingContext)
    
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        // Try to fetch the value of the argument by name
        var modelName = bindingContext.ModelName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
        if (valueProviderResult == ValueProviderResult.None)
            return Task.CompletedTask;

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        var dateStr = valueProviderResult.FirstValue;
        // Here you define your custom parsing logic, i.e. using "de-DE" culture
        if (!DateTime.TryParse(dateStr, new CultureInfo("de-DE"), DateTimeStyles.None, out DateTime date))
        
            bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "DateTime should be in format 'dd.MM.yyyy HH:mm:ss'");
            return Task.CompletedTask;
        

        bindingContext.Result = ModelBindingResult.Success(date);
        return Task.CompletedTask;
    

2) 为您的活页夹定义提供者:

 public class DateTimeModelBinderProvider : IModelBinderProvider

    public IModelBinder GetBinder(ModelBinderProviderContext context)
    
        if (context == null)
        
            throw new ArgumentNullException(nameof(context));
        

        if (context.Metadata.ModelType == typeof(DateTime) || 
            context.Metadata.ModelType == typeof(DateTime?))
        
            return new DateTimeModelBinder();
        

        return null;
    

3) 最后,注册您的提供程序以供 ASP.NET Core 使用:

services.AddMvc(options =>

    options.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider());
);

现在您的 DateTime 将按预期进行解析。

【讨论】:

工作就像一个魅力。谢谢! Microsoft Documentation 建议不要使用自定义模型绑定器将字符串转换为其他类型作为最佳实践:通常不应该用于将字符串转换为自定义类型,TypeConverter 通常是更好的选择。。考虑使用TypeConverter 谢谢,也为我工作。请注意:上面的模型绑定器不能很好地处理 DateTime?空值。【参考方案2】:

我想格式化回复中的日期,我在 ConfigureServices 方法中执行了以下操作:

services.AddMvc()
.AddJsonOptions(options =>

    options.SerializerSettings.DateFormatString = "mm/dd/yy, dddd";
);

希望对您有所帮助。

【讨论】:

谢谢。如果您只想将它​​用于特殊情况,也可以在转换时指定它var deserialized = JsonConvert.DeserializeObject(myJsonstring, new JsonSerializerSettings DateFormatString = "dd/MM/yyyy" );【参考方案3】:

考虑为您的日期时间使用自定义 TypeConverter (Source):

using System;
using System.ComponentModel;
using System.Globalization;
using System.Drawing;

public class DeDateTimeConverter : TypeConverter 
   // Overrides the CanConvertFrom method of TypeConverter.
   // The ITypeDescriptorContext interface provides the context for the
   // conversion. Typically, this interface is used at design time to 
   // provide information about the design-time container.
   public override bool CanConvertFrom(ITypeDescriptorContext context, 
      Type sourceType) 

      if (sourceType == typeof(string)) 
         return true;
      
      return base.CanConvertFrom(context, sourceType);
   
   // Overrides the ConvertFrom method of TypeConverter.
   public override object ConvertFrom(ITypeDescriptorContext context, 
      CultureInfo culture, object value) 
      if (value is string) 
         if (DateTime.TryParse(((string)value), new CultureInfo("de-DE") /*or use culture*/, DateTimeStyles.None, out DateTime date))
             return date;
      
      return base.ConvertFrom(context, culture, value);
   

并在您的财产上使用TypeConverter 属性:

[TypeConverter(typeof(DeDateTimeConverter))]
public DateTime CustomDateTime  get; set; 

更新

根据我的经验,感谢this answer 和@zdeněk 的评论,TypeConverter 属性不起作用,您应该在Startup.cs 中注册 TypeConverter:

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

【讨论】:

最佳答案 IMO。【参考方案4】:

尝试在您的 web.config 中手动设置文化

<configuration>
   <system.web>    
      <globalization culture="de-DE" uiCulture="de-DE"/>
   </system.web>
</configuration>

编辑:因为我刚刚意识到这是核心,你可以在 StartUp.Configure 中这样做:

var cultureInfo = new CultureInfo("de-DE");
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;

【讨论】:

问题是关于 Asp.NET Core 我已经尝试设置 CultureInfo.DefaultThreadCurrentCultureCultureInfo.DefaultThreadCurrentUICulture 并且在测试操作开始时,它们被正确设置为 de-DE。但是ASP.NET核心不管,GET参数解析错误。【参考方案5】:

MVC 一直将InvariantCulture 用于路由数据和查询字符串(URL 中的参数)。其背后的原因是本地化应用程序中的 URL 必须是通用的。否则,一个 url 可以根据用户区域提供不同的数据。

您可以将查询和路由 ValueProviderFactories 替换为您自己的尊重当前文化(或在表单中使用 method="POST"

public class CustomValueProviderFactory : IValueProviderFactory

    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    
        if (context == null)
        
            throw new ArgumentNullException(nameof(context));
        

        var query = context.ActionContext.HttpContext.Request.Query;
        if (query != null && query.Count > 0)
        
            var valueProvider = new QueryStringValueProvider(
                BindingSource.Query,
                query,
                CultureInfo.CurrentCulture);

            context.ValueProviders.Add(valueProvider);
        

        return Task.CompletedTask;
    


services.AddMvc(opts => 
    // 2 - Index QueryStringValueProviderFactory
    opts.ValueProviderFactories[2] = new CustomValueProviderFactory(); 
)

附:这是合理的行为,但我不明白为什么文档没有涵盖这个非常重要的事情。

【讨论】:

【参考方案6】:
              using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Builder;
        using Microsoft.AspNetCore.Hosting;
        using Microsoft.Extensions.Configuration;
        using Microsoft.Extensions.DependencyInjection;
        using Microsoft.Extensions.Logging;
        using Microsoft.Extensions.Options;
        using System.Globalization;
        using Microsoft.AspNetCore.Localization;

        namespace coreweb
        
            public class Startup
            
                public Startup(IHostingEnvironment env)
                
                    var builder = new ConfigurationBuilder()
                        .SetBasePath(env.ContentRootPath)
                        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                        .AddJsonFile($"appsettings.env.EnvironmentName.json", optional: true)
                        .AddEnvironmentVariables();

                    if (env.IsDevelopment())
                    
                        // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
                        builder.AddApplicationInsightsSettings(developerMode: true);
                    
                    Configuration = builder.Build();
                

                public IConfigurationRoot Configuration  get; 

                // This method gets called by the runtime. Use this method to add services to the container.
                public void ConfigureServices(IServiceCollection services)
                
                    // ... previous configuration not shown
                    services.AddMvc();
                    services.Configure<RequestLocalizationOptions>(
                        opts =>
                        
                            var supportedCultures = new[]
                            

                        new CultureInfo("de-DE"),
                            ;

                            opts.DefaultRequestCulture = new RequestCulture("de-DE");
                    // Formatting numbers, dates, etc.
                    opts.SupportedCultures = supportedCultures;
                    // UI strings that we have localized.
                    opts.SupportedUICultures = supportedCultures;
                        );
                

                // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
                public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
                
                    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
                    loggerFactory.AddDebug();

                 //   app.UseApplicationInsightsRequestTelemetry();

                    if (env.IsDevelopment())
                    
                        app.UseDeveloperExceptionPage();
                        app.UseBrowserLink();
                    
                    else
                    
                        app.UseExceptionHandler("/Home/Error");
                    

                  //  app.UseApplicationInsightsExceptionTelemetry();

                    app.UseStaticFiles();

                    var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
                    app.UseRequestLocalization(options.Value);



                    app.UseMvc(routes =>
                    
                        routes.MapRoute(
                            name: "default",
                            template: "controller=Home/action=Index/id?");
                    );
                
            
        

【讨论】:

不工作。适用于DateTime.Now,但不适用于从 GET 参数解析的参数。请查看我在问题中的编辑,因为它太长了,无法发表评论。 哦。让我检查您编辑的答案。只是为了确认在这种情况下正确的结果应该是什么? 12.04.2017 作为输入应该被解析为 2014 年 4 月 12 日,而不是 2014 年 12 月 4 日,就像 ASP.NET 一样。【参考方案7】:

如果您不介意使用通用的 StatusCode 方法进行此调用,您可以执行以下操作:

internal IActionResult CreateResponse(int code, object content = null)
    
        Type t = content?.GetType();
        bool textContent = t == typeof(string) || t == typeof(bool);
        //
        JsonSerializerSettings dateFormatSettings = new JsonSerializerSettings
        

            DateFormatString = myDateFormat
        ;

        string bodyContent = content == null || string.IsNullOrWhiteSpace(content + "")
                    ? null
                    : textContent
                        ? content + ""
                        : JsonConvert.SerializeObject(content, dateFormatSettings);

        ObjectResult or = base.StatusCode(code, bodyContent);
        string mediaType = 
                    !textContent
                        ? "application/json"
                        : "text/plain";
        or.ContentTypes.Add(new MediaTypeHeaderValue(mediaType));
        return or;
    

您可以将其添加到基类中并调用如下:

return base.CreateResponse(StatusCodes.Status200OK, new  name = "My Name", age = 23);

如果您想创建自己的 Ok、BadRequest 等方法,这取决于您,但对我来说这是可行的,我希望它对其他人有所帮助。如果您的大多数请求都是 GET,您甚至可以默认 int code = 200。此代码假定您想要使用字符串、布尔值或自定义对象进行响应,但您可以通过检查 Type.GetTypeInfo().IsPrimitive 甚至对小数、字符串、DateTime、TimeSpan、DateTimeOffset 进行一些检查来轻松处理所有原语, 或指导。

【讨论】:

【参考方案8】:

我遇到了同样的问题,广告差点生气。我尝试了一切都没有成功。首先,我找到了解决部分问题的解决方法:

解决方法:

string data1 
string horainicio 
string horafim

var ageData = new AgendaData();
var user = await _userManager.GetUserAsync(User);
string usuario = user.Id;
int empresa = user.IdEmpresa;
int Idprospect = Convert.ToInt32(prospect);
int minutos = 0;           
var tipoAgenda = TipoAgenda.Contato;

var provider = CultureInfo.InvariantCulture;
provider = new CultureInfo("en-US");            
string formato = "dd/MM/yyyy HH:mm";

var dataInicio = DateTime.ParseExact(data1 + " " + horainicio, formato, provider);
var dataFim = DateTime.ParseExact(data1 + " " + horafim, formato, provider);           
var dataAlerta = dataInicio.AddMinutes(-minutos);

但是,这样我就必须将不变文化设置为我所有的日期时间。我在 startup.cs 上的配置中找到了设置我的文化的解决方案。

在 startup.cs 上设置文化

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, CRMContext context)
        
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
                app.UseBrowserLink();
            
            else
            
                app.UseExceptionHandler("/Home/Error");
            

            //Fixar Cultura para en-US
            RequestLocalizationOptions localizationOptions = new RequestLocalizationOptions
            
                SupportedCultures = new List<CultureInfo>  new CultureInfo("en-US") ,
                SupportedUICultures = new List<CultureInfo>  new CultureInfo("en-US") ,
                DefaultRequestCulture = new RequestCulture("en-US")
            ;

            app.UseRequestLocalization(localizationOptions);      
            app.UseStaticFiles();
            app.UseIdentity();

            // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715

            app.UseMvc(routes =>
            
                routes.MapRoute(
                    name: "default",
                    template: "controller=Home/action=Index/id?");
            );

            context.Database.EnsureCreated();
        

希望对您有所帮助。

【讨论】:

【参考方案9】:

最好将您的日期以 ISO 格式从前端发送到控制器:“yyyy-MM-dd”

https://www.w3schools.com/js/js_date_formats.asp

具有任何文化的任何服务器端都可以正确理解这种日期格式。

所以,我使用这样的发送方式:

const dateStart = new Date();
$.post("localhost:4200/start",  dateStart: dateStart.toISOString() ,
    function(data) 
        console.log("Started!");
    );

【讨论】:

这实际上有助于而不是实施上述给定的解决方案。谢谢【参考方案10】:
DateTime dt = DateTime.ParseExact(dateString, "ddMMyyyy", CultureInfo.InvariantCulture);
dt.ToString("yyyyMMdd");

根据https://***.com/a/3477821/2914174

【讨论】:

我知道这一点。但在这里没有帮助,因为 ASP.NET Core 本身将 GET 参数解析为 DateTime 对象。我不做解析。使用字符串而不是 DateTime 作为日期参数然后按照您的建议解析它是一种解决方法。但我想避免这种情况,因为 ASP.NET Core 在这里做得很好,只是格式错误。

以上是关于更改 ASP.NET Core 中 DateTime 解析的默认格式的主要内容,如果未能解决你的问题,请参考以下文章

更改 ASP.NET Core 中 DateTime 解析的默认格式

在 ASP.NET Core 中更改控制器路由

更改 ASP.NET Core Razor 页面中的默认登录页面?

如何在 ASP.NET Core 中更改 Swagger 的基本 url

更改 Angular 和 ASP.NET Core 项目中的默认身份路由

无法更改 Asp 身份表名称.....Asp .Net Core 2?