¿Hay alguna forma de evitar que el controlador mongodb c # convierta cualquier fecha pasada en una consulta de filtro a UTC pero en su lugar acepte la fecha como UTC?

Estoy usando .net core 2.1 con algunos controles telerik para mostrar una cuadrícula. Dentro del encabezado de la cuadrícula tengo un control de filtro para filtrar un rango de fechas.

enter image description here

Dentro del evento del cliente antes de filtrar, estoy capturando la fecha y convirtiéndola a utc:

function onGridFilter(e) {
        // check if it is a date field
        if (e.filter && e.field === "created"){
            convertDateToUTC(e.filter);
        }
    }

    function convertDateToUTC(filter) {
        var filters = filter.filters;
        for (var i = 0; i < filters.length; i++) {
            if (filters[i].field === "created") {
                var date = filters[i].value;
                var isoDate = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());                    
                filter.filters[i].value = isoDate;
            }
        }
    }

En la llamada a la API, estoy convirtiendo la fecha UTC a la hora local del servidor de esta manera (esta API es un microservicio de kubernetes y puede tener una zona horaria diferente dependiendo de dónde se implemente):

// Update the dates to server times for filtering, mongo will accept dates and convert to UTC based on the server location
        foreach (var f in this.Filters)
        {
            if (f.ConvertedValue.GetType() == typeof(DateTime))
            {
                DateTime dt = (DateTime)f.Value;
                f.Value = dt.ToLocalTime();
            }
        }

Usando el filtro telerik (objeto DataSourceRequest) y el controlador mongodb c # (linq a mongo), estoy creando una consulta mongo para filtrar los registros en la base de datos mongo.

 public DataSourceResult GetCollectionQuery(string organizationId, DataSourceRequest request)
    {
        IMongoCollection<Case> casesCollection = _db.GetCollection<Case>(_collection);
        IMongoCollection<Person> personCollection = _db.GetCollection<Person>(_personCollection);
        IQueryable<CaseListViewModel> query;

        // Setup full results query
        query = (from c in casesCollection.AsQueryable()
                 where c.OrganizationId == organizationId
                 join p in personCollection.AsQueryable() on c.ClientId equals p.Id into p
                 from person in p.DefaultIfEmpty()
                 select new CaseListViewModel()
                 {
                     Id = c.Id,
                     DisplayName = person != null ? person.LastName + ", " + person.FirstName : string.Empty,
                     OrganizationCaseId = c.OrganizationCaseId,
                     ServiceName = c.ServiceName,
                     ClientType = c.ClientType,
                     Addresses = c.ClientTypeValue == ClientTypeValue.Person ? person.Addresses != null ?
                                    person.Addresses.Where(o => !o.End.HasValue).Select(o => o.AddressLine1) : null : null,
                     Worker = string.Empty, //c.Assignments,
                     Created = c.Created,
                     Status = c.Status,
                     OrganizationGeography = person != null ? person.OrganizationGeography != null ? person.OrganizationGeography.Name : string.Empty : string.Empty
                 });

        // Filter/Sort/Page results
        return query.ToDataSourceResult(request);
    }

La razón por la que estoy convirtiendo el cliente a UTC, UTC al servidor y luego paso esa fecha a la consulta de mongo es porque el cliente y el servidor pueden estar en diferentes zonas horarias.

Parece que es mucho trabajo innecesario filtrar la fecha en la cuadrícula. Actualmente, esta solución funciona, sin embargo, estoy buscando una alternativa en el lado del controlador de mongodb c #. Quiero que la consulta mongodb lea todas las fechas como UTC en lugar de convertir las fechas recuperadas a UTC.

Sé que hay una manera de decirle a una propiedad que se está guardando como utc con BsonDateTimeOptions DateTimeKind:

[BsonElement(elementName: "created")]
[BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
public DateTime Created { get; set; }

¿Existe algo similar para las consultas?

Actualización: La solución fue especificar cuál era el DateTimeKind para la fecha que se filtraba.

foreach (var f in this.Filters)
        {
            if (f.ConvertedValue.GetType() == typeof(DateTime))
            {
                DateTime dt = (DateTime)f.Value;
                dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
                f.Value = dt;
            }
        }
1
Taran Beekhuis 16 oct. 2018 a las 17:01

2 respuestas

La mejor respuesta

Puede intentar usar DateTime.SpecifyKind para su fecha de esta manera:

query = (from c in casesCollection.AsQueryable()
...
select new CaseListViewModel()
{
    ...
    Created = DateTime.SpecifyKind(c.Created, DateTimeKind.Utc)
}

En este caso, el controlador mongodb debería interpretar la fecha como si ya estuviera en utc, por lo que no realizará la conversión.

1
NamiraJV 16 oct. 2018 a las 19:37

No estoy completamente seguro de lo que está preguntando, pero hay algunas cosas que veo en su código:

  • En su código del lado del cliente, tiene:

    var isoDate = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
    

    Este es un anti-patrón común y nunca debería hacerlo. Los parámetros del objeto Date esperan valores en la hora local y usted está pasando valores que están en UTC. Hacer esto es básicamente lo mismo que agregar la cantidad de compensación UTC de la zona horaria local. En otras palabras, no convierte el objeto a una zona horaria diferente, simplemente elige un momento diferente en el tiempo.

    Lo que probablemente quieras hacer en su lugar es date.toISOString(). Esto emitirá una cadena basada en UTC en formato ISO 8601 que es apropiado para enviar al servidor.

  • En su código del lado del servidor, tiene:

    f.Value = dt.ToLocalTime();
    

    Esto se convertirá a la zona horaria local del servidor . En la gran mayoría de los casos, debe evitar escribir código que dependa de la configuración de la zona horaria del servidor. En su lugar, mantenga la hora de entrada en UTC como se le proporcionó. Almacene usando UTC, consulte usando UTC y devuelva respuestas basadas en UTC. En el código del lado del cliente que recibe la respuesta, conviértalo a la hora local utilizando el objeto Date o una biblioteca.

Además, en tu GetCollectionQuery, no veo nada relacionado con la fecha o la hora, así que no estoy seguro de cómo se relaciona eso con tu pregunta.

1
Matt Johnson-Pint 16 oct. 2018 a las 16:26