'Why can't I add months to OffsetDateTime?
I'm using NodaTime to manage dates and time zones in a .Net Core WebApi. One of the type in the library is OffsetDateTime, which is very similar to DateTimeOffset from the .Net framework. I use it everywhere to manipulate dates in an explicit and transparent way since dates are sometimes into system time zone and user time zone.
I need to add a month to a certain date at a certain point, but I can not add a month to a OffsetDateTime object, all I can do is add up until hours or a type called Duration which is calendar-independent. If it was the type Instant I'd understand since Instant represents a point in time in a really abstract way, but not OffsetDateTime. OffsetDateTime even has a "Calendar" property which really shows it's bound to a calendar system which should allow you to do arithmetics like what I want to do, without having to go through type conversions etc.
On top of that, DateTimeOffset (from the .net framework) allows you to add months, but I want to be consistent and use the same types everywhere.
Long story short, I can not do :
public OffsetDateTime GetPreviousMonth(OffsetDateTime input)
{
return input.AddMonths(-1)
}
I can only do:
offsetDateTime.PlusHours(15)
offsetDateTime.PlusMinutes(3000)
offsetDateTime.Minus(Duration.FromMinutes(60))
offsetDateTime.Minus(Duration.FromHours(1))
Any idea how I can solve this without going through type conversions? Maybe I overlooked something in the documentation, but I don't think so.
Solution 1:[1]
An OffsetDateTime
represents a local date time with an offset from UTC, and an Instant.
It is not, however, bound to a TimeZone.
For this reason, you can add a "fixed" amount from it like seconds, minutes and hours because those are not TimeZone dependents.
You cannot subtract a month from it, as it can't know if there was a daylight transition during the past month.
I know you asked for a solution without type conversion, but in reality you can't. To handle this correctly, you must convert it to a ZonedDateTime
with the correct time zone. Any solution without specifying the TimeZone you may eventually hit a case where the result is wrong.
Solution 2:[2]
You can use OffsetDateTime.With
, which lets you provide a LocalDate
adjuster. You can operate (Plus
, Minus
, ...) on a LocalDate
with a Period
which lets you specify time spans in terms of months:
public OffsetDateTime GetPreviousMonth(OffsetDateTime input)
{
return input.With((LocalDate ld) => ld.Minus(Period.FromMonths(1)));
}
Solution 3:[3]
You can use OffsetDateTime
directly, but you should use some method like now()
.
Then you can add or minus D, M, Y, H, M, S and so on
OffsetDateTime.now().minusDays(5).
format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"))
OR
OffsetDateTime.now(ZoneOffset.UTC).plusMonths(month).
format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Jeff |
Solution 3 | Moddasir |