Conflict of timezone for default note and other notes in onenote notebook - onenote

Default timezone for notes created in onenote is UTC but when I create new notebook in onenote and fetch default note using onenote api, we see GMT timezone for that note.

Here is the way I do:
Eg. For the createdTime attribute of any element
createdTime = getAsLocalDateTime(data, "createdTime");
/**
* Returns the attribute in a LocalDateTime object. Expects a string formatted as a {#link java.time.format.DateTimeFormatter#ISO_INSTANT}.
* Returns null if no such attribute or if the data is wrongly formatted.
*
* #param data
* #param attribut
*
* #return
*/
public static LocalDateTime getAsLocalDateTime(JsonObject data, String attribut) {
final JsonPrimitive primitive = data.getAsJsonPrimitive(attribut);
if (primitive == null)
return null;
final String tmsp = primitive.getAsString().substring(0, 19) + "Z"; // I cut OneNote timestamps that are too long in the nanoseconds
try {
// We read GMT, we've got to get back to local time zone
final ZonedDateTime d = ZonedDateTime.parse(tmsp, DATE_FORMAT);
final LocalDateTime ld = d.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
// System.out.println("# "+tmsp+" # "+d.toString()+" # "+ld.toString());
return ld;
} catch (DateTimeParseException e) {
logger.error("while retrieving date from \""+tmsp+"\"",e);
return null;
}
}

Related

MongoDB date format in strict mode

