-
Notifications
You must be signed in to change notification settings - Fork 247
Enhance CalDateTime and DateTimeSerializer #638
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
Conversation
01de82a to
0743c68
Compare
0743c68 to
a4cd5c0
Compare
08c7ff1 to
28df6fd
Compare
69f6508 to
a5b1bf5
Compare
Ical.Net/DataTypes/CalDateTime.cs
Outdated
| if (isEmpty) | ||
| if (string.IsNullOrWhiteSpace(value)) | ||
| { | ||
| Initialize(_value, _timeOnly.HasValue, value, Calendar); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing the TzId now initializes with immediate effect
7ff8e90 to
edf6103
Compare
Ical.Net/DataTypes/CalDateTime.cs
Outdated
| { | ||
| // The date and time parts that were used to initialize the instance | ||
| // or by the Value setter. | ||
| private DateTime _value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, we shouldn't maintain both, the DateTime value and Date-/TimeOnly. I suggest to remove _value altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without the backing value we lose the HasDate and HasTime "toggle". The Date-/TimOnly fields are just the replacement for the backing _hasDate and _hasTime booleans. This implementation is mainly because HasDate and HasTime require a setter from the IDateTime interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it would be an option to calculate the DateTime value from the other two just in time, when needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I get your point: HasDate and HasTime wouldn't have backing fields, they would just become pure boolean getters/setters. Then _value can disappear. Yep, that's better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be updated as proposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Ical.Net/DataTypes/CalDateTime.cs
Outdated
| private void Initialize(DateTime value, string tzId, Calendar cal) | ||
| { | ||
| if (!string.IsNullOrWhiteSpace(tzId) && !tzId.Equals("UTC", StringComparison.OrdinalIgnoreCase)) | ||
| if ((tzId != null && !string.IsNullOrWhiteSpace(tzId) && !tzId.Equals("UTC", StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, it's legacy (so nothing to necessarily deal with in this PR), but I think that we shouldn't have special handling for white space, or empty strings. Either the value is set (!= null) or not (== null). Input sanitization is not something this class should be responsible for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be updated as proposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Ical.Net/DataTypes/CalDateTime.cs
Outdated
| TzId = tzId; | ||
| _tzId = tzId; | ||
|
|
||
| initialValue = DateTime.SpecifyKind(dateTime, DateTimeKind.Local); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also legacy, but IMHO important: I think we should avoid DateTimeKind.Local wherever we can, because dealing with the system's local time really is a special case. If no tzid is specified, this would usually mean that the instance represents floating time (which btw cannot be converted to a different time zone). Floating time should rather be represented by DateTimeKind.Unspecified than .Local.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be updated as proposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
@axunonb I only had a very brief look at the new CalDateTime class. While I think that this PR brings some improvements, I feel that the whole design of the CalDateTime class deserves some more attention and improvements. Just a few thoughts:
jm2c |
|
Todo in this PR: Use of
|
* Introduced new methods and test cases in `CalDateTimeTests.cs` to ensure correct behavior of `CalDateTime` properties and methods. * Updated various tests in `DeserializationTests.cs` and `RecurrenceTests.cs` to handle `CalDateTime` without time components and improve clarity. * Refined `IsAllDay` property in `CalendarEvent.cs` and updated methods in `UniqueComponent.cs` and `VTimeZone.cs` to use `CalDateTime` with time components. * Removed outdated comments and improved code formatting. * Enabled nullable reference types and updated `IDateTime` interface. * Added new methods and properties to `CalDateTime.cs` for better date and time management. * Refactored methods in `Period`, `RecurrencePatternEvaluator`, and `RecurringEvaluator` classes to handle `HasTime` property correctly. * Improved `DateTimeSerializer` class for better performance and handling of nullable types. * Added XML documentation and marked obsolete methods. Resolves ical-org#630 Resolves ical-org#633 Resolves ical-org#635 Resolves ical-org#636 Resolves ical-org#637
1d15f23 to
54ee355
Compare
Depending on the time offset from local time to UTC the tests failed. Changed all date kinds to UTC.
54ee355 to
e7f772f
Compare
…me.Value` * Depending on `HasTime`, `Value` contains only the `DateTime.Date`, or the value including `DateTime.TimeOfDay`. * The timezone dermines whether `DateTimeKind.Utc`or `DateTimeKind.Unspecified`will be used.
minichma
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love the simplification that comes with removing the ambiguous DateTime member field and with removing DateTimeKind.Local! Didn't do a full review, only CalDateTime for now, but I think that's the core part anyways. Left a few comments with my thoughts, maybe not all relevant for this PR but for following ones.
Ical.Net/DataTypes/CalDateTime.cs
Outdated
| { | ||
| var copy = left.Copy<IDateTime>(); | ||
| var copy = left.Copy<CalDateTime>(); | ||
| if (Math.Abs(right.TotalDays % 1) > AlmostZeroEpsilon) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe (right.Ticks % TimeSpan.TicksPerDay) != 0? This way we could avoid dealing with FP precision issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
excellent, done
Ical.Net/Utility/DateUtil.cs
Outdated
|
|
||
| public static DateTime GetSimpleDateTimeData(IDateTime dt) | ||
| => DateTime.SpecifyKind(dt.Value, dt.IsUtc ? DateTimeKind.Utc : DateTimeKind.Local); | ||
| => DateTime.SpecifyKind(dt.Value, dt.IsUtc ? DateTimeKind.Utc : DateTimeKind.Unspecified); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the improved CalDateTime, this would be as simple as => dt.Value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right - done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, there are a few more places to deal with local date in this class.
- The behavior of
SimpleDateTimeToMatchis not easy to understand. Within the sln its only used for testing, so I'd suggest to move it to a test project, where the specific behavior might be helpful. MatchTimeZoneis only used inRecurrencePatternEvaluator. I'm pretty sure, that the way it deals with floating time is not the most natural one either (see GetOccurrences should not assume the system local date time #197). Probably we want to deal with that in a separate PR. Maybe move this method intoRecurrencePatternEvaluatorfor now and/or add a comment that we should reconsider/fix how it deals with local time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done:
- Move
DateUtil.SimpleDateTimeToMatchas private toRecurrenceTests - Move
DateUtil.MatchTimeZoneas private toRecurrencePatternEvaluator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DateUtil is internal, so this shouldn't be breaking.
| /// the system's local timezone. | ||
| /// </param> | ||
| /// <param name="hasTime">Set to <see langword="true"/> (default), if the <see cref="DateTime.TimeOfDay"/> must be included.</param> | ||
| public CalDateTime(DateTime value, string? tzId, bool hasTime = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having both, the Kind in the DateTime value, as well as the tzId, causes redundancy, that's not easy to understand. What, if we remove this constructor altogether? The user could easily call new CalDateTime(value).ToTimeZone(tzId), with way less ambiguity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be back on the agenda when we implement floating date/time. See #647
| public DateTime Date => Value.Date; | ||
|
|
||
| /// <inheritdoc cref="DateTime.TimeOfDay"/> | ||
| public TimeSpan TimeOfDay => Value.TimeOfDay; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this obsolete, now that we have the TimeOnlyValue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see task in #646
| public int DayOfYear => Value.DayOfYear; | ||
|
|
||
| /// <inheritdoc cref="DateTime.Date"/> | ||
| public DateTime Date => Value.Date; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Obsolete with DateOnlyValue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see task in #646
| /// <summary> | ||
| /// Gets the underlying <see cref="DateOnlyValue"/> of <see cref="Value"/>. | ||
| /// </summary> | ||
| public DateOnly? DateOnlyValue => HasDate ? _dateOnly : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just name it Date, in replacement of the existing, now obsolete Date?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see task in #646
| /// <summary> | ||
| /// Gets the underlying <see cref="TimeOnlyValue"/> of <see cref="Value"/>. | ||
| /// </summary> | ||
| public TimeOnly? TimeOnlyValue => HasTime ? _timeOnly : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just Time or TimeOfDay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see task in #646
* `if (Math.Abs(right.TotalDays % 1) > AlmostZeroEpsilon)` is now `if ((right.Ticks % TimeSpan.TicksPerDay) != 0)` * `DateUtil.DateTime GetSimpleDateTimeData(IDateTime dt)` returns `dt.Value` * Move internal `DateUtil.SimpleDateTimeToMatch` as private to `RecurrenceTests` * Move internal `DateUtil.MatchTimeZone` as private to `RecurrencePatternEvaluator` * Create tasks in ical-org#646 and ical-org#647 for new PRs
Also refactor for SonarCloud complaints
b8a64e7 to
7bf9c3b
Compare
|
|
@minichma Merged as you already reviewed twice :) |
|
Sorry for not being able to review this any earlier. Anyhow, this is a significant improvement, congrats! |



Breaking Changes
Calendarargument to constructor overload ofCalDateTimeCalDateTilmeonly usesDateTilmeKind.UtcandDateTimeKind.Unspecified.DateTimeKind.Localhas been dropped.nullfor the timezone will apply the systems local time withAsDateTimeOffset`.DateTimeKindof aDateTimevalue. This is also the case, if a UTC date and a timezone (not null) is used.CalDateTime.TzId: Will not read or writeCalDateTime.Parameters. The functionality has been moved to `DateTimeSerializer.Deserialize(TextReader)DateTimeSerializer.Deserialize(TextReader)will now throw for unrepresentable date/time values.CalDateTime.TzIdcan benullor a valid timezone ID. Whitespace is not allowed.CalDateTime
DateOnlyandTimeOnlyin order to return correct values forCalDateTime.HasTimeandCalDateTime.HasDate, even if hour, minutes and seconds are zero.ValueorTzIdwill re-initialize the instance in the same way as with CTOR overloads.ToStringoverloads to convert the date and time to a string representation. Thanks to @nzbart for his initial PR.DateTimeSerializer
SerializeToStringmethod to ensure the TZID parameter is correctly handled based on whether the date-time is in UTC.DateOnlyandTimeOnlyparts accordingly.Other
IDateTimePortable.System.DateTimeOnlyfor netstandard2.0 and netstandard2.1 for compatibility with net6.0 and later.Resolves #630
Resolves #633
Resolves #635
Resolves #636
Resolves #637
Resolves #197