dateTimeFormatters.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. import { toFixed, toFixedScaled } from './valueFormats';
  2. import { DecimalCount } from '../../types';
  3. import moment from 'moment';
  4. interface IntervalsInSeconds {
  5. [interval: string]: number;
  6. }
  7. export enum Interval {
  8. Year = 'year',
  9. Month = 'month',
  10. Week = 'week',
  11. Day = 'day',
  12. Hour = 'hour',
  13. Minute = 'minute',
  14. Second = 'second',
  15. Millisecond = 'millisecond',
  16. }
  17. const INTERVALS_IN_SECONDS: IntervalsInSeconds = {
  18. [Interval.Year]: 31536000,
  19. [Interval.Month]: 2592000,
  20. [Interval.Week]: 604800,
  21. [Interval.Day]: 86400,
  22. [Interval.Hour]: 3600,
  23. [Interval.Minute]: 60,
  24. [Interval.Second]: 1,
  25. [Interval.Millisecond]: 0.001,
  26. };
  27. export function toNanoSeconds(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) {
  28. if (size === null) {
  29. return '';
  30. }
  31. if (Math.abs(size) < 1000) {
  32. return toFixed(size, decimals) + ' ns';
  33. } else if (Math.abs(size) < 1000000) {
  34. return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' µs');
  35. } else if (Math.abs(size) < 1000000000) {
  36. return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' ms');
  37. } else if (Math.abs(size) < 60000000000) {
  38. return toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, ' s');
  39. } else {
  40. return toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, ' min');
  41. }
  42. }
  43. export function toMicroSeconds(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) {
  44. if (size === null) {
  45. return '';
  46. }
  47. if (Math.abs(size) < 1000) {
  48. return toFixed(size, decimals) + ' µs';
  49. } else if (Math.abs(size) < 1000000) {
  50. return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' ms');
  51. } else {
  52. return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' s');
  53. }
  54. }
  55. export function toMilliSeconds(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) {
  56. if (size === null) {
  57. return '';
  58. }
  59. if (Math.abs(size) < 1000) {
  60. return toFixed(size, decimals) + ' ms';
  61. } else if (Math.abs(size) < 60000) {
  62. // Less than 1 min
  63. return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' s');
  64. } else if (Math.abs(size) < 3600000) {
  65. // Less than 1 hour, divide in minutes
  66. return toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min');
  67. } else if (Math.abs(size) < 86400000) {
  68. // Less than one day, divide in hours
  69. return toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour');
  70. } else if (Math.abs(size) < 31536000000) {
  71. // Less than one year, divide in days
  72. return toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day');
  73. }
  74. return toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, ' year');
  75. }
  76. export function trySubstract(value1: DecimalCount, value2: DecimalCount): DecimalCount {
  77. if (value1 !== null && value1 !== undefined && value2 !== null && value2 !== undefined) {
  78. return value1 - value2;
  79. }
  80. return undefined;
  81. }
  82. export function toSeconds(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) {
  83. if (size === null) {
  84. return '';
  85. }
  86. // Less than 1 µs, divide in ns
  87. if (Math.abs(size) < 0.000001) {
  88. return toFixedScaled(size * 1e9, decimals, trySubstract(scaledDecimals, decimals), -9, ' ns');
  89. }
  90. // Less than 1 ms, divide in µs
  91. if (Math.abs(size) < 0.001) {
  92. return toFixedScaled(size * 1e6, decimals, trySubstract(scaledDecimals, decimals), -6, ' µs');
  93. }
  94. // Less than 1 second, divide in ms
  95. if (Math.abs(size) < 1) {
  96. return toFixedScaled(size * 1e3, decimals, trySubstract(scaledDecimals, decimals), -3, ' ms');
  97. }
  98. if (Math.abs(size) < 60) {
  99. return toFixed(size, decimals) + ' s';
  100. } else if (Math.abs(size) < 3600) {
  101. // Less than 1 hour, divide in minutes
  102. return toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min');
  103. } else if (Math.abs(size) < 86400) {
  104. // Less than one day, divide in hours
  105. return toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour');
  106. } else if (Math.abs(size) < 604800) {
  107. // Less than one week, divide in days
  108. return toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day');
  109. } else if (Math.abs(size) < 31536000) {
  110. // Less than one year, divide in week
  111. return toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week');
  112. }
  113. return toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, ' year');
  114. }
  115. export function toMinutes(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) {
  116. if (size === null) {
  117. return '';
  118. }
  119. if (Math.abs(size) < 60) {
  120. return toFixed(size, decimals) + ' min';
  121. } else if (Math.abs(size) < 1440) {
  122. return toFixedScaled(size / 60, decimals, scaledDecimals, 2, ' hour');
  123. } else if (Math.abs(size) < 10080) {
  124. return toFixedScaled(size / 1440, decimals, scaledDecimals, 3, ' day');
  125. } else if (Math.abs(size) < 604800) {
  126. return toFixedScaled(size / 10080, decimals, scaledDecimals, 4, ' week');
  127. } else {
  128. return toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, ' year');
  129. }
  130. }
  131. export function toHours(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) {
  132. if (size === null) {
  133. return '';
  134. }
  135. if (Math.abs(size) < 24) {
  136. return toFixed(size, decimals) + ' hour';
  137. } else if (Math.abs(size) < 168) {
  138. return toFixedScaled(size / 24, decimals, scaledDecimals, 2, ' day');
  139. } else if (Math.abs(size) < 8760) {
  140. return toFixedScaled(size / 168, decimals, scaledDecimals, 3, ' week');
  141. } else {
  142. return toFixedScaled(size / 8760, decimals, scaledDecimals, 4, ' year');
  143. }
  144. }
  145. export function toDays(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) {
  146. if (size === null) {
  147. return '';
  148. }
  149. if (Math.abs(size) < 7) {
  150. return toFixed(size, decimals) + ' day';
  151. } else if (Math.abs(size) < 365) {
  152. return toFixedScaled(size / 7, decimals, scaledDecimals, 2, ' week');
  153. } else {
  154. return toFixedScaled(size / 365, decimals, scaledDecimals, 3, ' year');
  155. }
  156. }
  157. export function toDuration(size: number, decimals: DecimalCount, timeScale: Interval): string {
  158. if (size === null) {
  159. return '';
  160. }
  161. if (size === 0) {
  162. return '0 ' + timeScale + 's';
  163. }
  164. if (size < 0) {
  165. return toDuration(-size, decimals, timeScale) + ' ago';
  166. }
  167. const units = [
  168. { long: Interval.Year },
  169. { long: Interval.Month },
  170. { long: Interval.Week },
  171. { long: Interval.Day },
  172. { long: Interval.Hour },
  173. { long: Interval.Minute },
  174. { long: Interval.Second },
  175. { long: Interval.Millisecond },
  176. ];
  177. // convert $size to milliseconds
  178. // intervals_in_seconds uses seconds (duh), convert them to milliseconds here to minimize floating point errors
  179. size *= INTERVALS_IN_SECONDS[timeScale] * 1000;
  180. const strings = [];
  181. // after first value >= 1 print only $decimals more
  182. let decrementDecimals = false;
  183. let decimalsCount = 0;
  184. if (decimals !== null || decimals !== undefined) {
  185. decimalsCount = decimals as number;
  186. }
  187. for (let i = 0; i < units.length && decimalsCount >= 0; i++) {
  188. const interval = INTERVALS_IN_SECONDS[units[i].long] * 1000;
  189. const value = size / interval;
  190. if (value >= 1 || decrementDecimals) {
  191. decrementDecimals = true;
  192. const floor = Math.floor(value);
  193. const unit = units[i].long + (floor !== 1 ? 's' : '');
  194. strings.push(floor + ' ' + unit);
  195. size = size % interval;
  196. decimalsCount--;
  197. }
  198. }
  199. return strings.join(', ');
  200. }
  201. export function toClock(size: number, decimals?: DecimalCount) {
  202. if (size === null) {
  203. return '';
  204. }
  205. // < 1 second
  206. if (size < 1000) {
  207. return moment.utc(size).format('SSS\\m\\s');
  208. }
  209. // < 1 minute
  210. if (size < 60000) {
  211. let format = 'ss\\s:SSS\\m\\s';
  212. if (decimals === 0) {
  213. format = 'ss\\s';
  214. }
  215. return moment.utc(size).format(format);
  216. }
  217. // < 1 hour
  218. if (size < 3600000) {
  219. let format = 'mm\\m:ss\\s:SSS\\m\\s';
  220. if (decimals === 0) {
  221. format = 'mm\\m';
  222. } else if (decimals === 1) {
  223. format = 'mm\\m:ss\\s';
  224. }
  225. return moment.utc(size).format(format);
  226. }
  227. let format = 'mm\\m:ss\\s:SSS\\m\\s';
  228. const hours = `${('0' + Math.floor(moment.duration(size, 'milliseconds').asHours())).slice(-2)}h`;
  229. if (decimals === 0) {
  230. format = '';
  231. } else if (decimals === 1) {
  232. format = 'mm\\m';
  233. } else if (decimals === 2) {
  234. format = 'mm\\m:ss\\s';
  235. }
  236. return format ? `${hours}:${moment.utc(size).format(format)}` : hours;
  237. }
  238. export function toDurationInMilliseconds(size: number, decimals: DecimalCount) {
  239. return toDuration(size, decimals, Interval.Millisecond);
  240. }
  241. export function toDurationInSeconds(size: number, decimals: DecimalCount) {
  242. return toDuration(size, decimals, Interval.Second);
  243. }
  244. export function toDurationInHoursMinutesSeconds(size: number) {
  245. const strings = [];
  246. const numHours = Math.floor(size / 3600);
  247. const numMinutes = Math.floor((size % 3600) / 60);
  248. const numSeconds = Math.floor((size % 3600) % 60);
  249. numHours > 9 ? strings.push('' + numHours) : strings.push('0' + numHours);
  250. numMinutes > 9 ? strings.push('' + numMinutes) : strings.push('0' + numMinutes);
  251. numSeconds > 9 ? strings.push('' + numSeconds) : strings.push('0' + numSeconds);
  252. return strings.join(':');
  253. }
  254. export function toTimeTicks(size: number, decimals: DecimalCount, scaledDecimals: DecimalCount) {
  255. return toSeconds(size, decimals, scaledDecimals);
  256. }
  257. export function toClockMilliseconds(size: number, decimals: DecimalCount) {
  258. return toClock(size, decimals);
  259. }
  260. export function toClockSeconds(size: number, decimals: DecimalCount) {
  261. return toClock(size * 1000, decimals);
  262. }
  263. export function dateTimeAsIso(value: number, decimals: DecimalCount, scaledDecimals: DecimalCount, isUtc?: boolean) {
  264. const time = isUtc ? moment.utc(value) : moment(value);
  265. if (moment().isSame(value, 'day')) {
  266. return time.format('HH:mm:ss');
  267. }
  268. return time.format('YYYY-MM-DD HH:mm:ss');
  269. }
  270. export function dateTimeAsUS(value: number, decimals: DecimalCount, scaledDecimals: DecimalCount, isUtc?: boolean) {
  271. const time = isUtc ? moment.utc(value) : moment(value);
  272. if (moment().isSame(value, 'day')) {
  273. return time.format('h:mm:ss a');
  274. }
  275. return time.format('MM/DD/YYYY h:mm:ss a');
  276. }
  277. export function dateTimeFromNow(value: number, decimals: DecimalCount, scaledDecimals: DecimalCount, isUtc?: boolean) {
  278. const time = isUtc ? moment.utc(value) : moment(value);
  279. return time.fromNow();
  280. }