UNPKG

ts-py-datetime

Version:

Datetime handling the python way (or as close as possible), now with TypeScript

191 lines (190 loc) 6.69 kB
import * as d3 from 'd3-time-format'; import { MAXYEAR, MAXYEAR_ORDINAL, MINYEAR, MINYEAR_ORDINAL, } from '../constants'; import { toSeconds } from '../models/timedelta'; import { isParams } from '../utils/utils'; import { datetime } from './datetime'; import { timedelta } from './timedelta'; export class date { /** * A date object represents a date (year, month, and day) in an idealized calendar, * the current Gregorian calendar indefinitely extended in both directions. * @param {number} year * @param {number} month * @param {number} day */ constructor(year, month, day) { this.year = 1970; this.month = 1; this.day = 1; if (!Number.isInteger(year) || year < MINYEAR || year > MAXYEAR) { throw RangeError(`year ${year} is out of range`); } if (!Number.isInteger(month) || month < 1 || month > 12) { throw RangeError(`month ${month} is out of range`); } if (!Number.isInteger(day) || day < 1 || day > new Date(year, month, 0).getDate()) { throw RangeError(`day ${day} is out of range for month`); } Object.assign(this, { year, month, day }); } /** * Return a date with the same value, * except for those parameters given new values by whichever arguments are specified. * @param {number} [year=this.year] * @param {number} [month=this.month] * @param {number} [day=this.day] * @returns {date} */ replace(year = this.year, month = this.month, day = this.day) { const args = { year: year, month, day, }; if (isParams(year)) { delete args.year; Object.assign(args, year); } return new date(args.year ?? this.year, args.month ?? this.month, args.day ?? this.day); } /** * Return the proleptic Gregorian ordinal ofthe date, where January 1 of year 1 has ordinal 1. * @returns {number} */ toordinal() { return Math.trunc((this.valueOf() + new timedelta({ days: 719163 }).total_seconds()) / toSeconds.days); } /** * Return the day of the week as an integer, where Monday is 0 and Sunday is 6. * @returns {number} */ weekday() { // javascript week starts on sunday, while python one starts on monday return (this.jsDate.getDay() + 6) % 7; } /** * Return the day of the week as an integer, where Monday is 1 and Sunday is 7. * @returns {number} */ isoweekday() { return this.weekday() + 1; } /** * Return an array with three components: year, week, and weekday. * @returns {[number, number, number]} */ isocalendar() { const [year, week, weekday] = d3 .utcFormat('%G-%V-%u')(this.jsDate) .split('-'); return [Number(year), Number(week), Number(weekday)]; } /** * Return a string representing the date in ISO 8601 format, YYYY-MM-DD. * @returns {string} */ isoformat() { return d3.utcFormat('%Y-%m-%d')(this.jsDate); } /** * Return the POSIX timestamp of the date, assuming it's time is midnight. * @returns {number} */ valueOf() { return this.jsDate.getTime() / 1000; } /** * For a date d, d.toString() is equivalent to d.isoformat(). * @returns {string} */ toString() { return this.isoformat(); } /** * Return a string representing the date, such as Wed Dec 4 00:00:00 2002. * @returns {string} */ ctime() { return d3.timeFormat('%a %b %-e 00:00:00 %Y')(this.jsDate); } /** * Return a string representing the date, controlled by an explicit format string. * Format codes referring to hours, minutes, or seconds will see 0 values. * @param {string} format * @returns {string} */ strftime(format) { return d3.timeFormat(format)(this.jsDate); } /** Return this object as a JS Date object */ get jsDate() { return new Date(this.year, this.month - 1, this.day); } /** The earliest representable date POSIX timestamp, equal to -2177434800. */ static get min() { return new date(MINYEAR, 1, 1); } /** The latest representable date POSIX timestamp, equal to 253402232400. */ static get max() { return new date(MAXYEAR, 12, 31); } /** The smallest possible difference between non-equal date objects, 1 day, in seconds. */ static get resolution() { return new timedelta({ days: 1 }); } /** * Return the current local date. * @returns {date} */ static today() { const today = datetime.now(); return new date(today.year, today.month, today.day); } /** * Return the local date corresponding to the POSIX timestamp. * @param {number} timestamp * @returns {date} */ static fromtimestamp(timestamp) { const d = datetime.fromtimestamp(timestamp); return new date(d.year, d.month, d.day); } /** * Return the date corresponding to the proleptic Gregorian ordinal, where January 1 of year 1 has ordinal 1. * @param {number} ordinal * @returns {date} */ static fromordinal(ordinal) { if (ordinal < MINYEAR_ORDINAL || ordinal > MAXYEAR_ORDINAL) { throw RangeError(`ordinal ${ordinal} is out of range`); } return date.fromtimestamp(date.min.valueOf() + new timedelta({ days: ordinal - MINYEAR_ORDINAL }).valueOf()); } /** * Return a date corresponding to a date_string given in the ISO 8601 format YYYY-MM-DD. * @param {string} date_string * @returns {date} */ static fromisoformat(date_string) { const d = d3.isoParse(date_string); if (d) { return new date(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate()); } throw SyntaxError('Unable to parse date string'); } /** * Return a date corresponding to the ISO calendar date specified by year, week, and day. * @param {number} year * @param {number} week * @param {number} day * @returns {date} */ static fromisocalendar(year, week, day) { const d = datetime.strptime(`${year}-${week}-${day}`, '%G-%V-%u'); return new date(Number(d.year), Number(d.month), Number(d.day)); } }