Using MongoDB java driver, applying toJson() method on Document will get a JSON representation of this document with JsonMode set to STRICT.
The following epoch format is used for dates: { "$date" : "dateAsMilliseconds" }
Using mongoexport, we get an ISO-8601 format.
Seen in official doc ( https://docs.mongodb.com/manual/reference/mongodb-extended-json/ ) :
In Strict mode, date is an ISO-8601 date format with a mandatory time zone field following the template YYYY-MM-DDTHH:mm:ss.mmm<+/-Offset>.
The MongoDB JSON parser currently does not support loading ISO-8601 strings representing dates prior to the Unix epoch. When formatting pre-epoch dates and dates past what your system’s time_t type can hold, the following format is used:
{ "$date" : { "$numberLong" : "dateAsMilliseconds" } }
I would appreciate if someone can explain me why there is no common format used between MongoDB java driver, mongoexport tool and official docs?
Thanks.
Obviously there is NO good reason for the Java driver to deviate from the official specification. The only exception are for those dates which cannot be expressed in the ISO8601 format (like B.C. dates...)
As a work around I have extended the JsonWriter class and provided two toJson static methods as an example of how to use it:
package whatever.package.you.like;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.bson.BSONException;
import org.bson.BsonContextType;
import org.bson.BsonDocument;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.EncoderContext;
import org.bson.conversions.Bson;
import org.bson.json.JsonMode;
import org.bson.json.JsonWriter;
import org.bson.json.JsonWriterSettings;
import com.mongodb.MongoClient;
/**
* A {#link JsonWriter} extension that conforms to the "strict" JSON format
* specified by MongoDB for data/time values.
*
* The {#link JsonWriter} class provided in the MongoDB Java driver (version
* 3.2.2) does not conform to official MongoDB specification for strict mode
* JSON (see https://docs.mongodb.com/manual/reference/mongodb-extended-json/).
* This is specifically a problem with the date/time values which get filled
* with a milliseconds value (i.e. {$date: 309249234098}) instead of the ISO8601
* date/time (i.e. {$date: "2016-07-14T08:44:23.234Z"}) value which the
* specification calls for. This extension of {#link JsonWriter} conforms to the
* MongoDb specification in this regard.
*/
public class ConformingJsonWriter extends JsonWriter {
private final JsonWriterSettings settings;
private final Writer writer;
private boolean writingIndentedDateTime = false;
/**
* Creates a new instance which uses {#code writer} to write JSON to.
*
* #param writer
* the writer to write JSON to.
*/
public ConformingJsonWriter(final Writer writer) {
this(writer, new JsonWriterSettings());
}
/**
* Creates a new instance which uses {#code writer} to write JSON to and uses
* the given settings.
*
* #param writer
* the writer to write JSON to.
* #param settings
* the settings to apply to this writer.
*/
public ConformingJsonWriter(final Writer writer,
final JsonWriterSettings settings) {
super(writer, settings);
this.writer = writer;
this.settings = settings;
setContext(new Context(null, BsonContextType.TOP_LEVEL, ""));
}
private void writeIndentation(int skip) throws IOException {
for (Context context = getContext()
.getParentContext(); context != null; context = context
.getParentContext()) {
if (skip-- <= 0) {
writer.write(settings.getIndentCharacters());
}
}
}
private static String millisToIso8601(long millis) throws IOException {
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat.format(new Date(millis));
}
#Override
protected void doWriteDateTime(final long value) {
if ((settings.getOutputMode() == JsonMode.STRICT)
&& (value >= -59014396800000L && value <= 253399536000000L)) {
try {
writeStartDocument();
if (settings.isIndent()) {
writingIndentedDateTime = true;
writer.write(settings.getNewLineCharacters());
writeIndentation(0);
} else {
writer.write(" ");
}
writer.write("\"$date\" : ");
writer.write("\"");
writer.write(millisToIso8601(value));
writer.write("\"");
writeEndDocument();
writingIndentedDateTime = false;
} catch (IOException e) {
throw new BSONException("Wrapping IOException", e);
}
} else {
super.doWriteDateTime(value);
}
}
#Override
protected void doWriteEndDocument() {
if (writingIndentedDateTime) {
try {
writer.write(settings.getNewLineCharacters());
writeIndentation(1);
writer.write("}");
if (getContext()
.getContextType() == BsonContextType.SCOPE_DOCUMENT) {
setContext(getContext().getParentContext());
writeEndDocument();
} else {
setContext(getContext().getParentContext());
}
} catch (IOException e) {
throw new BSONException("Wrapping IOException", e);
}
} else {
super.doWriteEndDocument();
}
}
/**
* Take a {#link Bson} instance and convert it to "strict" JSON
* representation with no indentation (read, "NOT pretty printed").
*
* #param bson
* The {#link Bson} instance to convert
* #return The JSON representation.
*/
public static String toJson(Bson bson) {
return toJson(bson, new JsonWriterSettings());
}
/**
* Take a {#link Bson} instance and convert it to JSON representation.
*
* #param bson
* The {#link Bson} instance to convert
* #param writerSettings
* {#link JsonWriterSettings} that specify details about how the
* JSON output should look.
* #return The JSON representation.
*/
public static String toJson(Bson bson,
final JsonWriterSettings writerSettings) {
BsonDocumentCodec encoder = new BsonDocumentCodec();
ConformingJsonWriter writer = new ConformingJsonWriter(new StringWriter(),
writerSettings);
encoder.encode(writer,
bson.toBsonDocument(BsonDocument.class,
MongoClient.getDefaultCodecRegistry()),
EncoderContext.builder().isEncodingCollectibleDocument(true)
.build());
return writer.getWriter().toString();
}
}

Datatransformer and json validation in symfony2 form

I'm building a Rest API and I receive a json_encoded string from the clients.
I want this string to be decoded before saving my entity, because it's going into a jsonb field in PostgreSQL.
The behavior I want is :
Validate that the string is valid json, if not, add a violation in the form via a custom validator
Automatically decode the string and set the json object in the entity property
I've tried two different strategies
In the entity setMetadata($value) method, if $value is a string, I decode it
I created a DataTransformer that json_decode the value received in the form
But both these solutions don't work because the custom validator I created is called after, and it calls directly $lesson->getMetadata(). Since the value has already been decoded (either in the setMetadata() method or in the DataTransformer, the validator receive either a json object or null. So I can't add a violation to the form, since I have no way to know if the value received was actually null, or if the string was malformed.
Here is the lesson entity:
class Lesson extends BaseContent
{
[…]
/**
* #var jsonb
*
* #ORM\Column(name="metadata", type="jsonb", nullable=true)
* #KreactiveAssert\Json
*/
private $metadata;
[…]
}
Here is the custom validator:
class JsonValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if ($value && !json_decode($value)) {
$this->context->addViolation($constraint->message, array('%string%' => $value));
}
}
}
And here is the DataTransformer:
class StringToJsonTransformer implements DataTransformerInterface
{
/**
* Transform a json object to a string
* #param Json|null $json
* #return String
*/
public function transform($json)
{
if (null === $json) {
return "";
}
return json_encode($json);
}
/**
* Transform a string to a json object
* #param String $string
* #return Object
*/
public function reverseTransform($string)
{
if (!$string) {
return null;
}
throw new TransformationFailedException('error transforming');
return json_decode($string);
}
}
Is there any way I can validate the input data in the form, and then set the metadata as a json object?
I've found this (I don't know how come I didn't find it earlier):
Combine constraints and data transformers
I'm going to make an ugly workaround as suggested, even though I don't like that solution.
<?php
class StringToJsonTransformer implements DataTransformerInterface
{
/**
* Transform a string to a json object
* #param String $string
* #return Object
*/
public function reverseTransform($string)
{
if (!$string) {
return null;
}
/*
* UGLY WORKAROUND
* we return -1 if the json_decode fail
* so the validator can add a violation in the form telling
* the json string was not valid
* If we don't do this, the validator will receive either
* null or a json object. In case of null, there is no way to
* tell if the client sent null, or if the decoding failed
*/
$value = json_decode($string);
return $value ? $value : -1;
}
}
I'm still not sure if I'm going to return -1 or something else. In the custom validator, I get an error if I try to compare a jsonObject with -1 (which is normal).

