2020-01-02 15:13:47 -05:00
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2019-04-27 19:07:11 -04:00
2020-08-15 10:37:17 -04:00
import { DateTimeFormatter } from "./formatter.ts" ;
2019-01-11 00:19:42 -05:00
2020-06-27 07:10:45 -04:00
export const SECOND = 1 e3 ;
export const MINUTE = SECOND * 60 ;
export const HOUR = MINUTE * 60 ;
export const DAY = HOUR * 24 ;
export const WEEK = DAY * 7 ;
2020-07-14 14:23:54 -04:00
const DAYS_PER_WEEK = 7 ;
enum Day {
Sun ,
Mon ,
Tue ,
Wed ,
Thu ,
Fri ,
Sat ,
}
2020-06-27 07:10:45 -04:00
2019-01-03 10:19:20 -05:00
/ * *
* Parse date from string using format string
2020-08-15 10:37:17 -04:00
* @param dateString Date string
2019-03-14 10:24:54 -04:00
* @param format Format string
* @return Parsed date
2019-01-03 10:19:20 -05:00
* /
2020-08-15 10:37:17 -04:00
export function parse ( dateString : string , formatString : string ) : Date {
const formatter = new DateTimeFormatter ( formatString ) ;
const parts = formatter . parseToParts ( dateString ) ;
return formatter . partsToDate ( parts ) ;
2019-01-03 10:19:20 -05:00
}
/ * *
2020-08-15 10:37:17 -04:00
* Format date using format string
* @param date Date
2019-03-14 10:24:54 -04:00
* @param format Format string
2020-08-15 10:37:17 -04:00
* @return formatted date string
2019-01-03 10:19:20 -05:00
* /
2020-08-15 10:37:17 -04:00
export function format ( date : Date , formatString : string ) : string {
const formatter = new DateTimeFormatter ( formatString ) ;
return formatter . format ( date ) ;
2019-01-03 10:19:20 -05:00
}
2019-03-11 11:00:30 -04:00
/ * *
* Get number of the day in the year
2019-03-14 10:24:54 -04:00
* @return Number of the day in year
2019-03-11 11:00:30 -04:00
* /
2019-03-14 10:24:54 -04:00
export function dayOfYear ( date : Date ) : number {
2020-08-27 05:12:49 -04:00
// Values from 0 to 99 map to the years 1900 to 1999. All other values are the actual year. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date)
// Using setFullYear as a workaround
2020-09-15 10:09:40 -04:00
2020-08-27 05:12:49 -04:00
const yearStart = new Date ( date ) ;
2020-09-15 10:09:40 -04:00
yearStart . setUTCFullYear ( date . getUTCFullYear ( ) , 0 , 0 ) ;
2020-07-14 15:24:17 -04:00
const diff = date . getTime ( ) -
2019-03-11 11:00:30 -04:00
yearStart . getTime ( ) +
( yearStart . getTimezoneOffset ( ) - date . getTimezoneOffset ( ) ) * 60 * 1000 ;
2020-09-15 10:09:40 -04:00
2020-06-27 07:10:45 -04:00
return Math . floor ( diff / DAY ) ;
2019-03-11 11:00:30 -04:00
}
2020-07-09 15:50:42 -04:00
/ * *
* Get number of the week in the year ( ISO - 8601 )
* @return Number of the week in year
* /
export function weekOfYear ( date : Date ) : number {
const workingDate = new Date (
2020-07-14 15:24:17 -04:00
Date . UTC ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) ) ,
2020-07-09 15:50:42 -04:00
) ;
2020-07-14 14:23:54 -04:00
const day = workingDate . getUTCDay ( ) ;
2020-07-14 15:24:17 -04:00
const nearestThursday = workingDate . getUTCDate ( ) +
2020-07-14 14:23:54 -04:00
Day . Thu -
( day === Day . Sun ? DAYS_PER_WEEK : day ) ;
workingDate . setUTCDate ( nearestThursday ) ;
2020-07-09 15:50:42 -04:00
// Get first day of year
const yearStart = new Date ( Date . UTC ( workingDate . getUTCFullYear ( ) , 0 , 1 ) ) ;
// return the calculated full weeks to nearest Thursday
2020-07-14 14:23:54 -04:00
return Math . ceil ( ( workingDate . getTime ( ) - yearStart . getTime ( ) + DAY ) / WEEK ) ;
2019-01-03 10:19:20 -05:00
}
2019-04-27 19:07:11 -04:00
/ * *
2020-12-10 14:45:45 -05:00
* Parse a date to return a IMF formatted string date
2019-04-27 19:07:11 -04:00
* RFC : https : //tools.ietf.org/html/rfc7231#section-7.1.1.1
* IMF is the time format to use when generating times in HTTP
* headers . The time being formatted must be in UTC for Format to
* generate the correct format .
* @param date Date to parse
2020-12-10 14:45:45 -05:00
* @return IMF date formatted string
2019-04-27 19:07:11 -04:00
* /
export function toIMF ( date : Date ) : string {
2019-10-05 12:02:34 -04:00
function dtPad ( v : string , lPad = 2 ) : string {
2020-03-31 06:34:13 -04:00
return v . padStart ( lPad , "0" ) ;
2019-04-27 19:07:11 -04:00
}
const d = dtPad ( date . getUTCDate ( ) . toString ( ) ) ;
const h = dtPad ( date . getUTCHours ( ) . toString ( ) ) ;
const min = dtPad ( date . getUTCMinutes ( ) . toString ( ) ) ;
const s = dtPad ( date . getUTCSeconds ( ) . toString ( ) ) ;
const y = date . getUTCFullYear ( ) ;
2019-11-16 08:24:07 -05:00
const days = [ "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" ] ;
2019-04-27 19:07:11 -04:00
const months = [
"Jan" ,
"Feb" ,
"Mar" ,
2019-11-16 08:24:07 -05:00
"Apr" ,
2019-04-27 19:07:11 -04:00
"May" ,
"Jun" ,
"Jul" ,
"Aug" ,
"Sep" ,
"Oct" ,
"Nov" ,
2020-03-28 13:03:49 -04:00
"Dec" ,
2019-04-27 19:07:11 -04:00
] ;
2019-05-13 14:03:24 -04:00
return ` ${ days [ date . getUTCDay ( ) ] } , ${ d } ${
2019-04-27 19:07:11 -04:00
months [ date . getUTCMonth ( ) ]
} $ { y } $ { h } : $ { min } : $ { s } GMT ` ;
}
2020-06-27 07:10:45 -04:00
/ * *
* Check given year is a leap year or not .
* based on : https : //docs.microsoft.com/en-us/office/troubleshoot/excel/determine-a-leap-year
* @param year year in number or Date format
* /
export function isLeap ( year : Date | number ) : boolean {
const yearNumber = year instanceof Date ? year . getFullYear ( ) : year ;
return (
( yearNumber % 4 === 0 && yearNumber % 100 !== 0 ) || yearNumber % 400 === 0
) ;
}
export type Unit =
2020-09-14 22:48:49 -04:00
| "milliseconds"
2020-06-27 07:10:45 -04:00
| "seconds"
| "minutes"
| "hours"
| "days"
| "weeks"
| "months"
| "quarters"
| "years" ;
export type DifferenceFormat = Partial < Record < Unit , number > > ;
export type DifferenceOptions = {
units? : Unit [ ] ;
} ;
/ * *
* Calculate difference between two dates .
* @param from Year to calculate difference
* @param to Year to calculate difference with
* @param options Options for determining how to respond
*
* example :
*
* ` ` ` typescript
* datetime . difference ( new Date ( "2020/1/1" ) , new Date ( "2020/2/2" ) , { units : [ "days" , "months" ] } )
* ` ` `
* /
export function difference (
from : Date ,
to : Date ,
2020-07-14 15:24:17 -04:00
options? : DifferenceOptions ,
2020-06-27 07:10:45 -04:00
) : DifferenceFormat {
2020-07-14 15:24:17 -04:00
const uniqueUnits = options ? . units ? [ . . . new Set ( options ? . units ) ] : [
2020-09-14 22:48:49 -04:00
"milliseconds" ,
2020-07-14 15:24:17 -04:00
"seconds" ,
"minutes" ,
"hours" ,
"days" ,
"weeks" ,
"months" ,
"quarters" ,
"years" ,
] ;
2020-06-27 07:10:45 -04:00
const bigger = Math . max ( from . getTime ( ) , to . getTime ( ) ) ;
const smaller = Math . min ( from . getTime ( ) , to . getTime ( ) ) ;
const differenceInMs = bigger - smaller ;
const differences : DifferenceFormat = { } ;
for ( const uniqueUnit of uniqueUnits ) {
switch ( uniqueUnit ) {
2020-09-14 22:48:49 -04:00
case "milliseconds" :
differences . milliseconds = differenceInMs ;
2020-06-27 07:10:45 -04:00
break ;
case "seconds" :
differences . seconds = Math . floor ( differenceInMs / SECOND ) ;
break ;
case "minutes" :
differences . minutes = Math . floor ( differenceInMs / MINUTE ) ;
break ;
case "hours" :
differences . hours = Math . floor ( differenceInMs / HOUR ) ;
break ;
case "days" :
differences . days = Math . floor ( differenceInMs / DAY ) ;
break ;
case "weeks" :
differences . weeks = Math . floor ( differenceInMs / WEEK ) ;
break ;
case "months" :
differences . months = calculateMonthsDifference ( bigger , smaller ) ;
break ;
case "quarters" :
differences . quarters = Math . floor (
( typeof differences . months !== "undefined" &&
differences . months / 4 ) ||
2020-07-14 15:24:17 -04:00
calculateMonthsDifference ( bigger , smaller ) / 4 ,
2020-06-27 07:10:45 -04:00
) ;
break ;
case "years" :
differences . years = Math . floor (
( typeof differences . months !== "undefined" &&
differences . months / 12 ) ||
2020-07-14 15:24:17 -04:00
calculateMonthsDifference ( bigger , smaller ) / 12 ,
2020-06-27 07:10:45 -04:00
) ;
break ;
}
}
return differences ;
}
function calculateMonthsDifference ( bigger : number , smaller : number ) : number {
const biggerDate = new Date ( bigger ) ;
const smallerDate = new Date ( smaller ) ;
const yearsDiff = biggerDate . getFullYear ( ) - smallerDate . getFullYear ( ) ;
const monthsDiff = biggerDate . getMonth ( ) - smallerDate . getMonth ( ) ;
2020-12-10 14:45:45 -05:00
const calendarDifferences = Math . abs ( yearsDiff * 12 + monthsDiff ) ;
2020-06-27 07:10:45 -04:00
const compareResult = biggerDate > smallerDate ? 1 : - 1 ;
biggerDate . setMonth (
2020-12-10 14:45:45 -05:00
biggerDate . getMonth ( ) - compareResult * calendarDifferences ,
2020-06-27 07:10:45 -04:00
) ;
2020-07-14 15:24:17 -04:00
const isLastMonthNotFull = biggerDate > smallerDate
? 1
: - 1 === - compareResult
? 1
: 0 ;
2020-12-10 14:45:45 -05:00
const months = compareResult * ( calendarDifferences - isLastMonthNotFull ) ;
2020-06-27 07:10:45 -04:00
return months === 0 ? 0 : months ;
}