Skip to content

Commit

Permalink
Merge pull request #18 from paulwarren-wk/timezone-fix
Browse files Browse the repository at this point in the history
Timezone fix
  • Loading branch information
rm-astro-wf committed Apr 19, 2019
2 parents b99b967 + 470bd1b commit bc3aa0a
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 91 deletions.
48 changes: 48 additions & 0 deletions iXBRLViewerPlugin/viewer/src/js/moment-jest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2019 Workiva Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import diff from 'jest-diff';
import moment from 'moment';

/* Helper to allow us to use toEqualDate() in jest tests */
expect.extend({
toEqualDate(unparsedReceived, unparsedExpected) {
const received = moment(unparsedReceived);
const expected = moment(unparsedExpected);

const receivedAsString = received.format();
const expectedAsString = expected.format();

const pass = received.isSame(expected);

const message = pass
? () =>
`${this.utils.matcherHint('.not.toBe')}\n\n` +
'Expected date to not be same date as:\n' +
` ${this.utils.printExpected(expectedAsString)}\n` +
'Received:\n' +
` ${this.utils.printReceived(receivedAsString)}`
: () => {
const diffString = diff(expectedAsString, receivedAsString, {
expand: this.expand,
});
return `${this.utils.matcherHint('.toBe')}\n\n` +
'Expected value to be (using ===):\n' +
` ${this.utils.printExpected(expectedAsString)}\n` +
'Received:\n' +
` ${this.utils.printReceived(receivedAsString)}${diffString ? `\n\nDifference:\n\n${diffString}` : ''}`;
};
return { actual: received, message, pass };
},
});
25 changes: 16 additions & 9 deletions iXBRLViewerPlugin/viewer/src/js/period.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { isodateToHuman } from "./util.js"
import { xbrlDateToMoment, momentToHuman } from "./util.js"
import moment from "moment";

export function Period(p) {
this._p = p;
Expand All @@ -26,30 +27,36 @@ Period.prototype.toString = function() {
}
else if (!this._p.includes('/')) {
/* instant */
s = isodateToHuman(this.to(), true);
s = momentToHuman(this.to(), true);
}
else {
s = isodateToHuman(this.from(), false) + " to " + isodateToHuman(this.to(), true);
s = momentToHuman(this.from(), false) + " to " + momentToHuman(this.to(), true);
}
return s;
}



/*
* Returns the date (instant) or end date (duration) of the period as a moment
* object
*/
Period.prototype.to = function() {
if (this._p.includes('/')) {
var r = this._p.split('/');
return new Date(r[1]);
return xbrlDateToMoment(r[1]);
}
else {
return new Date(this._p);
return xbrlDateToMoment(this._p);
}
}

/*
* Returns null (instant) or start date (duration) as a moment object.
*/
Period.prototype.from = function() {
if (this._p.includes('/')) {
var r = this._p.split('/');
return new Date(r[0]);
return xbrlDateToMoment(r[0]);
}
return null;
}
Expand All @@ -63,8 +70,8 @@ Period.prototype.isEquivalentDuration = function (op) {
if (this.from() == null || op.from() == null) {
return false;
}
var d1 = op.to() - op.from();
var d2 = this.to() - this.from();
var d1 = op.to().toDate() - op.from().toDate();
var d2 = this.to().toDate() - this.from().toDate();
if (Math.abs(d1-d2) < 0.1 * (d1+d2)) {
return true;
}
Expand Down
10 changes: 6 additions & 4 deletions iXBRLViewerPlugin/viewer/src/js/period.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,28 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import moment from 'moment';
import { Period } from "./period.js";
import "./moment-jest.js";

describe("Duration vs instant", () => {
test("Durations", () => {
var d = new Period("2018-01-01/2019-01-01");
expect(d.from()).toEqual(new Date("2018-01-01"));
expect(d.to()).toEqual(new Date("2019-01-01"));
expect(d.from()).toEqualDate(moment.utc("2018-01-01"));
expect(d.to()).toEqualDate(moment.utc("2019-01-01"));
expect(d.toString()).toEqual("1 Jan 2018 to 31 Dec 2018");
});
test("Instant without time", () => {
var i = new Period("2019-01-01");
expect(i.from()).toBeNull();
expect(i.to()).toEqual(new Date("2019-01-01"));
expect(i.to()).toEqualDate(moment.utc("2019-01-01"));
expect(i.toString()).toEqual("31 Dec 2018");
});

test("Instant with time", () => {
var i = new Period("2019-01-01T06:00:00");
expect(i.from()).toBeNull();
expect(i.to()).toEqual(new Date("2019-01-01T06:00:00"));
expect(i.to()).toEqualDate(moment.utc("2019-01-01T06:00:00"));
expect(i.toString()).toEqual("1 Jan 2019 06:00:00");
});
});
Expand Down
56 changes: 46 additions & 10 deletions iXBRLViewerPlugin/viewer/src/js/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,40 @@
// limitations under the License.

import dateFormat from "dateformat"
import moment from "moment";

export function isodateToHuman(d, adjust) {
if (d.getUTCHours() + d.getUTCMinutes() + d.getUTCSeconds() == 0) {
/*
* Takes a moment.js oject and converts it to a human readable date, or date
* and time if the time component is not midnight. Adjust specifies that a
* date (but not a date time) should be shown as the day before. This is to
* satisfy the human convention of describing durations using inclusive dates.
*
* i.e. 2018-01-01T00:00:00 to 2019-01-01T00:00:00 is described as
* "Jan 1 2018 to Dec 31 2018"
*
*/
export function momentToHuman(d, adjust) {
if (d.hours() + d.minutes() + d.seconds() == 0) {
if (adjust) {
d.setDate(d.getDate() - 1);
d = d.clone().subtract(1, 'day');
}
return dateFormat(d,"d mmm yyyy");
}
else {
return dateFormat(d,"d mmm yyyy HH:MM:ss");
return d.format('D MMM Y');
}
return d.format("D MMM Y HH:mm:ss");
}

/*
* Format a number with a thousands separator, and the specified number of
* decimal places.
*/
export function formatNumber(v, d) {
return Number(v).toFixed(d).replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, '$1,');
}

/* takes a string phrase and breaks it into separate phrases
* no bigger than 'maxwidth', breaks are made at complete words.*/
/*
* Takes a string phrase and breaks it into separate phrases no bigger than
* 'maxwidth'. breaks are made at complete words.
*/
export function wrapLabel(str, maxwidth){
var sections = [];
var words = str.split(" ");
Expand Down Expand Up @@ -73,10 +88,31 @@ export function wrapLabel(str, maxwidth){
}

});

return sections;
}

/*
* The JSON format supports datetimes being abbreviated to just xsd:dates.
* moment.js doesn't support timezoned dates, so fix them to midnight before
* passing to moment
*
* Note that the strings we're working with are not raw XBRL 2.1 dates, and
* do not apply different conventions for start and aned dates. A date
* with no time part always means T00:00:00.
*/
export function xbrlDateToMoment(dateString) {
/* If the string has something after the date part other than a time part,
* insert a time part of 'T00:00:00'
*
* i.e. 2010-01-01Z => 2010-01-01T00:00:00Z
*/
dateString = dateString.replace(
/^(\d{4,}-\d{2}-\d{2})(?!T|$)/,
function(match, $1) { return $1 + 'T00:00:00' }
);
return moment.utc(dateString);
}

export function escapeRegex(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
Loading

0 comments on commit bc3aa0a

Please sign in to comment.