ServiceStack.Text in .NET not deserializing nested datetime - datetime

I am talking to a web service from Xamarin. It is sending dates down over the wire in JSON, in ISO-8601 format, like this:
"2016-05-13T16:02:47.480-07:00"
If I try to deserialize just a single date, it works fine. But when the DateTime is a member of class, it does not work. No error is given. Here is some example code:
using ServiceStack.Text;
class TestDate {
public DateTime testDate;
}
void Test() {
JsConfig.DateHandler = JsonDateHandler.ISO8601;
// this works just fine:
String dateString = "2016-05-13T16:02:47.480-07:00";
DateTime theDate = dateString.FromJson<DateTime> ();
// this does not deserialize
String testStr = "{\"testDate\":\"2016-05-13T16:02:51.923-07:00\"}";
TestDate tester = testStr.FromJson<TestDate> ();
}
Any ideas? I don't see any error, just get a new TestDate object without the date being set.

ServiceStack Text Serializer does serialize as expected:
JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601;
var dto = new TestDate { testDate = new DateTime(2016, 05, 13, 16, 02, 47, 480) };
var json = dto.ToJson();
json.Print(); //= {"testDate":"2016-05-13T16:02:47.4800000"}
and deserializes correctly:
var fromJson = json.FromJson<TestDate>();
Assert.That(fromJson.testDate, Is.EqualTo(dto.testDate)); //= true

Related

Why does JsonConvert change time of DateTimes with DateTimeKind.Unspecified when using DateTimeStyles.AssumeUniversal?

I'm building a web API and am having trouble with the JSON serialization of DateTimes. After doing some tests I can only conclude that the behavior of Newtonsoft.Json.JsonConvert and/or the Newtonsoft IsoDateTimeConverter is not what I expected.
Consider this:
// Arrange
var noonUtc = new DateTime(2016, 05, 12, 12, 0, 0, DateTimeKind.Utc);
var noon = new DateTime(2016, 05, 12, 12, 0, 0, DateTimeKind.Unspecified);
var settings = new JsonSerializerSettings();
settings.Converters.Add(new IsoDateTimeConverter
{
Culture = CultureInfo.InvariantCulture,
DateTimeStyles = DateTimeStyles.AdjustToUniversal
});
// Act
var utcJson = JsonConvert.SerializeObject(noonUtc, settings); // "\"2016-05-12T12:00:00Z\""
var json = JsonConvert.SerializeObject(noon, settings); // "\"2016-05-12T10:00:00Z\""
... // Assertions
Okay, so the time for the DateTime with DateTimeKind.Unspecified has been adjusted from 12 o'clock to 10 o'clock. I'm in Stockholm which is currently two hours ahead of UTC, so fair enough.
However, let's change the serializer settings to use DateTimeStyles.AssumeUniversal, like so:
settings.Converters.Add(new IsoDateTimeConverter
{
Culture = CultureInfo.InvariantCulture,
DateTimeStyles = DateTimeStyles.AssumeUniversal
});
This results in the exact same strings and thus also adjusts the DateTime with DateTimeKind.Unspecified by two hours! Should it not assume the date time was already UTC time and leave the time as it was? What am I missing here?
I don't think you're missing anything; this looks like it might be a bug in the IsoDateTimeConverter. Here is the relevant code from the source:
if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal
|| (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal)
{
dateTime = dateTime.ToUniversalTime();
}
As you can see, it only looks at whether _dateTimeStyles is set to AdjustToUniversal or AssumeUniversal before calling ToUniversalTime(); it never checks the date's Kind property.
And the documentation for DateTime.ToUniversalTime() says this:
Starting with the .NET Framework version 2.0, the value returned by the ToUniversalTime method is determined by the Kind property of the current DateTime object. The following table describes the possible results.
Kind | Results
----------- | ----------------------------------------------------------
Utc | No conversion is performed.
Local | The current DateTime object is converted to UTC.
Unspecified | The current DateTime object is assumed to be a local time,
| and the conversion is performed as if Kind were Local.
So yeah, it looks like the converter should definitely not be calling ToUniversalTime in this situation. You might want to report an issue.
For now, you can work around this issue by implementing a replacement converter (derived from the original) with the correct behavior. This is probably closer to what you would want:
public class CorrectedIsoDateTimeConverter : IsoDateTimeConverter
{
private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is DateTime)
{
DateTime dateTime = (DateTime)value;
if (dateTime.Kind == DateTimeKind.Unspecified)
{
if (DateTimeStyles.HasFlag(DateTimeStyles.AssumeUniversal))
{
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
}
else if (DateTimeStyles.HasFlag(DateTimeStyles.AssumeLocal))
{
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
}
}
if (DateTimeStyles.HasFlag(DateTimeStyles.AdjustToUniversal))
{
dateTime = dateTime.ToUniversalTime();
}
string format = string.IsNullOrEmpty(DateTimeFormat) ? DefaultDateTimeFormat : DateTimeFormat;
writer.WriteValue(dateTime.ToString(format, Culture));
}
else
{
base.WriteJson(writer, value, serializer);
}
}
}

