diff --git a/docs/design-proposals/human-readable-datetime-display.md b/docs/design-proposals/human-readable-datetime-display.md new file mode 100644 index 00000000000..6f15688d466 --- /dev/null +++ b/docs/design-proposals/human-readable-datetime-display.md @@ -0,0 +1,169 @@ +# Implementation Design Proposal: Human-Readable DateTime Display + +## Status + +**Proposal** - Not yet implemented + +## Related Issues + +- [Implement Easier to read file lengths](https://github.com/kilasuit/PowerShell/issues/170) +- [PowerShell/PowerShell#24011 - Easier to read file sizes in directory listing](https://github.com/PowerShell/PowerShell/issues/24011) + +## Summary + +This proposal describes how to make `DateTime` values easier to read in default PowerShell formatted output. + +This is the second of three related proposals covering human-readable display improvements: + +1. Size - see `human-readable-file-size-display.md` +2. **DateTime** (this proposal) +3. TimeSpan - see `human-readable-timespan-display.md` + +## Motivation / Problem Statement + +PowerShell currently renders file timestamps in fixed short date/time formats (for example `04/07/2024 01:36`). While this is compact and culture-aware, it is not always the most useful representation when users are triaging recent activity. + +Examples where relative display is easier to scan: + +- Incident response: "modified 3 minutes ago" +- Build triage: "updated yesterday" +- Log analysis: "2 days ago" versus counting calendar dates + +The current display is precise but not always quickly readable for time-relative tasks. + +## Current Implementation + +### File listings (`Get-ChildItem`) + +File listing views rely on the computed property `LastWriteTimeString` in +`src/System.Management.Automation/namespaces/FileSystemProvider.cs`: + +```csharp +public static string LastWriteTimeString(PSObject instance) +{ + return instance?.BaseObject is FileSystemInfo fileInfo + ? string.Create(CultureInfo.CurrentCulture, $"{fileInfo.LastWriteTime,10:d} {fileInfo.LastWriteTime,8:t}") + : string.Empty; +} +``` + +That property is used by `src/System.Management.Automation/FormatAndOutput/DefaultFormatters/FileSystem_format_ps1xml.cs` for the `children` and `childrenWithHardlink` table views. + +### `System.DateTime` default formatting + +`src/System.Management.Automation/FormatAndOutput/DefaultFormatters/DotNetTypes_format_ps1xml.cs` +defines a `DateTime` view that renders the `DateTime` property directly, which defers to existing `DateTime` formatting conventions. + +## Proposed Implementation Options + +### Option 1: Keep absolute format, improve readability only + +Retain absolute date/time display but increase readability with consistent spacing and explicit format style guidance. + +**Example:** +``` +04 Jul 2024 01:36 +``` + +**Pros** +- Minimal behavioral change +- No ambiguity around timezone or reference point +- Very low compatibility risk + +**Cons** +- Does not solve the "how recent is this" problem +- Users still need mental conversion to relative time + +### Option 2: Relative DateTime for recent values + +Use relative display for recent values while preserving absolute display for older values. + +**Example policy:** +- `< 1 minute`: `just now` +- `< 1 hour`: `N min ago` +- `< 24 hours`: `N hours ago` +- `< 7 days`: `N days ago` +- `>= 7 days`: culture-aware absolute date/time + +**Example output:** +``` +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 3 min ago 776 backup.xml +-a--- 19 hours ago 17088 metadata.xml +-a--- 04/07/2024 01:36 6884 oldfile.xml +``` + +**Pros** +- Fast scanning for recency +- Better triage UX for interactive sessions + +**Cons** +- Loses precise timestamp directly in the table for recent items +- Could be surprising in scripts that parse formatted output + +### Option 3: Dual-format (relative + absolute) + +Display both relative and absolute values together. + +**Example:** +``` +3 min ago (04/07/2024 01:36) +``` + +**Pros** +- Keeps precision and readability +- Reduces ambiguity + +**Cons** +- Expands column width significantly +- May truncate in narrow terminals + +### Option 4: Experimental feature with opt-in behavior + +Implement Option 2 or Option 3 behind an experimental feature flag (for example `PSHumanReadableDateTime`). + +**Pros** +- Safe rollout +- Community feedback before default behavior changes +- Existing behavior remains unchanged unless enabled + +**Cons** +- Additional implementation and maintenance cost + +## Recommended Approach + +**Option 2 behind an experimental feature (Option 4)**. + +Reasoning: + +1. Relative rendering provides the most user value for interactive use. +2. Experimental gating avoids breaking established expectations in default output. +3. The model matches the size proposal's rollout strategy for consistency. + +## Implementation Plan (Design) + +1. Add experimental feature metadata (`PSHumanReadableDateTime`) in experimental feature manifests. +2. Add a helper formatter for relative DateTime rendering with culture-aware units. +3. Update `LastWriteTimeString` to switch between existing absolute output and relative output when the feature is enabled. +4. Keep the table column width stable (target fixed width strings like `999 days ago`). +5. Add tests around boundaries (`59s`, `60s`, `59m`, `60m`, `23h`, `24h`, `6d`, `7d`). +6. Document the behavior and provide examples in changelog/docs. + +## Compatibility and Risk + +- Object properties are unchanged (`LastWriteTime` remains `DateTime`). +- Only display formatting changes. +- Scripts that parse formatted text output may be affected; this is a known non-goal pattern and should be called out in release notes. + +## Open Questions + +1. Should "future" timestamps render as `in 5 min` (clock skew scenarios)? +2. Should relative formatting include seconds granularity (`45 sec ago`) or round to minutes? +3. Should absolute fallback threshold be 7 days, 30 days, or configurable? +4. Should timezone indicator be appended in absolute fallback values? + +## Related Work + +- Size proposal: `human-readable-file-size-display.md` +- TimeSpan proposal: `human-readable-timespan-display.md` diff --git a/docs/design-proposals/human-readable-file-size-display.md b/docs/design-proposals/human-readable-file-size-display.md new file mode 100644 index 00000000000..50088ef45ee --- /dev/null +++ b/docs/design-proposals/human-readable-file-size-display.md @@ -0,0 +1,262 @@ +# Implementation Design Proposal: Human-Readable File Size Display + +## Status + +**Proposal** - Not yet implemented + +## Related Issues + +- [Implement Easier to read file lengths](https://github.com/kilasuit/PowerShell/issues/170) +- [PowerShell/PowerShell#24011 - Easier to read file sizes in directory listing](https://github.com/PowerShell/PowerShell/issues/24011) + +## Summary + +This proposal describes how to make the `Length` column in `Get-ChildItem` output easier to read by displaying file sizes in a human-readable format (e.g., `1.18 TB` instead of `1298450612224`). + +This is the first of three related proposals covering human-readable display improvements: + +1. **Size** (this proposal) +2. DateTime (separate proposal - see `human-readable-datetime-display.md`) +3. TimeSpan (separate proposal - see `human-readable-timespan-display.md`) + +## Motivation / Problem Statement + +When using `Get-ChildItem` (or its alias `dir`/`ls`) to list files, the `Length` column currently shows raw byte counts. For large files, these numbers are difficult to read at a glance: + +``` +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 04/07/2024 01:36 859832320 small-backup.vhdx +-a--- 04/07/2024 01:36 1298450612224 large-backup.vhdx +-a--- 04/07/2024 01:36 62914560 esp.vhdx +``` + +The number `1298450612224` requires the user to manually count digits to determine the order of magnitude. This is poor user experience compared to tools like Windows File Explorer, which displays `1.18 TB` using binary units (1024-based), or the Unix `ls -h` flag which uses a similar convention. + +## Current Implementation + +### `LengthString` in `FileSystemProvider.cs` + +The `Length` column is populated via the `LengthString` computed property defined in +`src/System.Management.Automation/namespaces/FileSystemProvider.cs`: + +```csharp +public static string LengthString(PSObject instance) +{ + return instance?.BaseObject is FileInfo fileInfo + ? fileInfo.Attributes.HasFlag(FileAttributes.Offline) + ? $"({fileInfo.Length})" + : fileInfo.Length.ToString() + : string.Empty; +} +``` + +This is registered in the type system via `TypeTable_Types_Ps1Xml.cs` and used in the table format view defined in `FileSystem_format_ps1xml.cs`. + +### Existing `DisplayHumanReadableFileSize` Utility + +There is already a utility function in `src/System.Management.Automation/engine/Utils.cs` that converts bytes to human-readable format. It is currently used for progress display during copy/remove operations: + +```csharp +internal static string DisplayHumanReadableFileSize(long bytes) +{ + return bytes switch + { + < 1024 and >= 0 => $"{bytes} Bytes", + < 1048576 and >= 1024 => $"{(bytes / 1024.0).ToString("0.0")} KB", + < 1073741824 and >= 1048576 => $"{(bytes / 1048576.0).ToString("0.0")} MB", + < 1099511627776 and >= 1073741824 => $"{(bytes / 1073741824.0).ToString("0.000")} GB", + < 1125899906842624 and >= 1099511627776 => $"{(bytes / 1099511627776.0).ToString("0.00000")} TB", + < 1152921504606847000 and >= 1125899906842624 => $"{(bytes / 1125899906842624.0).ToString("0.0000000")} PB", + >= 1152921504606847000 => $"{(bytes / 1152921504606847000.0).ToString("0.000000000")} EB", + _ => $"0 Bytes", + }; +} +``` + +## Proposed Implementation Options + +### Option 1: Thousands Separator (N0 format) + +Format the byte count with thousands separators, keeping the raw byte value but making it easier to read at a glance. + +**Example output:** +``` +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 04/07/2024 01:36 859,832,320 small-backup.vhdx +-a--- 04/07/2024 01:36 1,298,450,612,224 large-backup.vhdx +-a--- 04/07/2024 01:36 62,914,560 esp.vhdx +``` + +**Pros:** +- Lossless: retains exact byte count +- Familiar to users who need precise sizes +- Locale-aware (commas vs. periods depending on system locale) +- No breaking change for scripts that parse the `Length` property (`.Length` on `FileInfo` is unaffected) + +**Cons:** +- Column still grows wide for very large files +- Still requires some cognitive effort for very large numbers + +**Implementation:** + +Modify `LengthString` to use the `N0` format specifier: + +```csharp +public static string LengthString(PSObject instance) +{ + return instance?.BaseObject is FileInfo fileInfo + ? fileInfo.Attributes.HasFlag(FileAttributes.Offline) + ? $"({fileInfo.Length:N0})" + : fileInfo.Length.ToString("N0") + : string.Empty; +} +``` + +Note: The column width in `FileSystem_format_ps1xml.cs` would need to be adjusted from 14 to accommodate larger formatted strings (e.g., `1,298,450,612,224` is 17 characters). + +### Option 2: Human-Readable Units (KB/MB/GB/TB) + +Display file sizes in the largest appropriate unit, similar to Windows File Explorer or Unix `ls -h`. + +**Example output:** +``` +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 04/07/2024 01:36 820.1 MB small-backup.vhdx +-a--- 04/07/2024 01:36 1.18 TB large-backup.vhdx +-a--- 04/07/2024 01:36 60.0 MB esp.vhdx +``` + +**Pros:** +- Extremely easy to read at a glance +- Consistent with Windows Explorer, macOS Finder, and `ls -h` on Unix +- Fixed-width column (e.g., `10.0 KB` to `999.9 TB`) + +**Cons:** +- Lossy: exact byte count not shown +- Could potentially break scripts that parse the `Length` column from formatted output (though this is not recommended practice) +- Requires a decision on binary (KiB/MiB/GiB) vs. decimal (KB/MB/GB) units + +**Implementation:** + +Reuse the existing `DisplayHumanReadableFileSize` utility (with possible refinements for consistent column width) in `LengthString`: + +```csharp +public static string LengthString(PSObject instance) +{ + return instance?.BaseObject is FileInfo fileInfo + ? fileInfo.Attributes.HasFlag(FileAttributes.Offline) + ? $"({Utils.DisplayHumanReadableFileSize(fileInfo.Length)})" + : Utils.DisplayHumanReadableFileSize(fileInfo.Length) + : string.Empty; +} +``` + +The `DisplayHumanReadableFileSize` function would need to be reviewed and potentially updated to produce consistent-width output suitable for table alignment. + +### Option 3: Experimental Feature with Configurable Format + +Wrap the change in an experimental feature flag (similar to other formatting changes in PowerShell), allowing users to opt in. The format could be configurable via `$PSStyle`. + +**Example (experimental feature enabled):** + +```powershell +Enable-ExperimentalFeature PSFileInfoHumanReadableSize +# Restart PowerShell session +Get-ChildItem +``` + +``` +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 04/07/2024 01:36 820.1 MB small-backup.vhdx +-a--- 04/07/2024 01:36 1.18 TB large-backup.vhdx +-a--- 04/07/2024 01:36 60.0 MB esp.vhdx +``` + +**Pros:** +- No breaking change for existing users +- Opt-in allows gathering feedback before making the change the default +- Follows established PowerShell pattern for behavior-changing features + +**Cons:** +- More implementation work (experimental feature infrastructure) +- Feature may never graduate from experimental status +- Users need to discover and enable the feature + +## Recommended Approach + +**Option 2 (human-readable units) wrapped in an experimental feature (Option 3)** is the recommended approach because: + +1. It provides the most user-friendly output +2. The experimental feature wrapper minimizes risk of breaking existing workflows +3. The existing `DisplayHumanReadableFileSize` function can be reused with minor adjustments +4. This mirrors how other improvements to `FileSystemProvider` formatting have been introduced (e.g., the `PSAnsiRenderingFileInfo` experimental feature) + +Once the experimental feature has been validated and has received positive community feedback, it can be promoted to the default behavior in a future major version. + +## Implementation Steps + +1. **Review and refine `DisplayHumanReadableFileSize`** + - Make the function `public` or extract a `public` variant that is suitable for column display + - Ensure consistent output width for table alignment + - Consider whether to use binary (1024-based) or decimal (1000-based) units + - Add culture-aware formatting + +2. **Create experimental feature** + - Add `PSFileInfoHumanReadableSize` to `experimental-feature-windows.json` and `experimental-feature-linux.json` + - Update `TypeTable_Types_Ps1Xml.cs` to conditionally use the new format when the feature is enabled + +3. **Update `LengthString`** + - Modify `FileSystemProvider.cs` to call the human-readable formatter when the experimental feature is active + - Keep existing behavior when the feature is not enabled + +4. **Update column width in format definition** + - Update `FileSystem_format_ps1xml.cs` to accommodate the new format width (e.g., 10 characters is typically sufficient for `000.0 XB`) + +5. **Update tests** + - Add/update tests in `test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1` to verify the new formatting behavior + - Test with the experimental feature both enabled and disabled + +6. **Documentation** + - Update `CHANGELOG.md` with the new feature + - Consider adding an entry to the `docs/` for user-facing documentation + +## Considerations and Open Questions + +### Binary vs. Decimal Units + +- **Binary (IEC) units**: KB = 1024 bytes, MB = 1,048,576 bytes, etc. (also written KiB, MiB, etc.) +- **Decimal (SI) units**: KB = 1,000 bytes, MB = 1,000,000 bytes, etc. + +Windows Explorer displays file sizes using binary units (1024-based), even though it labels them as KB/MB/GB/TB rather than using the IEC KiB/MiB/GiB/TiB notation. The existing `DisplayHumanReadableFileSize` in `Utils.cs` also uses binary units. The implementation should follow this convention for consistency, but the community should confirm whether to adopt IEC notation (KiB/MiB/GiB) for precision. + +### Offline Files + +The current implementation wraps offline file sizes in parentheses: `(1234)`. This convention should be preserved: `(1.2 GB)` or `(1,234,567,890)`. + +### Very Small Files (< 1 KB) + +Files under 1 KB should still display bytes. The threshold for switching to KB should be at least 1024 bytes to avoid displaying `0.0 KB`. + +### Backwards Compatibility + +The `Length` **property** on `FileInfo` objects (`$file.Length`) is unaffected — it will continue to return an exact `long` value. Only the formatted **display** string changes. + +Scripts that parse `Get-ChildItem | Format-Table Length` output would be affected if they rely on the raw byte string, but such parsing is generally considered bad practice in favor of using the property value directly. + +### Column Width + +Switching from raw bytes to human-readable units changes the column width characteristics: +- Raw bytes: variable width, up to 20+ characters for very large files +- Human-readable: relatively fixed width, typically 6-10 characters + +The column width in `FileSystem_format_ps1xml.cs` should be updated accordingly. + +## Related Work + +- This proposal covers **Size** only. Related proposals should be created for: + - **DateTime**: Easier-to-read `LastWriteTime` display (e.g., relative times like "2 hours ago") + - **TimeSpan**: Easier-to-read `TimeSpan` display in other contexts diff --git a/docs/design-proposals/human-readable-timespan-display.md b/docs/design-proposals/human-readable-timespan-display.md new file mode 100644 index 00000000000..9e69330ebf5 --- /dev/null +++ b/docs/design-proposals/human-readable-timespan-display.md @@ -0,0 +1,180 @@ +# Implementation Design Proposal: Human-Readable TimeSpan Display + +## Status + +**Proposal** - Not yet implemented + +## Related Issues + +- [Implement Easier to read file lengths](https://github.com/kilasuit/PowerShell/issues/170) +- [PowerShell/PowerShell#24011 - Easier to read file sizes in directory listing](https://github.com/PowerShell/PowerShell/issues/24011) + +## Summary + +This proposal describes how to make `TimeSpan` values easier to read in default PowerShell formatted output. + +This is the third of three related proposals covering human-readable display improvements: + +1. Size - see `human-readable-file-size-display.md` +2. DateTime - see `human-readable-datetime-display.md` +3. **TimeSpan** (this proposal) + +## Motivation / Problem Statement + +PowerShell commonly displays `TimeSpan` values using canonical .NET formatting (for example `00:00:00.0452000` or `1.02:03:04`). These are precise but not always easy to interpret quickly. + +Examples of friction: + +- `00:00:00.0452000` requires mental conversion to "45.2 milliseconds" +- `1.02:03:04` is compact but not instantly readable as "1 day, 2 hours, 3 minutes, 4 seconds" +- Large durations are difficult to compare visually in table output + +A human-readable representation improves discoverability and triage speed in interactive sessions. + +## Current Implementation + +`System.TimeSpan` format views are defined in: +`src/System.Management.Automation/FormatAndOutput/DefaultFormatters/DotNetTypes_format_ps1xml.cs` + +Current views: + +- **List view**: shows component and total properties (`Days`, `Hours`, `TotalMilliseconds`, etc.) +- **Table view**: columns for `Days`, `Hours`, `Minutes`, `Seconds`, `Milliseconds` +- **Wide view**: `TotalMilliseconds` + +There is no dedicated compact humanized formatter today; output is mostly numeric and property-centric. + +## Proposed Implementation Options + +### Option 1: Keep numeric output; add separators/rounding guidance + +Retain current component display and only standardize precision for common contexts. + +**Pros** +- Minimal change +- Keeps exact numeric interpretation + +**Cons** +- Does not significantly improve readability + +### Option 2: Humanized natural-language string + +Introduce compact natural language rendering for interactive default table/list contexts. + +**Examples:** + +- `00:00:00.0452000` -> `45.2 ms` +- `00:03:12` -> `3 min 12 sec` +- `02:15:00` -> `2 hr 15 min` +- `1.02:03:04` -> `1 d 2 hr 3 min` + +**Pros** +- Very fast visual scanning +- Unit-aware representation +- Better UX for diagnostics and progress-like outputs + +**Cons** +- Loses direct full precision unless explicitly requested +- Potential width variability + +### Option 3: Dual output (humanized + canonical) + +Display humanized value followed by canonical string. + +**Example:** +``` +2 hr 15 min (02:15:00) +``` + +**Pros** +- Readable and precise at the same time + +**Cons** +- Significantly wider display strings +- More truncation in narrow terminal widths + +### Option 4: Experimental feature-gated formatter + +Use Option 2 (or Option 3) behind an experimental feature (for example `PSHumanReadableTimeSpan`). + +**Pros** +- Low-risk rollout +- Community feedback before default behavior changes +- Preserves compatibility by default + +**Cons** +- Added implementation complexity + +## Recommended Approach + +**Option 2 behind an experimental feature (Option 4)**. + +Reasoning: + +1. Humanized TimeSpan values provide the largest readability gain. +2. Experimental gating protects existing workflows and expectations. +3. This aligns rollout strategy with the size and DateTime proposals. + +## Proposed Formatting Rules (Design Draft) + +### Unit selection + +- `< 1 ms`: show microseconds where available (`250 us`) or `0 ms` if no sub-ms precision is desired +- `< 1 sec`: show milliseconds with one decimal place (`45.2 ms`) +- `< 1 min`: show seconds with one decimal place (`12.4 sec`) +- `< 1 hr`: show minutes and optional seconds (`3 min 12 sec`) +- `< 1 day`: show hours and minutes (`2 hr 15 min`) +- `>= 1 day`: show days, hours, and minutes (`1 d 2 hr 3 min`) + +### Rounding + +- Use banker-independent standard rounding (`MidpointRounding.AwayFromZero`) for user-facing values. +- Keep at most one decimal place for sub-minute displays. + +### Negative values + +- Preserve sign prefix: `-3.5 sec`, `-1 hr 12 min`. + +### Width target + +- Keep default rendered strings under ~16 characters where possible to fit common table layouts. + +## Implementation Plan (Design) + +1. Add experimental feature metadata (`PSHumanReadableTimeSpan`) to feature manifests. +2. Add a centralized TimeSpan humanization helper in formatting/runtime utilities. +3. Update `System.TimeSpan` format views to use the helper for the relevant default view(s) when feature is enabled. +4. Keep existing views unchanged when feature is disabled. +5. Add tests for boundary values and sign handling. +6. Document behavior and examples in changelog/docs. + +## Compatibility and Risk + +- `TimeSpan` objects and properties are unchanged. +- Only display rendering changes in selected views. +- Scripts parsing formatted strings may be impacted; this should be documented as unsupported parsing behavior. + +## Test Matrix (Design) + +Suggested targeted test cases: + +- `TimeSpan.Zero` +- `TimeSpan.FromTicks(1)` +- `TimeSpan.FromMilliseconds(999.95)` +- `TimeSpan.FromSeconds(59.95)` +- `TimeSpan.FromMinutes(59.5)` +- `TimeSpan.FromHours(23.9)` +- `TimeSpan.FromDays(1.5)` +- Negative variants for each category + +## Open Questions + +1. Should sub-millisecond values be surfaced as microseconds (`us`) or rounded to milliseconds? +2. Should rounding mode be configurable? +3. Should canonical string be available via an opt-in table column style? +4. Should we align abbreviations with .NET or PowerShell-specific conventions (`hr` vs `h`, `sec` vs `s`)? + +## Related Work + +- Size proposal: `human-readable-file-size-display.md` +- DateTime proposal: `human-readable-datetime-display.md`