- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
DateTime is retrieved with DateTimeKind.Unspecified even if saved with DateTimeKind.Utc
Joined: 22-Jan-2007
I am on version 4.0 Final using adapter.
My issue is explained her: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=17566
The solution suggested by Otis can be implemented like this, I have not tested:
public class DateTimeAsUtcConverter : SystemTypeConverterBase<DateTime> {
protected override object PerformConvertFrom(object value) {
if (value is DateTime) {
var dateTime = (DateTime) value;
if (dateTime.Kind == DateTimeKind.Unspecified) {
return new DateTime(dateTime.Ticks, DateTimeKind.Utc);
}
return value;
}
throw new NotSupportedException(CreateInvalidConvertFromExceptionMessage(value));
}
}
This must be a common issue. Is there a smoother way to do this such as a global DateTimeKind project setting?
If not I need to introduce a type converter library just for this and apply that to all the datetime entity fields using the designer. I see that this can be done in the designer global settings.
If I need to do this, do you have any comments on the converter?
Joined: 28-Nov-2005
The TypeConverter is the easiest way to solve this. The only difference from what you proposed is that it's not a built-in typeConverter.
Yes, i think it could be considered, but to be honest I don't think it will be built-in as it's not that common and it's a matter of how the data is saved and how the specified provider interpret that.
Using your own TypeConverter and auto-assign it to your datetime fields is not hard at all. Please give it a try.
Joined: 22-Jan-2007
Thanks.
It would be much easier for your customers if you would supply this type converter. I need to create a new library assembly for this class. All developers need to copy this to their llblgen installation. We also need to update the deploy package with a new assembly.
If it is not that common, I think it is because people don't realize this is a broken window. If code by mistake calls ToUniversalTime on a retrieved DateTime field saved in db as UTC, it will be converted as if it was in local time.
Joined: 22-Jan-2007
It looks like it is not supported with a custom SystemTypeConverter, at least it is not showing in the designer.
using System;
using System.Collections;
using System.ComponentModel;
using SD.LLBLGen.Pro.ORMSupportClasses;
namespace Collabora.LLBLGen.TypeConverters {
[Description("Converts local time to utc for saving to db. Sets DateTimeKind.Utc on datetime values retrieved from db.")]
public class DateTimeAsUtcConverter : SystemTypeConverterBase<DateTime> {
protected override object PerformConvertFrom(object value) {
if (value is DateTime) {
var dateTime = (DateTime)value;
if (dateTime.Kind == DateTimeKind.Unspecified) {
return new DateTime(dateTime.Ticks, DateTimeKind.Utc);
}
return value;
}
throw new NotSupportedException(CreateInvalidConvertFromExceptionMessage(value));
}
protected override object PerformConvertTo(DateTime value, Type destinationType) {
if (destinationType != typeof(DateTime)) {
throw new NotSupportedException(CreateInvalidConvertToExceptionMessage(destinationType));
}
return value.Kind == DateTimeKind.Local ? value.ToUniversalTime() : value;
}
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) {
return DateTime.UtcNow;
}
}
}
Joined: 17-Aug-2003
The included DateTimeDateTimeUTCConverter (which is shipped with v4.0 and available in the runtime, so no external typeconverter needed) is that one sufficient?
That one is for a different kind of problem.
System type converters are type converters which are supported by the runtime. As yours is a custom type converter, you should create a custom type converter. This is a couple of lines of code, really.
See the below code as a guide:
using System;
using System.ComponentModel;
using System.Data;
namespace My.TypeConverters
{
[Description("Converter with as core type System.DateTime and which converts DateTime values to/from UTC")]
public class DateTimeToUtcConverter : TypeConverter
{
/// <summary>
/// Initializes a new instance of the <see cref="DateTimeToUtcConverter"/> class.
/// </summary>
public DateTimeToUtcConverter()
{
}
/// <summary>
/// Returns whether this converter can convert an object of the given type to the type of this converter (DateTime).
/// </summary>
/// <param name="context">Ignored</param>
/// <param name="sourceType">A <see cref="T:System.Type"/> that represents the type you want to convert from.</param>
/// <returns>
/// <see langword="true "/>if this converter can perform the conversion; otherwise, <see langword="false"/>.
/// </returns>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (sourcetype==typeof(DateTime));
}
/// <summary>
/// Returns whether this converter can convert the object to the specified type.
/// </summary>
/// <param name="context">Ignored</param>
/// <param name="destinationType">A <see cref="T:System.Type"/> that represents the type you want to convert to.</param>
/// <returns>
/// <see langword="true "/>if this converter can perform the conversion; otherwise, <see langword="false"/>.
/// </returns>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return (sourcetype==typeof(DateTime));
}
/// <summary>
/// Converts the given object to the type of this converter (DateTime).
/// </summary>
/// <param name="context">Ignored</param>
/// <param name="culture">Ignored</param>
/// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
/// <returns>
/// An <see cref="T:System.Object"/> that represents the converted value, which is of type DateTime.
/// </returns>
/// <exception cref="T:System.NotSupportedException">The conversion could not be performed.</exception>
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
// convert to value which is sent to the DB
}
/// <summary>
/// Converts the given value object to the specified type
/// </summary>
/// <param name="context">Ignored</param>
/// <param name="culture">Ignored</param>
/// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
/// <param name="destinationType">The <see cref="T:System.Type"/> to convert the <paramref name="value"/> parameter to.</param>
/// <returns>
/// An <see cref="T:System.Object"/> that represents the converted value.
/// </returns>
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
// convert value read from DB to value which is used as in-memory entity field value
}
/// <summary>
/// Creates an instance of the Type that this <see cref="T:System.ComponentModel.TypeConverter"/> is associated with (DateTime)
/// </summary>
/// <param name="context">ignored.</param>
/// <param name="propertyValues">ignored.</param>
/// <returns>
/// An <see cref="T:System.Object"/> of type DateTime.
/// </returns>
public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
return DateTime.Now;
}
}
}
Joined: 22-Jan-2007
Thanks Otis. I also found a good example on your web pages. I tested the below and it works for us.
For anyone else looking for a solution, this will do the trick. It also works for nullable DateTime.
using System;
using System.Collections;
using System.ComponentModel;
namespace Collabora.LLBLGen.TypeConverters {
[Description("Converts local time to utc for saving to db. Sets DateTimeKind.Utc on datetime values retrieved from db.")]
public class DateTimeAsUtcConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
return sourceType == typeof(DateTime);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
return destinationType == typeof(DateTime);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) {
if (value == null) {
return null;
}
if (!(value is DateTime)) {
throw new NotSupportedException("Conversion from a value of type '" + value.GetType() + "' to System.DateTime isn't supported");
}
var dateTime = (DateTime)value;
return dateTime.Kind == DateTimeKind.Unspecified ? new DateTime(dateTime.Ticks, DateTimeKind.Utc) : value;
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) {
if (value == null) {
return null;
}
if (!(value is DateTime)) {
throw new NotSupportedException("Conversion of a value of type '" + destinationType + "' isn't supported");
}
if (destinationType != typeof(DateTime)) {
throw new NotSupportedException("Conversion to a value of type '" + destinationType + "' isn't supported");
}
var dt = (DateTime) value;
return dt.Kind == DateTimeKind.Local ? dt.ToUniversalTime() : value;
}
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) {
return DateTime.UtcNow;
}
}
}