Formatting Joda time with Ethiopic Chronology not displaying the name of the month

I'm using Joda time to convert gregorian date and time to Ethiopic chronology and I'm trying to format it with "MMMM dd, yyyy" format. I expect the Date to be displayed as "Meskerem 01, 2007" instead I get "1 01, 2007". Is this a bug in Joda time or am I doing something wrong?
DateTimeFormatter dtf = DateTimeFormat.forPattern("MMMM dd, yyyy")
Date time myDate = new DateTime(2014,9,11,0,0,0,0).withChronology(EthiopicChronology.getInstance()).toString(dtf)
Well, JodaTime has never been good in internationalization, sorry. But I will present a workaround.
DateTimePrinter printer =
new DateTimePrinter() {
#Override
public int estimatePrintedLength() {
return 8; // type the maximum chars you need for printing ethiopic months
}
#Override
public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
int index = LocalDate.now().indexOf(DateTimeFieldType.monthOfYear());
int month = partial.getValue(index);
print(buf, month);
}
#Override
public void printTo(Writer out, ReadablePartial partial, Locale locale)
throws IOException
{
StringBuffer sb = new StringBuffer();
printTo(sb, partial, locale);
out.write(sb.toString());
}
#Override
public void printTo(
StringBuffer buf,
long instant,
Chronology chrono,
int displayOffset,
DateTimeZone displayZone,
Locale locale
) {
LocalDate date = new LocalDate(instant, EthiopicChronology.getInstance());
print(buf, date.getMonthOfYear());
}
#Override
public void printTo(
Writer out,
long instant,
Chronology chrono,
int displayOffset,
DateTimeZone displayZone,
Locale locale
) throws IOException
{
StringBuffer sb = new StringBuffer();
printTo(sb, instant, chrono, displayOffset, displayZone, locale);
out.write(sb.toString());
}
private void print(StringBuffer buf, int month) {
switch (month) {
case 1 : // attention: ethiopic month index
buf.append("Meskerem");
break;
// case 2: etc.
default :
buf.append(month);
}
}
};
DateTimeFormatter dtf =
new DateTimeFormatterBuilder().append(printer).appendPattern(" dd, yyyy").toFormatter();
Chronology chronology = EthiopicChronology.getInstance();
DateTime ethiopic = new DateTime(2014, 9, 11, 0, 0, 0).withChronology(chronology);
String myDate = ethiopic.toString(dtf);
System.out.println(ethiopic); // 2007-01-01T00:00:00.000+02:00 (ethiopic month number and year and day-of-month!!!)
System.out.println(myDate); // Meskerem 01, 2007
Just to note: This code (as suggested by #Opal?) does not work for me:
Chronology chronology = EthiopicChronology.getInstance();
DateTimeFormatter dtf =
DateTimeFormat.forPattern("MMMM dd, yyyy").withChronology(chronology);
String myDate = new DateTime(2014, 9, 11, 0, 0, 0).toString(dtf2);
System.out.println(myDate); // 1 01, 2007
The reason is the sad fact that Joda-Time does not manage its own text resources for non-gregorian chronologies, compare also this SO-post. You can also use a specialized field implementation as suggested in that post. Here I have presented a solution using DateTimePrinter on which you have to add the missing month names you need.
Have a look at the example below:
#Grab(group='joda-time', module='joda-time', version='2.7')
import org.joda.time.format.DateTimeFormat
import org.joda.time.format.DateTimeFormatter
import org.joda.time.DateTime
import org.joda.time.chrono.EthiopicChronology
DateTimeFormatter dtf = DateTimeFormat.forPattern("MMMM dd, yyyy")
println new DateTime(2014,9,11,0,0,0,0).toString(dtf)
It prints the date correctly - full month name. Now have a look at the docs. It states that Chronology object returns a new formatter. Probably this is not a bug but the just returned formatter is used instead of the one defined.
UPDATE
It might be a bug, it doesn't work with BuddhistChronology as well:
#Grab(group='joda-time', module='joda-time', version='2.7')
import org.joda.time.format.DateTimeFormat
import org.joda.time.format.DateTimeFormatter
import org.joda.time.DateTime
import org.joda.time.chrono.*
DateTimeFormatter dtf = DateTimeFormat.forPattern("MMMM dd, yyyy")
println new DateTime(2014,9,11,0,0,0,0).withChronology(BuddhistChronology.getInstance()).toString(dtf)

NodaTime conversions (Part 2). How to?

Following my first post:
DateTime conversions using NodaTime on ASP.Net MVC 3 Razor website. How to?
I'm struggling to find an easy way to convert date/time between local and UTC (both ways), using NodaTime.
The current picture is:
I have the date/time saved as UTC in the database.
When displaying it to the user, I should consider the local time zone and convert it accordingly.
When the user provides date/time as a filter, I need to convert it back to UTC before sending to the SQL query.
What I have so far:
Extension to convert from UTC to local (this part is working fine):
public static DateTime UTCtoLocal(this DateTime dateTime)
{
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
var utcTimeZone = timeZoneProvider["UTC"];
var dateTimeFromDb = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond);
var zonedDbDateTime = utcTimeZone.AtLeniently(LocalDateTime.FromDateTime(dateTimeFromDb));
var usersTimezoneId = "Europe/London"; //just an example
var usersTimezone = timeZoneProvider[usersTimezoneId];
var usersZonedDateTime = zonedDbDateTime.WithZone(usersTimezone);
return usersZonedDateTime.ToDateTimeUnspecified();
}
Extension to convert from local back to UTC (this part is the problem):
public static DateTime LocaltoUTC(this DateTime dateTime)
{
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
var usersTimezoneId = "Europe/London";
var usersTimezone = timeZoneProvider[usersTimezoneId];
var dateTimeFromDb = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond);
var zonedDbDateTime = usersTimezone.AtLeniently(LocalDateTime.FromDateTime(dateTimeFromDb));
var utcTimezoneId = "UTC";
var utcTimezone = timeZoneProvider[utcTimezoneId];
var utcZonedDateTime = zonedDbDateTime.WithZone(utcTimezone);
return utcZonedDateTime.ToDateTimeUtc();
}
What am I doing wrong here?
Your UTCToLocal looks like it's doing more work than it needs to, to be honest.
It should just be:
// Note: the DateTime here must have a "Kind" of Utc.
public static DateTime UTCtoLocal(this DateTime dateTime)
{
Instant instant = Instant.FromDateTimeUtc(dateTime);
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
var usersTimezoneId = "Europe/London"; //just an example
var usersTimezone = timeZoneProvider[usersTimezoneId];
var usersZonedDateTime = instant.InZone(usersTimezone);
return usersZonedDateTime.ToDateTimeUnspecified();
}
Similarly your LocalToUTC should be along these lines:
// The DateTime here should have a "Kind" of Unspecified
public static DateTime LocaltoUTC(this DateTime dateTime)
{
LocalDateTime localDateTime = LocalDateTime.FromDateTime(dateTime);
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
var usersTimezoneId = "Europe/London";
var usersTimezone = timeZoneProvider[usersTimezoneId];
var zonedDbDateTime = usersTimezone.AtLeniently(localDateTime);
return zonedDbDateTime.ToDateTimeUtc();
}
You don't need to convert it to a different time zone: ZonedDateTime knows what the instant is, and ToDateTimeUtc will do the right thing. Note that there's no real dateTimeFromDb here, because if you're converting from an unspecified DateTime, that's presumably from the user...

