Skip to content

DateTime addDays #53729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Magnuti opened this issue Oct 11, 2023 · 8 comments
Open

DateTime addDays #53729

Magnuti opened this issue Oct 11, 2023 · 8 comments
Labels
area-documentation Prefer using 'type-documentation' and a specific area label.

Comments

@Magnuti
Copy link

Magnuti commented Oct 11, 2023

This is a feature request for a method that can add whole days without being affected by daylight savings.

The DateTime add(Duration duration) method adds seconds. Several issues have been created where developers are confused about the behaviour of the method #37449.

image

The DateTime API is well documented, but the question on how to add days without being affected by daylight savings is not well documented. This is also causing issues in third party packages like jiffy jama5262/jiffy#81

Could we either

  • Update the documentation to specify how to add whole days, for example like this DateTime newDate = DateTime(date.year, date.month, date.day + days); (I don't even know if this is the correct way to do it, some StackOverflow posts mentions that solution)
  • Get a new method, for example DateTime addDays(int days) which is not affected by daylight savings
@parlough
Copy link
Member

parlough commented Oct 11, 2023

I'm not sure if I fully understand what you're looking for, but does the DateTime.copyWith extension method help you?

void main() {
  final beforeDaylightSavings = DateTime(2023, 11, 4, 12);
  
  final sameTimeNextDay = beforeDaylightSavings.copyWith(
    day: beforeDaylightSavings.day + 1,
  );
  
  final oneDayLater = beforeDaylightSavings.add(const Duration(days: 1));

  print('Timezone: ${beforeDaylightSavings.timeZoneName}');
  print('Day before daylight savings: $beforeDaylightSavings');
  print('Next day, same time: $sameTimeNextDay');
  print('One day (24 hours) later: $oneDayLater');
}

Results in the following output:

Timezone: Central Daylight Time
Day before daylight savings: 2023-11-04 12:00:00.000
Next day, same time: 2023-11-05 12:00:00.000
One day (24 hours) later: 2023-11-05 11:00:00.000

@Magnuti
Copy link
Author

Magnuti commented Oct 11, 2023

That is exactly what I am looking for @parlough! Could it be an idea to put a reference to that method in the DateTime.add method's documentation? The documentation mentions a possible problem but it mentions nothing about how to solve it.

Notice that the duration being added is actually 50 * 24 * 60 * 60 seconds. If the resulting DateTime has a different daylight saving offset than this, then the result won't have the same time-of-day as this, and may not even hit the calendar date 50 days later.
Be careful when working with dates in local time.

@parlough
Copy link
Member

parlough commented Oct 11, 2023

That is exactly what I am looking for @parlough!

Glad I could point you there! However, do note that (I think) copyWith has some limitations with daylight savings as well, as outlined in its API docs.

Since copyWith is implemented as an extension method, it can be particularly hard to discover from the generated API docs. I will re-kickstart discussion on dart-lang/dartdoc#2021 which plans to more clearly list them out.

Could it be an idea to put a reference to that method in the DateTime.add method's documentation? The documentation mentions a possible problem but it mentions nothing about how to solve it.

I haven't really worked with DateTime a whole lot, but a cross-reference from the add API docs makes sense to me. An expansion of the mentioned possible problems (or a link to learn more) is probably a good idea as well. @lrhn may have more insight on what makes the most sense :)

@parlough parlough added the area-documentation Prefer using 'type-documentation' and a specific area label. label Oct 11, 2023
@lrhn
Copy link
Member

lrhn commented Oct 11, 2023

It's an annoying issue that add is mostly useless, but it's also hard to change.
I'd love to change it to

DateTime add([Duration durationMilliseconds], {int years = 0, int months = 0, int days = 0, int hours = 0, int minutes = 0, int seconds = 0, int milliseconds = 0});

That won't work, partly because it's breaking to change the API because there are classes out there implementing it (they should not), and partly because you can't mix positional and named optional parameters (yet, as I've said for 12 years).

So, the good name is taken.
A similar extension method, with a different name and a named duration parameter, would avoid that issue.
It's just about finding the name, then.

And deciding what it means to add +1 day and -25 hours. The trivial approach is probably the best (same as date.with(day: date.day + 1, hours: date.hours - 25)).

increment? (nope!)
shift? (... no).
add..Something, but what?
update? Too close to with, but not entirely without merit. Probably what I'd go with. ... except that it doesn't have a direction, and date.update(days: 6) isn't clearly adding 6 days.

@Magnuti
Copy link
Author

Magnuti commented Oct 11, 2023

Thank you for opening the discussion again @parlough.

However, do note that copyWith has some limitations with daylight savings as well, as outlined in its API docs.

I don't think I understand this limitation properly. Could you give an example of it? Even if the daylight saving occurs it seems to return the DateTime I want.

void main() {
  final beforeDaylightSaving = DateTime(2023, 10, 29, 02, 30);
  print(beforeDaylightSaving);
  print(beforeDaylightSaving.timeZoneName);
  
  final afterDaylightSaving = beforeDaylightSaving.copyWith(
    minute: beforeDaylightSaving.minute + 30,
  );
  print(afterDaylightSaving);
  print(afterDaylightSaving.timeZoneName);
  
  final withAdd = beforeDaylightSaving.add(Duration(minutes: 30));
  print(withAdd);
  print(withAdd.timeZoneName);
}
2023-10-29 02:30:00.000
GMT+02:00

2023-10-29 03:00:00.000 // This is what I want
GMT+01:00

2023-10-29 02:00:00.000
GMT+01:00

To rephrase the question: I want DateTime arithmetics without worrying about daylight saving differences. Would the copyWith method suffice for that?

@lrhn
Copy link
Member

lrhn commented Oct 12, 2023

If you don't want to worry about daylight saving, use UTC. Nothing else will suffice.

On the other hand, if you do use UTC, almost any approach will work.

@ChopinDavid
Copy link

So, it seems to me the best fix for this is to do DateTime(d1.year, d1.month, d1.day + 1) rather than d1.add(Duration(days: 1))? And apparently this fix doesn't run into issues when the day value is greater than the actual number of days in the month, like DateTime(2023, 11, 31) (there are only 30 days in November) because the day value sort of "overflows" and this is essentially synonymous with DateTime(2023, 12, 1). At least running DateTime(2023, 11, 31).toIso8601String() prints "2023-12-01T00:00:00.000", so this does seem to be the case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-documentation Prefer using 'type-documentation' and a specific area label.
Projects
None yet
Development

No branches or pull requests

4 participants