diff --git a/__tests__/humanFileSize.spec.ts b/__tests__/humanFileSize.spec.ts index acba5dba..192c7f2b 100644 --- a/__tests__/humanFileSize.spec.ts +++ b/__tests__/humanFileSize.spec.ts @@ -4,8 +4,8 @@ import { formatFileSize, parseFileSize } from '../lib/humanfilesize' describe('humanFileSize', () => { describe('formatFileSize', () => { - it('renders binary sizes by default', () => { - expect(formatFileSize(2048)).toBe('2 KiB') + it('renders binary size with decimal units by default', () => { + expect(formatFileSize(2048)).toBe('2 KB') }) it('renders file sizes with the correct unit', function() { @@ -32,12 +32,14 @@ describe('humanFileSize', () => { [128000000000000, '116.4 TiB'], [128000000000000.0, '116.4 TiB'], [128000000000000000.0, '113.7 PiB'], - ] + ] as const for (let i = 0; i < dataDecimal.length; i++) { - expect(formatFileSize(dataDecimal[i][0], false, false)).toEqual(dataDecimal[i][1]) + expect(formatFileSize(dataDecimal[i][0], false, false, true)).toEqual(dataDecimal[i][1]) } for (let i = 0; i < dataBinary.length; i++) { expect(formatFileSize(dataBinary[i][0], false, true)).toEqual(dataBinary[i][1]) + // Binary sizes but decimal units + expect(formatFileSize(dataBinary[i][0], false, false)).toEqual(dataBinary[i][1].replace('i', '')) } }) @@ -61,12 +63,14 @@ describe('humanFileSize', () => { [128000000000000, '116.4 TiB'], [128000000000000.0, '116.4 TiB'], [128000000000000000.0, '113.7 PiB'], - ] + ] as const for (let i = 0; i < dataDecimal.length; i++) { - expect(formatFileSize(dataDecimal[i][0], true, false)).toEqual(dataDecimal[i][1]) + expect(formatFileSize(dataDecimal[i][0], true, false, true)).toEqual(dataDecimal[i][1]) } for (let i = 0; i < dataBinary.length; i++) { expect(formatFileSize(dataBinary[i][0], true, true)).toEqual(dataBinary[i][1]) + // Binary sizes but decimal units + expect(formatFileSize(dataBinary[i][0], true, false)).toEqual(dataBinary[i][1].replace('i', '')) } }) @@ -95,12 +99,14 @@ describe('humanFileSize', () => { [128000000000000, '116,4 TiB'], [128000000000000.0, '116,4 TiB'], [128000000000000000.0, '113,7 PiB'], - ] + ] as const for (let i = 0; i < dataDecimal.length; i++) { - expect(formatFileSize(dataDecimal[i][0], false, false)).toEqual(dataDecimal[i][1]) + expect(formatFileSize(dataDecimal[i][0], false, false, true)).toEqual(dataDecimal[i][1]) } for (let i = 0; i < dataBinary.length; i++) { expect(formatFileSize(dataBinary[i][0], false, true)).toEqual(dataBinary[i][1]) + // Binary sizes but decimal units + expect(formatFileSize(dataBinary[i][0], false, false)).toEqual(dataBinary[i][1].replace('i', '')) } }) }) diff --git a/lib/humanfilesize.ts b/lib/humanfilesize.ts index 49765e54..a34a8476 100644 --- a/lib/humanfilesize.ts +++ b/lib/humanfilesize.ts @@ -29,28 +29,30 @@ const humanListBinary = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'] /** * Format a file size in a human-like format. e.g. 42GB * + * The default for Nextcloud is like Windows using binary sizes but showing decimal units, + * meaning 1024 bytes will show as 1KB instead of 1KiB or like on Apple 1.02 KB + * * @param size in bytes * @param skipSmallSizes avoid rendering tiny sizes and return '< 1 KB' instead - * @param binaryPrefixes True if size base 2 (and binary prefixes like `KiB`) should be used + * @param binaryPrefixes True if size binary prefixes like `KiB` should be used as per IEC 80000-13 + * @param base1000 Set to true to use base 1000 as per SI or used by Apple (default is base 1024 like Linux or Windows) */ -export function formatFileSize(size: number|string, skipSmallSizes = false, binaryPrefixes = true): string { +export function formatFileSize(size: number|string, skipSmallSizes = false, binaryPrefixes = false, base1000 = false): string { + // Binary prefixes only work with base 1024 + binaryPrefixes = binaryPrefixes && !base1000 if (typeof size === 'string') { size = Number(size) } - /* - * @note This block previously used Log base 1024, per IEC 80000-13; - * however, the wrong prefix was used. Now we use decimal calculation - * with base 1000 per the SI. Base 1024 calculation with binary - * prefixes is optional, but has yet to be added to the UI. - */ // Calculate Log with base 1024 or 1000: size = base ** order - let order = size > 0 ? Math.floor(Math.log(size) / Math.log(binaryPrefixes ? 1024 : 1000)) : 0 + let order = size > 0 ? Math.floor(Math.log(size) / Math.log(base1000 ? 1000 : 1024)) : 0 + // Stay in range of the byte sizes that are defined order = Math.min((binaryPrefixes ? humanListBinary.length : humanList.length) - 1, order) + const readableFormat = binaryPrefixes ? humanListBinary[order] : humanList[order] - let relativeSize = (size / Math.pow(binaryPrefixes ? 1024 : 1000, order)).toFixed(1) + let relativeSize = (size / Math.pow(base1000 ? 1000 : 1024, order)).toFixed(1) if (skipSmallSizes === true && order === 0) { return (relativeSize !== '0.0' ? '< 1 ' : '0 ') + (binaryPrefixes ? humanListBinary[1] : humanList[1])