How do I parse JSON dates with ActionScript?

I have dates in JSON which are generated from ASP.NET pages using the JSON.NET library. These dates look like this:
"LastModifiedDate": "\/Date(1301412877000-0400)\/"
How do I parse these with ActionScript from Flex 3 Professional? I'd like to have this in a native data format.
NOTE: What I'm not asking here is how do I parse a JSON feed with as3corelib. I have the JSON deserialized with that library but the dates are not decoded. That is why I need to know how to decode this date format.
You'll want to use as3corelib's JSON implementation to decode your string into Objects.
/**
* Converts 'Unix tick' format JSON Date to AS3 Date instances.
* Example json input: "{\"BarDate\":\"\/Date(1334672700000)\/\" }"
* Example json input: "{\"BarDate\":\"\/Date(1334672700000+0000)\/\" }"
*
* #param json date from JSON
* #return Date if conversion possible and worked else null.
*/
public static function parseJSONUnixTickDateToDate(json:String):Date
{
var date:Date = null;
if (json)
{
json = json.substring(json.indexOf("(") + 1, json.indexOf(")"));
var arr:Array = json.split("+");
date = new Date(Number(arr[0]));
}
return date;
}
In ASP I use this function to get a Json date
public static double JsonTime(DateTime dt)
{//Convert datetime to a Json Datetime
DateTime d1 = new DateTime(1970, 1, 1);
DateTime d2 = dt.ToUniversalTime();
TimeSpan ts = new TimeSpan(d2.Ticks - d1.Ticks);
return Math.Round( ts.TotalMilliseconds,0);
}
and then in JavaScript I use:
if (data.indexOf("\/Date(")==7)
return new Date(+data.replace(/\D/g, ''));
to convert back to a date if that helps
You will also have fun with single and double quotes but for the life of me I can not find a decoder that works server side with Json strings on Net Framework 3!
if you use String ="\u0027" in your code then the string is already converted and in the end I wrote a function that about does the job using a loop.
string Padding = "000";
for (int f = 1; f <= 256; f++)
{
string Hex = "\\u" + Padding.Substring(0, 4 - f.ToString().Length) + f;
string Dec = "&#" + Int32.Parse(f.ToString(), NumberStyles.HexNumber) + ";";
HTML = HTML.Replace(Hex, Dec);
}
HTML = System.Web.HttpUtility.HtmlDecode(HTML);
Bad I know but if you have a better answer then please let me know!

How to work with several fields in DateTime?

public DateTime EnterDeparture()
{
Console.WriteLine("Enter Year:");
return new DateTime().AddYears(int.Parse(Console.ReadLine()));
}
// This will return new DateTime(Without assigned Year) Cause DateTime is value type.
public DateTime EnterDeparture()
{
DateTime EnterDeparture = new DateTime();
Console.WriteLine("Enter Year:");
EnterDeparture.AddYears(int.Parse(Console.ReadLine()));
return EnterDeparture;
}
How to work with several fields in DateTime ? (Year,Days for example) Default constructors aren't suitable.
The DateTime.AddXXX methods return new DateTime instances, the existing struct does not change. Since each method returns a new instance, you can chain the method calls together. At the very least, you want to capture each return value into a variable. For example:
DateTime myDate = DateTime.Today;
DateTime tomorrowAtNoon = myDate.AddDays(1).AddHours(12);
You could have also written it like
DateTime tomorrow = myDate.AddDays(1);
DateTime tomorrowAtNoon = tomorrow.AddHours(12);
Follow?

Resources