Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
.dark .cover.show {
background-color: rgb(79 58 120) !important;
}

.dark .markdown-section p.tip, .dark .markdown-section tr:nth-child(2n) {
background-color: #4c4c4c;
}
</style>
</head>
<body>
Expand Down
4 changes: 2 additions & 2 deletions src/datetime.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function adjustTime(inst, dur) {

// helper useful in turning the results of parsing into real dates
// by handling the zone options
function parseDataToDateTime(parsed, parsedZone, opts, format, text, specificOffset) {
export function parseDataToDateTime(parsed, parsedZone, opts, format, text, specificOffset) {
const { setZone, zone } = opts;
if ((parsed && Object.keys(parsed).length !== 0) || parsedZone) {
const interpretationZone = parsedZone || zone,
Expand Down Expand Up @@ -801,7 +801,7 @@ export default class DateTime {
const normalized = normalizeObject(obj, normalizeUnitWithLocalWeeks);
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, loc);

const tsNow = Settings.now(),
const tsNow = opts.overrideNow ?? Settings.now(),
offsetProvis = !isUndefined(opts.specificOffset)
? opts.specificOffset
: zoneToUse.offset(tsNow),
Expand Down
19 changes: 18 additions & 1 deletion src/duration.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ const orderedUnits = [

const reverseUnits = orderedUnits.slice(0).reverse();

// This is a map of which units to convert
// for toHuman due to missing support from Intl.
// if value is set, then key is what to convert to and value is what to convert
// if value is null, then key is a unit to be ignored
const humanizeUnitConversion = {
months: "quarters",
quarters: null,
};

// clone really means "create another instance just like this one, but with these changes"
function clone(dur, alts, clear = false) {
// deep merge for vals
Expand Down Expand Up @@ -509,7 +518,15 @@ export default class Duration {

const l = orderedUnits
.map((unit) => {
const val = this.values[unit];
const convertUnit = humanizeUnitConversion[unit];
if (convertUnit === null) return null;
let val = this.values[unit];
if (convertUnit) {
const val2 = this.values[convertUnit];
if (val2) {
val = (val ?? 0) + val2 * this.matrix[convertUnit][unit];
}
}
if (isUndefined(val) || (val === 0 && !showZeros)) {
return null;
}
Expand Down
33 changes: 33 additions & 0 deletions src/impl/regexParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,39 @@ export function parseISODate(s) {
);
}

// ISO Interval parsing

// Note: Do not optimize the outer non-capturing group, it is necessary, because the
// regex is combined with other regexes and contains |
const partialIsoIntervalEndDate = /(?:(?:(\d\d)-)?(\d\d)?|(?:W(\d\d)-)?(\d)|(\d{3}))/;
const isoIntervalEndDateTime = combineRegexes(partialIsoIntervalEndDate, isoTimeExtensionRegex);

const extractPartialIsoIntervalEndDate = simpleParse(
"month",
"day",
"weekNumber",
"weekDay",
"ordinal"
);

const extractISOIntervalPartialDateAndTime = combineExtractors(
extractPartialIsoIntervalEndDate,
extractISOTime,
extractISOOffset,
extractIANAZone
);

export function parseISOIntervalEnd(s) {
return parse(
s,
[isoIntervalEndDateTime, extractISOIntervalPartialDateAndTime],
[isoYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],
[isoWeekWithTimeExtensionRegex, extractISOWeekTimeAndOffset],
[isoOrdinalWithTimeExtensionRegex, extractISOOrdinalDateAndTime],
[isoTimeCombinedRegex, extractISOTimeAndOffset]
);
}

export function parseRFC2822Date(s) {
return parse(preprocessRFC2822(s), [rfc2822, extractRFC2822]);
}
Expand Down
25 changes: 22 additions & 3 deletions src/interval.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import DateTime, { friendlyDateTime } from "./datetime.js";
import DateTime, { friendlyDateTime, parseDataToDateTime } from "./datetime.js";
import Duration from "./duration.js";
import Settings from "./settings.js";
import { InvalidArgumentError, InvalidIntervalError } from "./errors.js";
import Invalid from "./impl/invalid.js";
import Formatter from "./impl/formatter.js";
import * as Formats from "./impl/formats.js";
import { parseISOIntervalEnd } from "./impl/regexParser.js";

const INVALID = "Invalid Interval";

Expand Down Expand Up @@ -134,24 +135,42 @@ export default class Interval {
* @return {Interval}
*/
static fromISO(text, opts) {
const { zone, setZone, ...restOpts } = opts || {};
const [s, e] = (text || "").split("/", 2);
if (s && e) {
let start, startIsValid;
try {
start = DateTime.fromISO(s, opts);
// we need to know the zone that was used in the string, so that we can
// default to it when parsing end, therefor use setZone: true
start = DateTime.fromISO(s, { ...restOpts, zone, setZone: true });
startIsValid = start.isValid;
} catch (e) {
startIsValid = false;
}

let end, endIsValid;
try {
end = DateTime.fromISO(e, opts);
const [vals, parsedZone] = parseISOIntervalEnd(e);
const endParseOpts = {
...restOpts,
overrideNow: startIsValid ? start.valueOf() : null,
zone: startIsValid ? start.zone : zone,
setZone: true,
};
end = parseDataToDateTime(vals, parsedZone, endParseOpts, "ISO 8601 Interval end", e);
endIsValid = end.isValid;
} catch (e) {
endIsValid = false;
}

// if we overrode the user's choice for setZone earlier, make up for it now
if (startIsValid && !setZone) {
start = start.setZone(zone);
}
if (endIsValid && !setZone) {
end = end.setZone(zone);
}

if (startIsValid && endIsValid) {
return Interval.fromDateTimes(start, end);
}
Expand Down
55 changes: 55 additions & 0 deletions test/duration/format.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,58 @@ test("Duration#toHuman works in differt languages", () => {
"1聽an, 2聽mois, 1聽semaine, 3聽jours, 4聽heures, 5 minutes, 6聽secondes, 7聽millisecondes"
);
});

test("Duration#toHuman handles quarters", () => {
expect(
Duration.fromObject({
years: 1,
quarters: 2,
hours: 2,
}).toHuman()
).toEqual("1 year, 6 months, 2 hours");
});

test("Duration#toHuman handles quarters and months together", () => {
expect(
Duration.fromObject({
years: 1,
months: 1,
quarters: 2,
hours: 2,
}).toHuman()
).toEqual("1 year, 7 months, 2 hours");
});

test("Duration#toHuman handles quarters and months with showZeros false", () => {
expect(
Duration.fromObject({
years: 1,
months: 1,
quarters: 0,
hours: 2,
}).toHuman({ showZeros: false })
).toEqual("1 year, 1 month, 2 hours");
expect(
Duration.fromObject({
years: 1,
months: 0,
quarters: 1,
hours: 2,
}).toHuman({ showZeros: false })
).toEqual("1 year, 3 months, 2 hours");
expect(
Duration.fromObject({
years: 1,
months: 0,
quarters: 0,
hours: 2,
}).toHuman({ showZeros: false })
).toEqual("1 year, 2 hours");
expect(
Duration.fromObject({
years: 1,
quarters: 0,
hours: 2,
}).toHuman({ showZeros: false })
).toEqual("1 year, 2 hours");
});
Loading