Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add date normalization in constructors of CalendarDate classes #7436

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
96 changes: 69 additions & 27 deletions packages/@internationalized/date/src/CalendarDate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/

import {add, addTime, addZoned, constrain, constrainTime, cycleDate, cycleTime, cycleZoned, set, setTime, setZoned, subtract, subtractTime, subtractZoned} from './manipulation';
import {add, addTime, addZoned, constrain, constrainTime, cycleDate, cycleTime, cycleZoned, normalize, set, setTime, setZoned, subtract, subtractTime, subtractZoned} from './manipulation';
import {AnyCalendarDate, AnyTime, Calendar, CycleOptions, CycleTimeOptions, DateDuration, DateField, DateFields, DateTimeDuration, Disambiguation, TimeDuration, TimeField, TimeFields} from './types';
import {compareDate, compareTime} from './queries';
import {dateTimeToString, dateToString, timeToString, zonedDateTimeToString} from './string';
Expand Down Expand Up @@ -65,12 +65,22 @@ export class CalendarDate {
constructor(calendar: Calendar, era: string, year: number, month: number, day: number);
constructor(...args: any[]) {
let [calendar, era, year, month, day] = shiftArgs(args);
this.calendar = calendar;
this.era = era;
this.year = year;
this.month = month;
this.day = day;

const normalized = normalize({
calendar,
era,
year,
month,
day,
hour: 0,
minute: 0,
second: 0,
millisecond: 0
});
this.calendar = normalized.calendar;
this.era = normalized.era;
this.year = normalized.year;
this.month = normalized.month;
this.day = normalized.day;
constrain(this);
}

Expand Down Expand Up @@ -222,16 +232,32 @@ export class CalendarDateTime {
constructor(calendar: Calendar, era: string, year: number, month: number, day: number, hour?: number, minute?: number, second?: number, millisecond?: number);
constructor(...args: any[]) {
let [calendar, era, year, month, day] = shiftArgs(args);
this.calendar = calendar;
this.era = era;
this.year = year;
this.month = month;
this.day = day;
this.hour = args.shift() || 0;
this.minute = args.shift() || 0;
this.second = args.shift() || 0;
this.millisecond = args.shift() || 0;

let hour = args.shift() || 0;
let minute = args.shift() || 0;
let second = args.shift() || 0;
let millisecond = args.shift() || 0;

const normalized = normalize({
calendar,
era,
year,
month,
day,
hour,
minute,
second,
millisecond
});

this.calendar = normalized.calendar;
this.era = normalized.era;
this.year = normalized.year;
this.month = normalized.month;
this.day = normalized.day;
this.hour = normalized.hour;
this.minute = normalized.minute;
this.second = normalized.second;
this.millisecond = normalized.millisecond;
constrain(this);
}

Expand Down Expand Up @@ -336,18 +362,34 @@ export class ZonedDateTime {
let [calendar, era, year, month, day] = shiftArgs(args);
let timeZone = args.shift();
let offset = args.shift();
this.calendar = calendar;
this.era = era;
this.year = year;
this.month = month;
this.day = day;
let hour = args.shift() || 0;
let minute = args.shift() || 0;
let second = args.shift() || 0;
let millisecond = args.shift() || 0;

const normalized = normalize({
calendar,
era,
year,
month,
day,
hour,
minute,
second,
millisecond
});

this.calendar = normalized.calendar;
this.era = normalized.era;
this.year = normalized.year;
this.month = normalized.month;
this.day = normalized.day;
this.timeZone = timeZone;
this.offset = offset;
this.hour = args.shift() || 0;
this.minute = args.shift() || 0;
this.second = args.shift() || 0;
this.millisecond = args.shift() || 0;

this.hour = normalized.hour;
this.minute = normalized.minute;
this.second = normalized.second;
this.millisecond = normalized.millisecond;
constrain(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ const BUDDHIST_ERA_START = -543;
export class BuddhistCalendar extends GregorianCalendar {
identifier = 'buddhist';

fromJulianDay(jd: number): CalendarDate {
fromJulianDay(jd: number): AnyCalendarDate {
let gregorianDate = super.fromJulianDay(jd);
let year = getExtendedYear(gregorianDate.era, gregorianDate.year);
return new CalendarDate(
this,
year - BUDDHIST_ERA_START,
gregorianDate.month,
gregorianDate.day
);

return {
calendar: this,
era: 'BE', // Buddhist Era
year: year - BUDDHIST_ERA_START,
month: gregorianDate.month,
day: gregorianDate.day
};
}

toJulianDay(date: AnyCalendarDate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
// Original licensing can be found in the NOTICE file in the root directory of this source tree.

import {AnyCalendarDate, Calendar} from '../types';
import {CalendarDate} from '../CalendarDate';
import {mod, Mutable} from '../utils';

const EPOCH = 1721426; // 001/01/03 Julian C.E.
Expand Down Expand Up @@ -70,7 +69,7 @@ const daysInMonth = {
export class GregorianCalendar implements Calendar {
identifier = 'gregory';

fromJulianDay(jd: number): CalendarDate {
fromJulianDay(jd: number): AnyCalendarDate {
let jd0 = jd;
let depoch = jd0 - EPOCH;
let quadricent = Math.floor(depoch / 146097);
Expand All @@ -93,7 +92,13 @@ export class GregorianCalendar implements Calendar {
let month = Math.floor(((yearDay + leapAdj) * 12 + 373) / 367);
let day = jd0 - gregorianToJulianDay(era, year, month, 1) + 1;

return new CalendarDate(era, year, month, day);
return {
calendar: this,
era,
year,
month,
day
};
}

toJulianDay(date: AnyCalendarDate): number {
Expand Down
14 changes: 10 additions & 4 deletions packages/@internationalized/date/src/calendars/IndianCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
// Original licensing can be found in the NOTICE file in the root directory of this source tree.

import {AnyCalendarDate} from '../types';
import {CalendarDate} from '../CalendarDate';
import {fromExtendedYear, GregorianCalendar, gregorianToJulianDay, isLeapYear} from './GregorianCalendar';

// Starts in 78 AD,
Expand All @@ -31,7 +30,7 @@ const INDIAN_YEAR_START = 80;
export class IndianCalendar extends GregorianCalendar {
identifier = 'indian';

fromJulianDay(jd: number): CalendarDate {
fromJulianDay(jd: number): AnyCalendarDate {
// Gregorian date for Julian day
let date = super.fromJulianDay(jd);

Expand Down Expand Up @@ -71,8 +70,15 @@ export class IndianCalendar extends GregorianCalendar {
indianDay = (mDay % 30) + 1;
}
}

return new CalendarDate(this, indianYear, indianMonth, indianDay);
const eras = this.getEras();
const era = eras[eras.length - 1];
return {
calendar: this,
era: era,
year: indianYear,
month: indianMonth,
day: indianDay
};
}

toJulianDay(date: AnyCalendarDate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ function toGregorian(date: AnyCalendarDate) {
throw new Error('Unknown era: ' + date.era);
}

return new CalendarDate(
date.year + eraAddend,
date.month,
date.day
);
return {
era: 'AD',
year: date.year + eraAddend,
month: date.month,
day: date.day,
calendar: new GregorianCalendar()
};
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
// Original licensing can be found in the NOTICE file in the root directory of this source tree.

import {AnyCalendarDate, Calendar} from '../types';
import {CalendarDate} from '../CalendarDate';
import {mod} from '../utils';

const PERSIAN_EPOCH = 1948320;
Expand Down Expand Up @@ -44,7 +43,7 @@ const MONTH_START = [
export class PersianCalendar implements Calendar {
identifier = 'persian';

fromJulianDay(jd: number): CalendarDate {
fromJulianDay(jd: number): AnyCalendarDate {
let daysSinceEpoch = jd - PERSIAN_EPOCH;
let year = 1 + Math.floor((33 * daysSinceEpoch + 3) / 12053);
let farvardin1 = 365 * (year - 1) + Math.floor((8 * year + 21) / 33);
Expand All @@ -53,7 +52,14 @@ export class PersianCalendar implements Calendar {
? Math.floor(dayOfYear / 31)
: Math.floor((dayOfYear - 6) / 30);
let day = dayOfYear - MONTH_START[month] + 1;
return new CalendarDate(this, year, month + 1, day);

return {
calendar: this,
era: 'AP', // Anno Persico/Persian Era
year,
month: month + 1,
day
};
}

toJulianDay(date: AnyCalendarDate): number {
Expand Down
13 changes: 7 additions & 6 deletions packages/@internationalized/date/src/calendars/TaiwanCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,11 @@ export class TaiwanCalendar extends GregorianCalendar {

function toGregorian(date: AnyCalendarDate) {
let [era, year] = fromExtendedYear(gregorianYear(date));
return new CalendarDate(
era,
year,
date.month,
date.day
);
return {
era: era,
year: year,
month: date.month,
day: date.day,
calendar: new GregorianCalendar()
};
}
6 changes: 3 additions & 3 deletions packages/@internationalized/date/src/conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
// Portions of the code in this file are based on code from the TC39 Temporal proposal.
// Original licensing can be found in the NOTICE file in the root directory of this source tree.

import {AnyCalendarDate, AnyDateTime, AnyTime, Calendar, DateFields, Disambiguation, TimeFields} from './types';
import {AnyCalendarDate, AnyDateTime, AnyTime, Calendar, Copyable, DateFields, Disambiguation, TimeFields} from './types';
import {CalendarDate, CalendarDateTime, Time, ZonedDateTime} from './CalendarDate';
import {constrain} from './manipulation';
import {getExtendedYear, GregorianCalendar} from './calendars/GregorianCalendar';
import {getLocalTimeZone} from './queries';
import {Mutable} from './utils';

export function epochFromDate(date: AnyDateTime) {
export function epochFromDate(date: AnyDateTime & Copyable) {
date = toCalendar(date, new GregorianCalendar());
let year = getExtendedYear(date.era, date.year);
return epochFromParts(year, date.month, date.day, date.hour, date.minute, date.second, date.millisecond);
Expand Down Expand Up @@ -259,7 +259,7 @@ export function toTime(dateTime: CalendarDateTime | ZonedDateTime): Time {
}

/** Converts a date from one calendar system to another. */
export function toCalendar<T extends AnyCalendarDate>(date: T, calendar: Calendar): T {
export function toCalendar<T extends AnyCalendarDate & Copyable>(date: T, calendar: Calendar): T {
if (date.calendar.identifier === calendar.identifier) {
return date;
}
Expand Down
Loading