Skip to content
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

Cleanup code handling timezones when converting icalDate to PHP's DateTime #222

Merged
merged 5 commits into from
Jul 10, 2019

Conversation

s0600204
Copy link
Collaborator

@s0600204 s0600204 commented May 27, 2019

There are quite a few things currently wrong with this method:

  • If a date is pre-1970, then a timezone is applied - so long as it's IANA (or converted Microsoft): support for CLDR timezones was never added into this logic path.

  • If a date is pre-1970, the branch of code ignores the work done by the earlier preg_match, and acquires the timezone through string manipulation instead.

  • If neither $forceUtc or $forceTimezone are true (i.e. the default state) then the timezone attached to a date is not applied to the date.

  • If $forceUtc is true, but $forceTimezone is false, then all dates in a calendar are assumed to be UTC.

  • If $forceTimezone is true, then the value of $forceUtc is functionally ignored.

    • This is also the only logic code path that a post-1969 date actually has its timezone applied.
  • In all but two calls to this method, both $forceTimezone and $forceUTC are passed as true (contrary to their default value of false).

    • The two exceptions to this are the dtstart and dtend of the first date of a Monthly recurrence rule - meaning that the first date of said rule will have no timezone applied (unless pre-1970), whilst subsequent dates generated from the rule will.
  • The provided changeset resolves all of that, and is a cleaner and saner approach to conversion.

There are quite a few things currently wrong with this method:

* If a date is pre-1970, then a timezone *is* applied - so long as it's IANA (or
  converted Microsoft): support for CLDR timezones was never added into this logic
  path.

* If a date is pre-1970, the branch of code ignores the work done by the earlier
  `preg_match`, and acquires the timezone through string manipulation instead.

* If neither $forceUtc or $forceTimezone are true (ie. the default state) then the
  timezone attached to a date is not applied to the date.
  - (If ignoring all timezone hinting is desired, then an argument "$ignoreTimezone"
    would be a better option.)

* If $forceUtc is true, but $forceTimezone is false, then all dates in a calendar
  are assumed to be UTC.

* If $forceTimezone is true, then the value of $forceUtc is functionally ignored.
  - (Admittedly it does lead to the timezone of any new PHP `DateTime` object that
    is created from a UTC time to be set to UTC twice, but who's counting?)
  - This is also the only logic code path that a post-1969 date actually has its
    timezone applied.

* In every call (except two) to this function, both $forceTimezone and $forceUTC
  are passed as true (contrary to their default value of false).
  - The two exceptions to this are the `dtstart` and `dtend` of the first date of
    a Monthly recurrence rule - meaning that the first date of said rule will have
    no timezone applied (unless pre-1970), whilst subsequent dates generated from
    the rule will.

The provided changeset resolves all of that, and is a cleaner and saner approach
to conversion.
@u01jmg3 u01jmg3 self-requested a review May 27, 2019 22:06
@u01jmg3 u01jmg3 changed the title Cleanup code handling timezones when converting icalDate to php's DateTime Cleanup code handling timezones when converting icalDate to PHP's DateTime May 27, 2019
@u01jmg3
Copy link
Owner

u01jmg3 commented Jun 3, 2019

Immediately getting an exception:

Exception: DateTime::__construct():
Failed to parse time string (20171032T000000) at position 7 (2): Unexpected character

Rather than have PHP try to work it out without revlevant context.

This means that if users try to feed us a date with an out-of-range date-portion
(eg. 20171032), then PHP can deal with it sensibly instead of throwing an exception.
@u01jmg3
Copy link
Owner

u01jmg3 commented Jun 4, 2019

I'm now getting:

Exception: Invalid iCal date format. in ics-parser\src\ICal\ICal.php:1035

Can you try testing with ICal.ics?

Given `TZID="(UTC-05:00) Eastern Time (US & Canada)":20160409T090000`:
* `str_getcsv()` inside `keyValueFromString()` strips the quote marks from the timezone
* The iCal Date is later put back together in `processEvents()` as `TZID=(UTC-05:00) Eastern Time (US & Canada):20160409T090000`
* This violates the iCal specification, as `:` is a control character and may not be used within what the specification calls "paramtext"

Thus, in this changeset:
* We add back the quotes in `processEvents()`
* Unfortunately, this means that CLDR timezone identifiers, (e.g. `Europe/Berlin`) also now have quotes around them and so do not get recognised as a valid timezone
* So we remove the quotes before the timezone text is passed to be comprehended.

*sigh*
@u01jmg3
Copy link
Owner

u01jmg3 commented Jun 4, 2019

@s0600204: thanks for being so quick with these fixes.

Reading your latest commit message:

Unfortunately, this means that CLDR timezone identifiers, (e.g. Europe/Berlin) also now have quotes around them and so do not get recognised as a valid timezone

Can you see a way around this?

@s0600204
Copy link
Collaborator Author

s0600204 commented Jun 4, 2019

The new method could, in theory, be used on all param-texts (as the iCal specification calls them) not just TZIDs, but applying it to all is beyond the intended scope of this pull request.

(And I meant IANA timezone identifiers, as CLDR identifiers are the ones that typically have a : in...)

@u01jmg3
Copy link
Owner

u01jmg3 commented Jun 13, 2019

composer test has 3 failures to correct.

(Sorry for the slow response)

Code was generating a date-time string that was essentially UTC, but formatting
it in such a way that was causing `iCalDateToUnixTimestamp` to interpret it as
a "floating" local time.

Solution is not to format a integer into a string and then interpret it into a
different integer.

A better solution might be to maintain a php DateTime object instead of an integer
timestamp: bumping up the days, months, weeks or years as necessary, so constantly
converting between integers, strings, and the odd DateTime object is unnecessary.

However, rewritting much of the event recurrence handling code is beyond the scope
of the pull request this commit is destined for.
@u01jmg3 u01jmg3 merged commit e7602fe into u01jmg3:master Jul 10, 2019
@u01jmg3 u01jmg3 added this to the v2.x.x milestone Jul 10, 2019
@s0600204 s0600204 deleted the timezones branch July 10, 2019 22:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants