I just upgraded to the RTM version of the OData Library. I've noticed what appear to be inconsistencies in DateTime handling and would like to know if anyone can explain what I might be missing, or if there are in fact some problems. In addition to the RTM library I'm relying on the 3/30/2012 version of MS-ODATA.
MS-ODATA defines dateTimeUriLiteral in the following format (simplified for example):
YYYY-MM-DDTHH:MM:SS.NS where NS is defined as nanoSeconds=1*7DIGIT
And MS-ODATA defines VJsonDateTime as the dreaded /Date(...)/ format.
However using the Library in verbose JSON serialization we see the dateTimeUriLiteral format, not VJsonDateTime. Furthermore the deserialization accepts only the dateTimeUriLiteral format. This looks like a conflict between the spec and the implementation.
Also, dateTimeUriLiteral makes no allowance for time zone offset (such as would be the case in an ISO 8601 format). However we see that the Library emits a 'Z' terminating character (ISO 8601 for UTC) when the serialized datetime object is designated as DateTimeKind.Utc. This also looks like a conflict between the spec and the implementation.
Also, when we use the Library to deserialize a dateTimeUriLiteral that has the terminating 'Z' - the deserialized object is marked as DateTimeKind.Local. Whether or not there is a spec issue WRT support for the UTC designator, this does not look like it could be correct. Either the 'Z' should cause a deserialization failure or it should result in a time marked as UTC (not local).
Verbose JSON V3 uses the ISO DateTime format (the same one as XML uses). Verbose JSON V2 and V1 use the /Date(...)/ format. So it depends which version of the payload you're writing and reading.
The dateTimeUriLiteral is not the same thing as the Verbose JSON V3 date time format. The one in Verbose JSON V3 uses the Z (it's literaly the same thing as you get from XmlConvert.ToString(datetime, XmlDateTimeSerializationMode.RoundtripKind)).
As for reading the "Z" value. This seems to be a bug. The product team is looking into this in more detail. Possible workaround seems to be to either revert back to V2 format, or use DateTimeOffset values instead (which don't have this problem).
Related
I'm working on integrating with an API that (unfortunately) does not always append the timezone offset to their date. I know this isn't optimal but I can't change their behavior.
Example:
2022-06-08T16:07:13.96
Using the java generator and the java8 date library produces a runtime error while parsing the date:
java.time.format.DateTimeParseException: Text '2022-06-08T16:07:13.96' could not be parsed at index 22
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2052)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954)
at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:404)
at com.acme.openapi.JSON$OffsetDateTimeTypeAdapter.read(JSON.java:287)
From my limited understanding, I believe that ISO-8601 dates should be treated as local dates instead of offset dates if the zone offset is omitted. Im unsure if this is something that is supported in the java generator.
I ended up swapping the date library to java8-localdatetime but it gave GSON some problems:
Expected BEGIN_OBJECT but was String
I changed to a jackson based client and that cleared everything up.
I use this nasty wrapper everywhere, where I use a DateTime from the API, to "hack" it in the current timezone.
DateTime utcHack(DateTime dt) {
return DateTime.parse(dt.toLocal().toString() + "Z");
}
I am open for improvements!
I am storing XBRL JSON using elasticsearch.
This xBRL-JSON OIM spec describes the oim:period property:
Otherwise, an ISO 8601 time interval representing the {interval}
property, expressed in one of the following forms:
<start>/<end>
<start>/<duration>
<duration>/<end>
Where <start> and <end> are valid according to the xsd:dateTime datatype, and <duration> is valid according to xsd:duration.
Examples from arelle's plugin look like this:
2016-01-01T00:00:00/PT0S
2015-01-01T00:00:00/P1Y
I notice that arelle's plugin exclusively produces this format:
<start>/<duration>
My question
Is there a way to save at least the <start> part as a date type in elasticsearch?
Ideas I had:
elastichsearch only (my preference)
Use a custom date format which anticipates the /<duration> part, but ignores it
I haven't checked Joda yet; will it ignore characters in the date format if they aren't part of the special character? Like the "/" delimiter or the "P" which precedes any duration value (like PT0S and P1Y above)?
EDIT So the single-quote character escapes literals; this works yyyy'/P' will accept a value '2015/P'. However, the rest of the duration could be more dynamic
Re: dynamic; will Joda accept regex or wildcard character like "\d" or "+" qualifier so I can ignore all the possible variations following the P?
Use a character filter to strip out the /<duration> part before saving only <start>as datetime. But I don't know if character filters happen before saving as type: date. If they don't, the '/`part isn't stripped, and I wouldn't be passing valid date strings.
Don't use date type: Use a pattern tokenizer to split on /, and at least the two parts will be saved as separate tokens. Can't use date math, though.
Use a transformation; although it seems like this is deprecated. I read about using copy_to instead, but that seems to combine terms, and I want to break this term apart
Some sort of plugin? Maybe a plugin which will fully support this "interval" datatype described by the OIM spec... maybe a plugin which will store its separate parts...?
change my application (I prefer to use elasticsearch-only techniques if possible)
I could edit this plugin or produce my own plugin which uses exclusively <start> and <end> parts, and saves both into separate fields;
But this breaks the OIM spec, which says they should be combined in a single field
Moreover it can be awkward to express an "instant" fact (with no duration; the PT0S examples above); I guess I just use the same value for end property as start property... Not more awkward than a 0-length duration (PT0S) I guess.
Not a direct answer, but it's worth noting that the latest internal drafts of the xBRL-JSON specification have moved away from the the single-field representation. Although the "/" separated notation is an ISO standard, tool support for it appears to be extremely poor, and so the working group has chosen to switch to separate fields for start and end dates. I would expect Arelle support to follow suit in due course.
We and foolishly assumed a block of momentjs code would work in all browsers. Right now its correctly working in chrome, but all other browsers its not applying the UTC offset. How do I make this code work consistently in other browsers? Right now chrome is working, all others are not.
new moment(new Date(date)).fromNow();
//below shows an example of an exact date.
var now = new moment(new Date("2013-09-30T23:33:36.937")).fromNow();
In chrome you would see something like "now", all other browsers you would see "in 4 hours"
The way to get it to work:
var now = moment.utc("2013-10-01T13:15:30.937").fromNow();
Note that if you turn it into a "Date" and call moment.utc
var now = moment.utc(new Date("2013-10-01T13:15:30.937")).fromNow();
It will not work. It makes sense now that I think of it.
Original Answer
Try this:
var now = moment(date).fromNow();
But rutter is correct that you should specify either a Z or an offset like -07:00. From .net, you should make sure you use either a DateTime with .Kind set to Utc, or use a DateTimeOffset field.
Expanded Answer
You should understand that when you send a string of 2013-10-01T13:15:30.937, you are not sending any context with it. There is no way to know from that string alone whether the time is at UTC, or in your server's time zone, or in the browser's time zone.
If you pass it directly to moment("2013-10-01T13:15:30.937"), it is going to assume the context of the browser's local time zone.
As you discovered, you can tell moment explicitly that this time is in UTC by using the .utc function, such as moment.utc("2013-10-01T13:15:30.937").
While this will work, there are good reasons not to rely on that alone. For example, what if you ever use the same server API for another application, or perhaps for third-parties to consume? Unless you tell them separately that the timestamp is meant to represent UTC, then there is no way to know that.
These strings are in the ISO 8601 / RFC 3339 format. Part of that specification describes how to indicate that a timestamp is in UTC. You simply add a Z at the end. If you supply the Z then any consumer of this timestamp will know that the time should be interpreted as UTC. And sure enough, if you pass that into moment directly, such as moment("2013-10-01T13:15:30.937Z"), it will give the result you expected.
You said in comments that you were generating these values from ASP.Net Web API. Run your application in debug mode and set a breakpoint so you can examine the output of your controller. When you look at the specific DateTime property in question, you will see that it has its own .Kind property. It is probably set to DateTimeKind.Unspecified.
Since you said specifically that your application uses UTC, then these values should have DateTimeKind.Utc instead. Once that is set, then WebAPI will properly emit the Z at the end of the timestamp.
Somewhere in your server-side code, you should do something like this:
dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
You should do this as early as possible. For example, in your data access layer when you retrieve the value from the database. If that's not possible, then at least you should do it in your API Controller so the value gets emitted properly.
See also the MSDN reference for DateTimeKind and DateTime.SpecifyKind.
Also - the reason you were getting browser inconsistencies is because you were using the Date object's constructor instead of the parsing functions built in to moment. While moment will accept a Date, there are several known problems and inconsistencies with how browsers support parsing a Date from a string. Some of those inconsistencies are documented here.
Use this:
var lastLoginTime = moment(user.lastLoginTime).fromNow();
Is there a way to specify the formatting of DateTime data returned by a WCF data service?
This question seems to say that the default format, which looks like /Date(1339515455030)/, is the number of milliseconds since midnight Januery 1, 1970 UTC. I can figure out how to parse that if I have to but I'd rather specify a more friendly format.
The OData DateTime format (assuming you haven't already formatted it out to a string) is dependent on the wire format negotiated between client and server. The format above is from JSON Verbose and corresponds to a standard JavaScript Date object. The Atom format is different, and JSON Light is different still. Of special note is that we decided to move away from the traditional JavaScript Date format in favor of ISO 8601 for JSON Light. In any of these cases, if you're using a client such as the WCF DS client or data.js, the client will take care of deserializing the value properly.
Atom formatted date: <d:OrderDate
m:type="Edm.DateTime">1996-07-04T00:00:00</d:OrderDate>
JSON verbose formatted date: "OrderDate":"\/Date(836438400000)\/"
JSON light formatted date: "ShippedDate":"1996-07-16T00:00:00"
For current client libraries on a variety of platforms, see here: http://www.odata.org/libraries
With OData V3 the JSON Verbose format uses the ISO 8601 which seems to be more friendly to some consumers. Note though that it will only be used if the payload is V3. So you will need to update your server side to supper OData V3 (if it's WCF DS, then you want to use the WCF DS 5.0 release which can be found for example on NuGet http://blogs.msdn.com/b/astoriateam/archive/2012/05/18/wcf-data-services-5-0-1-released.aspx).
Once you have that you might need to force the server to use V3 in the response payload since by default it will use the lowest possible version for the response. This can be done by sending a MinDataServiceVersion: 3.0; header with the request from the client.
After this even JSON Verbose payloads should use the ISO date time format.
I believe that you should be able to feed the string value sent back into the datetime.parse(http://msdn.microsoft.com/en-us/library/1k1skd40.aspx) and shouldn't have to worry about the rest
Here is an example usage: http://www.dotnetperls.com/datetime-parse
I'm having a bit of an issue with the Date (java) / DateTime (.net) type when using the Ambient Data Framework in SDL Tridion 2009.
I'm setting a claim value in java like this:
Date myDate = rs.getDate("DATE_FIELD_IN_DB");
store.put(CLAIM_URI_DATE, myDate);
This is great and life is fantastic :)
In my web application (which is .net) I need to get this value from the claim store, when I try to do so, it fails:
My code is:
if (_store.Contains(new Uri("taf:claim:company:date"))) {
DateTime claimdata = _store.Get<DateTime>("taf:claim:company:date");
Response.Write(claimdata.ToString());
}
And the error message is:
[RuntimeException] Codemesh.JuggerNET.NTypeValue.Throw(Int64 inst)
+373 Codemesh.JuggerNET.JavaClass.ThrowTypedException(Int64 inst) +1365 Codemesh.JuggerNET.JavaMethod.CallInt(JavaProxy jpo) +233 Tridion.ContentDelivery.AmbientData.JuggerNetTypeHelper.FromProxyObject(Object
proxyObject) +738
Tridion.ContentDelivery.AmbientData.ClaimStore.Get(Uri claimUri) +109
I've also tried obtaining as an object, which i then cast to DateTime. I'm obviously making a really basic error here, any help would be greatly appreciated.
I have also read the SDL documentation here : http://sdllivecontent.sdl.com/LiveContent/content/en-US/SDL_Tridion_2011_SPONE/concept_26FDE76C277D43F893175E512EFDF09A which shows the java Data object is converted to the .net DateTime.
Thanks
John
UPDATE
As per the suggestions of Peter, i've check and ensured the correct jar files are in place.
It might be worth mentioning that the date is stored in a SQL database as a 'Date' type, a typical value doesn't include a timestamp, for example '2011-09-03'. Still this all functions great in Java and I can work with the value as a date. Still when getting the value in .Net the typed exception is thrown.
It should convert it automatically. Are you sure you are not using a different Date object than java.util.Date, though? There's quite a few of them out there...
If you really cannot get it to work, you can work around it by storing the date as a string (in ISO 8601 format) and then using DateTime.Parse on the .NET side.
But like I said: it normally works fine so there's probably something else going wrong.
Is your ADF working fine for other things in .NET? Maybe it's missing a setup step for the .NET side of things...
A bit of a long shot, but can you verify that the right JDBC driver is installed as mentioned in this question/answer?
Exception loading CustomMeta from Tridion Broker Service (2009 SP1)
The Date value, does it contain the time? Can you ensure that it does when you get the value from the store?