Google Drive Android API onChildrenRetrievedCallback

I'm building an app and trying to view a PDF stored in a shared Google Drive folder in-app. I've already connected to the Drive and I have a GoogleApiClient already set up. Right now I'm querying the database and trying to handle the results, but I'm having trouble with this code:
Query query = new Query.Builder().addFilter(Filters.and(
Filters.eq(SearchableField.TITLE, "sample_pdf.pdf"),
Filters.eq(SearchableField.MIME_TYPE, "application/vnd.google-apps.file")))
.build();
Drive.DriveApi.query(googleApiClient, query)
.setResultCallback(new OnChildrenRetrievedCallback() {
#Override
public void onChildrenRetrieved(MetadataBufferResult result) {
// Iterate over the matching Metadata instances in mdResultSet
}
});
Android Studio is not able to resolve the class OnChildrenRetrievedCallback. I have imported the com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks package but I'm not sure what else to do here. Any help?
Try this:
/********************************************************************
* find file/folder in GOODrive
* #param prnId parent ID (optional), null searches full drive, "root" searches Drive root
* this is a String representation of DriveId (DriveId.encodeToString()
* #param titl file/folder name (optional)
* #param mime file/folder mime type (optional)
* #return void (arraylist of found objects in callback)
*/
static void find(String prnId, String titl, String mime) {
// add query conditions, build query
ArrayList<Filter> fltrs = new ArrayList<>();
if (prnId != null){
fltrs.add(Filters.in(SearchableField.PARENTS,
prnId.equalsIgnoreCase("root") ?
Drive.DriveApi.getRootFolder(mGAC).getDriveId() : DriveId.decodeFromString(prnId)));
}
if (titl != null) fltrs.add(Filters.eq(SearchableField.TITLE, titl));
if (mime != null) fltrs.add(Filters.eq(SearchableField.MIME_TYPE, mime));
Query qry = new Query.Builder().addFilter(Filters.and(fltrs)).build();
// fire the query
Drive.DriveApi.query(mGAC, qry).setResultCallback(new ResultCallback<MetadataBufferResult>() {
#Override
public void onResult(MetadataBufferResult rslt) {
if (rslt.getStatus().isSuccess()) {
MetadataBuffer mdb = null;
try {
mdb = rslt.getMetadataBuffer();
for (Metadata md : mdb) {
if (md == null || !md.isDataValid() || md.isTrashed()) continue;
// collect files
DriveId driveId = md.getDriveId();
String dId = driveId.encodeToString();
String mime = md.getMimeType();
//.....
}
} finally { if (mdb != null) mdb.close(); } // don't know if necessary
}
}
});
}
Your mimeType filter doesn't look right, either. I suggest skipping the mimeType filter, search by name(title) only and double-check the mime type you're getting (the md.getMimeType() in the code above).
BEWARE! File name/title IS NOT UNIQUE IDENTIFIER in the GooDrive universe
ONE MORE BEWARE! You will not see the file if t was not created by your Android App (see GDAA supported SCOPES).
Good Luck

How to print JSON a date in correct format returned from a REST API?

