Repository: flutter
Issue: DateUtils lacks support for non-local time zones
Labels: framework,f: material design,has reproducible steps,P3,found in release: 3.10,team-design,triaged-design,found in release: 3.13

### Is there an existing issue for this?

- [X] I have searched the [existing issues](https://github.com/flutter/flutter/issues)
- [X] I have read the [guide to filing a bug](https://flutter.dev/docs/resources/bug-reports)

### Steps to reproduce

Trying to use DateUtils with UTC dates in a non-UTC time zone, I discovered that many of the functions in the DateUtils class currently lack support for dates with non-local time zones, leading to unintended behaviors. I'll try to clarify the unintended behaviors that currently happen with some examples.

**All examples are based on the local UTC+2 timezone.**
```dart
DateTime.now().timeZoneOffset;
// 2:00:00.000000
```

**See also [the comment with summaries of the examples](https://github.com/flutter/flutter/issues/132076#issuecomment-1669912088)**.

<details><summary>Show examples</summary>

## `dateOnly`

<details>
<summary>Examples</summary>

```dart
DateUtils.dateOnly(
    DateTime(1970, 1, 1, 23)
);
```
```dart
DateUtils.dateOnly(
    DateTime.utc(1970, 1, 1)
).toUtc();
```
```DART
DateUtils.dateOnly(
    DateTime.utc(1970, 1, 1, 23)
);
```
```dart
DateUtils.dateOnly(
    DateTime.utc(1970, 1, 1, 23).toLocal()
);
```
</details>
<details><summary>
Code reference</summary>

https://github.com/flutter/flutter/blob/ecef964a2e59c6fe38b514460d6a8ba29b997b2c/packages/flutter/lib/src/material/date.dart#L13-L15

</details>

## `datesOnly`
<details><summary>Examples</summary>

```dart
DateUtils.datesOnly(
  DateTimeRange(
    start: DateTime(1970, 1, 1, 23),
    end: DateTime(1970, 1, 2, 1),
  ),
);
```
```dart
DateUtils.datesOnly(
    DateTimeRange(
        start: DateTime.utc(1970, 1, 1, 23),
        end: DateTime.utc(1970, 1, 2, 1),
    ),
);
```
```dart
DateUtils.datesOnly(
  DateTimeRange(
    start: DateTime.utc(1970, 1, 1, 23).toLocal(),
    end: DateTime(1970, 1, 2, 1),
  ),
);
```
</details>
<details><summary>Code reference</summary>

https://github.com/flutter/flutter/blob/ecef964a2e59c6fe38b514460d6a8ba29b997b2c/packages/flutter/lib/src/material/date.dart#L22-L24

</details>

## `isSameDay`

<details><summary>Examples</summary>

```dart
DateUtils.isSameDay(
    DateTime(1970, 1, 1, 23),
    DateTime(1970, 1, 2),
);
```
```dart
DateUtils.isSameDay(
    DateTime.utc(1970, 1, 1, 23).toLocal(),
    DateTime(1970, 1, 2),
);
```
```dart
DateUtils.isSameDay(
    DateTime.utc(1970, 1, 1, 23),
    DateTime(1970, 1, 2),
);
```
</details>

<details><summary>Code reference</summary>

https://github.com/flutter/flutter/blob/ecef964a2e59c6fe38b514460d6a8ba29b997b2c/packages/flutter/lib/src/material/date.dart#L28-L33

</details>

## `isSameMonth`
<details><summary>Examples</summary>

```dart
DateUtils.isSameMonth(
    DateTime(1970, 12, 31, 23),
    DateTime(1971, 1, 1),
);
```
```dart
DateUtils.isSameMonth(
    DateTime.utc(1970, 12, 31, 23),
    DateTime(1971, 1, 1),
);
```
```dart
DateUtils.isSameMonth(
    DateTime.utc(1970, 12, 31, 23).toLocal(),
    DateTime(1971, 1, 1),
);
```
</details>

<details><summary>Code reference</summary>

https://github.com/flutter/flutter/blob/ecef964a2e59c6fe38b514460d6a8ba29b997b2c/packages/flutter/lib/src/material/date.dart#L37-L41

</details>

## `monthDelta`
<details><summary>Examples</summary>

```dart
DateUtils.monthDelta(
    DateTime(1970, 1, 31, 23),
    DateTime(1970, 2, 1),
);
```
```dart
DateUtils.monthDelta(
    DateTime.utc(1970, 1, 31, 23),
    DateTime(1970, 2, 1),
);
```
</details>

<details><summary>Code reference</summary>

https://github.com/flutter/flutter/blob/ad0aa8de75120f532463e493d5671cb72354250b/packages/flutter/lib/src/material/date.dart#L54-L56

</details>

## `addMonthsToMonthDate`
<details><summary>Examples</summary>

```dart
DateUtils.addMonthsToMonthDate(
    DateTime.utc(1970, 1),
    1,
);
```
</details>
<details><summary>Code reference</summary>

https://github.com/flutter/flutter/blob/ad0aa8de75120f532463e493d5671cb72354250b/packages/flutter/lib/src/material/date.dart#L70-L72

</details>

## `addDaysToDate`
<details><summary>Examples</summary>

```dart
DateUtils.addDaysToDate(
    DateTime.utc(1970, 1, 1),
    1,
);
```
</details>

<details><summary>Code reference</summary>

https://github.com/flutter/flutter/blob/ad0aa8de75120f532463e493d5671cb72354250b/packages/flutter/lib/src/material/date.dart#L76-L78

</details>

</details>

### Expected results

<details><summary>Show</summary>

## `dateOnly`

<details><summary>Expected results</summary>

Correct:
```console
1970-01-01 00:00:00.000
```

`dateOnly` evaluates the date in UTC and returns a UTC date:
```console
1970-01-01 00:00:00.000Z
```

`dateOnly` evaluates the date in UTC and returns a UTC date:
```console
1970-01-01 00:00:00.000Z
```

Correct:
```console
1970-01-02 00:00:00.000
```

</details>

## `datesOnly`
<details><summary>Expected results</summary>

Correct:
```console
1970-01-01 00:00:00.000 - 1970-01-02 00:00:00.000
```
DateTimeRange's `start` and `end` are given in the same time zone (UTC) of inputted date:
```console
1970-01-01 00:00:00.000Z - 1970-01-02 00:00:00.000Z
```
Correct:
```console
1970-01-02 00:00:00.000 - 1970-01-02 00:00:00.000
```

</details>

## `isSameDay`
<details><summary>Expected results</summary>

Correct:
```console
false
```
Correct:
```console
true
```
We could either raise an error if dates have different time zones or choose to evaluate them in UTC. Then the correct output may be:
```console
true
```

</details>

## `isSameMonth`
<details><summary>Expected results</summary>

Correct:
```console
false
```
We could either raise an error if dates have different time zones or choose to evaluate them in UTC. Then the correct output may be:
```console
true
```
Correct:
```console
true
```

</details>

## `monthDelta`
<details><summary>Expected results</summary>

Correct:
```console
1
```
We could either raise an error if dates have different time zones or choose to evaluate them in UTC. Then the correct output may be:
```console
0
```

</details>

## `addMonthsToMonthDate`
<details><summary>Expected results</summary>

The returned date has the same time zone (UTC) as the inputted date:
```console
1970-02-01 00:00:00.000Z
```

</details>

## `addDaysToDate`
<details><summary>Expected results</summary>

The returned date has the same time zone (UTC) as the inputted date:
```console
1970-01-02 00:00:00.000Z
```

</details>
</details>

### Actual results

<details><summary>Show</summary>

## `dateOnly`
<details><summary>Actual results</summary>

Correct:
```console
1970-01-01 00:00:00.000
```

The returned date is now in the local time zone. Consequently `toUct()` caused the change of the date:
```console
1969-12-31 23:00:00.000Z
```

The returned date is now in the local timezone but the date is wrong:
```console
1970-01-01 00:00:00.000
```

Correct:
```console
1970-01-02 00:00:00.000
```

</details>

## `datesOnly`
<details><summary>Actual results</summary>

Correct:
```console
1970-01-01 00:00:00.000 - 1970-01-02 00:00:00.000
```
It implicitly converted the date, `start` and `end` are now in the local time zone:
```console
1970-01-01 00:00:00.000 - 1970-01-02 00:00:00.000
```
Correct:
```console
1970-01-02 00:00:00.000 - 1970-01-02 00:00:00.000
```

</details>

## `isSameDay`
<details><summary>Actual results</summary>

Correct:
```console
false
```
Correct:
```console
true
```
Wrong. It's the same day in the local time zone, it's a different day in UTC. It may be an ambiguous method but instead, it just compares different time zone dates in the wrong way.
```console
false
```

</details>

## `isSameMonth`
<details><summary>Actual results</summary>

Correct:
```console
false
```
Wrong. It's the same month in both local and UTC time zones.
```console
false
```
Correct:
```console
true
```

</details>

## `monthDelta`
<details><summary>Actual results</summary>

Correct:
```console
1
```
Wrong. The month delta should be `1` in both local and UTC time zones.
```console
1
```

</details>

## `addMonthsToMonthDate`
<details><summary>Actual results</summary>

Wrong. The returned date mismatch the given date timezone.
```console
1970-02-01 00:00:00.000
```

</details>

## `addDaysToDate`
<details><summary>Actual results</summary>

Wrong. The returned date mismatch the given date timezone.
```console
1970-01-02 00:00:00.000
```

</details>
</details>

### Code sample

See above

### Screenshots or Video

_No response_

### Logs

_No response_

### Flutter Doctor output

<details><summary>Doctor output</summary>

```console
% flutter doctor -v
[✓] Flutter (Channel stable, 3.10.6, on macOS 13.4.1 22F770820d darwin-x64, locale en-IT)
    • Flutter version 3.10.6 on channel stable at /Users/aspadaro/development/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f468f3366c (4 weeks ago), 2023-07-12 15:19:05 -0700
    • Engine revision cdbeda788a
    • Dart version 3.0.6
    • DevTools version 2.23.1
```

</details>

Edit: Used collapsible sections to improve readability; added reference to the comment that provides summaries of the examples.