You've already found out that the warning was shown because it is part of the set of warnings for level 5. But you may still be wondering what this warning actually means.
A hint of that is already in the warning itself. When you call an instance method on a struct type, which includes virtual methods like ToString(), the compiler cannot ascertain that the underlying struct remains immutable. This is a key point for F# and it tries very hard to make sure your original let binding remains unchanged.
There are several optimizations in the F# compiler that try to minimize the amount of defensive copying being done. But there are still numerous cases where it cannot be certain a value doesn't change. This is true for any virtual call (you might argue that a virtual call cannot be overridden from a struct, but the override for the current struct can be overridden, and has access to the fields, hence, it can mutate its data), and more generally, for any instance member.
If I take your code, and pass it into FSI (after setting warn:5), it correctly reports two warnings:
> let now = DateTimeOffset.Now
now.AddDays(30.0).ToString("yyyy-MM-dd");;
now.AddDays(30.0).ToString("yyyy-MM-dd");;
^^^^^^^^^^^^^^^^^
stdin(3,1): warning FS0052: The value has been copied to ensure the original is not mutated by this operation or because the copy is implicit when returning a struct from a member and another member is then accessed
now.AddDays(30.0).ToString("yyyy-MM-dd");;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stdin(3,1): warning FS0052: The value has been copied to ensure the original is not mutated by this operation or because the copy is implicit when returning a struct from a member and another member is then accessed
val now : DateTimeOffset = 16-7-2020 0:21:21 +02:00
val it : string = "2020-08-15"
Oftentimes, the JIT can optimize these away, but just like F#, the JIT also cannot always ascertain the defensive copy is necessary or not. In such cases, the copying will still take place. I've seen this behavior being different for different JITs (can even change for the same JIT between x86 and x64).
So how would you prevent this copying from happening? This isn't always easy, and certainly not if you cannot change the type's implementation. A little counter-intuitive, if you tell F# you don't care whether it is mutated or not, it will stop copying the struct for you:
let mutable now = DateTimeOffset.Now
now <- now.AddDays(30.)
now.ToString("yyyy-MM-dd");;
Note that this warning does not raise for certain build-in types like float or int, because the compiler knows these types and their implementation and knows they won't mutate (all BCL methods are safe). No defensive copies are made for those, generally.
Also note that it is not specific to DateTimeOffset, for instance, DateTime and Guid behave exactly the same, as will do almost any other struct that isn't part of the primitive types.
Edit: Tomas' answer is also valuable, he explains why the copy for AddDays is actually necessary in this case. But this is an intermediate copy for the result and is not the defensive copy, which in this case end up being the same thing (confusing, I know). The warning is raised even if the result doesn't require an intermediate copy, like for ToString.