I have written a REST API which gets data from a database. I have a column in my table which stores a date. I am using timestamp format to store the date.
My issue is when I am fetching the data, I'm not able to display the date in the proper format. I am getting 1420655400000 instead of 2015-01-08 00:00:00.
Here is my controller:
#RequestMapping(value="/getEstimation" , method=RequestMethod.GET)
public List<Estimation1> getEstimation(ModelAndView model) throws IOException{
List<Estimation1> estdetail;
estdetail= estimation.getEstimationbyId(5);
return estdetail;
}
Implementation of getEstimationId(double):
#Override
public List<Estimation1> getEstimationbyId(double id) {
// TODO Auto-generated method stub
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "SELECT * FROM estimation where est_id=" +id;
List<Estimation1> estimdetails= jdbcTemplate.query(sql, new RowMapper<Estimation1>()
{
#Override
public Estimation1 mapRow(ResultSet rs, int rowNum) throws SQLException
{
Estimation1 aContact = new Estimation1();
aContact.setDate(rs.getTimestamp("est_date"));
aContact.setEst_contactperson(rs.getString("est_contact_person"));
aContact.setEst_customer(rs.getString("est_customer"));
aContact.setEst_revision(rs.getInt("est_revision"));
aContact.setEst_prjt(rs.getString("est_project"));
aContact.setEst_status(rs.getString("est_status"));
return aContact;
}
});
return estimdetails;
}
Here is the data which I am getting from the database after execution:
[{"date":1420655400000,"est_prjt":"project1","est_revision":0,"est_customer":null,"est_contactperson":"robert","est_status":null,"est_id":0.0,"ec":null}]**
What changes should I make to print the date in the proper format?
You need to give a hint to the Jackson's object mapper of the format in which you want your dates to be deserialized. Following should work out for you
#JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
private Timestamp date;

Deserializing from JSON back to joda DateTime in Play 2.0

I can't figure out the magic words to allow posting JSON for a DateTime field in my app. When queried, DateTimes are returned as microseconds since the epoch. When I try to post in that format though ({"started":"1341006642000","task":{"id":1}}), I get "Invalid value: started".
I also tried adding #play.data.format.Formats.DateTime(pattern="yyyy-MM-dd HH:mm:ss") to the started field and posting {"started":"2012-07-02 09:24:45","task":{"id":1}} which had the same result.
The controller method is:
#BodyParser.Of(play.mvc.BodyParser.Json.class)
public static Result create(Long task_id) {
Form<Run> runForm = form(Run.class).bindFromRequest();
for (String key : runForm.data().keySet()) {
System.err.println(key + " => " + runForm.apply(key).value() + "\n");
}
if (runForm.hasErrors())
return badRequest(runForm.errorsAsJson());
Run run = runForm.get();
run.task = Task.find.byId(task_id);
run.save();
ObjectNode result = Json.newObject();
result.put("id", run.id);
return ok(result);
}
I can also see from the output that the values are being received correctly. Anyone know how to make this work?
After reading the "Register a custom DataBinder" section of the Handling form submission page along with the Application global settings page and comparing with this question I came up with the following solution:
I created a custom annotation with an optional format attribute:
package models;
import java.lang.annotation.*;
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#play.data.Form.Display(name = "format.joda.datetime", attributes = { "format" })
public #interface JodaDateTime {
String format() default "";
}
and registered a custom formatter from onStart:
import java.text.ParseException;
import java.util.Locale;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import play.*;
import play.data.format.Formatters;
public class Global extends GlobalSettings {
#Override
public void onStart(Application app) {
Formatters.register(DateTime.class, new Formatters.AnnotationFormatter<models.JodaDateTime,DateTime>() {
#Override
public DateTime parse(models.JodaDateTime annotation, String input, Locale locale) throws ParseException {
if (input == null || input.trim().isEmpty())
return null;
if (annotation.format().isEmpty())
return new DateTime(Long.parseLong(input));
else
return DateTimeFormat.forPattern(annotation.format()).withLocale(locale).parseDateTime(input);
}
#Override
public String print(models.JodaDateTime annotation, DateTime time, Locale locale) {
if (time == null)
return null;
if (annotation.format().isEmpty())
return time.getMillis() + "";
else
return time.toString(annotation.format(), locale);
}
});
}
}
You can specify a format if you want, or it will use milliseconds since the epoch by default. I was hoping there would be a simpler way since Joda is included with the Play distribution, but this got things working.
Note: you'll need to restart your Play app as it doesn't seem to detect changes to the Global class.