Armand A. Verstappen
2010-Aug-27 19:37 UTC
[iCalendar-devel] trouble parsing timezone data
Hi, I recently started playing with the ruby icalendar module, and I''m running into some trouble. This may very well be cause by ruby being far from my native language, on the other hand I may have hit a bug.>From what I''ve learned so far from reading examples, I believe I should be able to do... cals = Icalendar.parse($<) cals.each do |cal| cal.timezones.earch do |tz| puts "timezone id is: #{tz.tzid}" puts "standard offset from is: #{tz.standard.tzoffsetfrom}" end end ... With stock icalendar 1.1.5, the tz.standard.tzoffsetfrom is allway empty. While debugging, I came to believe this being cause by: ... # Allow block syntax for declaration of standard and daylight components of timezone def standard(&block) e = Standard.new self.add_component e e.instance_eval &block if block e end def daylight(&block) e = Daylight.new self.add_component e e.instance_eval &block if block e end ... in lib/icalendar/component/timezone.rb At least, renaming those functions to add_standard and add_daylight will leave me with properly populated standard and daylight objects/components when parsing .ics files. I am of course sure that in renaming those functions, I have broken just about everything on the creation of ical files. Not a real problem for me, as my use case only involves parsing, but I''d like to have my solution work with an unpatched version of icalendar of course. Looking at my problem, I see two possible causes: a) I have completely misguessed the proper way to access the ''standard'' and ''daylight'' components of the timezone object/component, thus overwriting the properly parsed versions with empty versions when hitting the ''e=Standard.new'' and ''e=Daylight.new'' lines above. b) There''s a bug in icalendar, where the daylight and standard methods are designed for creating new components/objects, but accidently block calling of their methods and the retrieval of their attributes. As this is the first time I jump into ruby, and I have only taken half the time to breeze through a ruby introduction tutorial, I''m not equipped to decide whether a) or b) is the case, and certainly not to produce a patch should it turn out to be b). I would really appreciate if someone could tell me if a) or b) is the case, as is a nice pointer to documentation that would have allowed me to produce the answer myself. best regards, -- Armand A. Verstappen <armand at nl.envida.net>
On Fri, Aug 27, 2010 at 3:37 PM, Armand A. Verstappen <armand at nl.envida.net> wrote:> Hi, > > I recently started playing with the ruby icalendar module, and I''m running into some trouble. This may very well be cause by ruby being far from my native language, on the other hand I may have hit a bug. > > >From what I''ve learned so far from reading examples, I believe I should be able to do > > ... > cals = Icalendar.parse($<) > cals.each do |cal| > ?cal.timezones.earch do |tz| > ?puts "timezone id is: #{tz.tzid}" > ?puts "standard offset from is: #{tz.standard.tzoffsetfrom}" > ?end > end > ... > > With stock icalendar 1.1.5, the tz.standard.tzoffsetfrom is allway empty. While debugging, I came to believe this being cause by: > > ... > ? ?# Allow block syntax for declaration of standard and daylight components of timezone > ? ?def standard(&block) > ? ? ?e = Standard.new > ? ? ?self.add_component e > > ? ? ?e.instance_eval &block if block > > ? ? ?e > ? ?end > > ? ?def daylight(&block) > ? ? ?e = Daylight.new > ? ? ?self.add_component e > > ? ? ?e.instance_eval &block if block > > ? ? ?e > ? ?end > ... > > in lib/icalendar/component/timezone.rb > > At least, renaming those functions to add_standard and add_daylight will leave me with properly populated standard and daylight objects/components when parsing .ics files. I am of course sure that in renaming those functions, I have broken just about everything on the creation of ical files. Not a real problem for me, as my use case only involves parsing, but I''d like to have my solution work with an unpatched version of icalendar of course. Looking at my problem, I see two possible causes: > > a) I have completely misguessed the proper way to access the ''standard'' and ''daylight'' components of the timezone object/component, thus overwriting the properly parsed versions with empty versions when hitting the ''e=Standard.new'' and ''e=Daylight.new'' lines above. > b) There''s a bug in icalendar, where the daylight and standard methods are designed for creating new components/objects, but accidently block calling of their methods and the retrieval of their attributes. > > As this is the first time I jump into ruby, and I have only taken half the time to breeze through a ruby introduction tutorial, I''m not equipped to decide whether a) or b) is the case, and certainly not to produce a patch should it turn out to be b). > > I would really appreciate if someone could tell me if a) or b) is the case, as is a nice pointer to documentation that would have allowed me to produce the answer myself. >I can''t really speak for the iCalendar gem as I haven''t used it for some time. I''m the author of ri_cal which is a newer implementation of the icalendar data format (RFC 2445/5445) for Ruby. The main impetus for writing ri_cal was to support more of the semantics of icalendar data, particularly in the area of time zones, and enumeration of recurring events. So I do know a bit about ical format. One issue here is that a VTIMEZONE component doesn''t in general have only one standard and one daylight component, it may have one or more of either. As such it doesn''t make sense to talk about THE standard component for a VTIMEZONE. Why can a VTIMEZONE have multiple standard components? Because a VTIMEZONE defines a time zone over the time period covering all of the events in the calendar, and time zone definitions change over time. For example in the US the dates of the transition times have changed over the years, most recently during the Bush administration daylight saving time was extended. In ri_cal there is a method to return a collection of standard periods, and a similar one to return a collection of daylight periods, but it''s undocumented and isn''t part of the API. To get a period ri_cal timezone components have methods like period_for_utc(time_or_date_time) # return the period in effect for the timezone at a given time in UTC period_for_local(time_or_date_time) # returns the period(s) in effect at the given local time period_for_local may actually return two different periods for some edge case times near a standard to daylight saving transition. These methods aren''t normally used externally though, the main methods in ri_cal are utc_to_local, and local_to_utc which do what the names imply. So as I said, I don''t know how to answer you in the context of the icalendar gem, actually had a quick look at the source code and I think that iCalendar as it is right now won''t handle timezones with more than one daylight or more than one standard period, which is a misinterpretation of the RFCs. Actually I can see several bugs in the icalendar gem''s implementation of the TImezone component. 1) At the top of the class it has class Timezone < Component ical_component :standard, :daylight This defines methods called standard and daylight which each return an element of a hash containing the subcomponents. This hash is referenced by the instance variable @components. 2) I then overrides the add_component method of its superclass # Define a custom add component method because standard and daylight # are the only components that can occur just once with their parent. def add_component(component) key = component.class.to_s.downcase.gsub(''icalendar::'','''').to_sym @components[key] = component end The overridden method makes the values in the @components hash to be arrays, which allows having more than one of the same kind of component. 3) It then REDEFINES the methods standard and daylight throwing away the methods which were created by metaprogramming in that ical_component call (see #1) # Allow block syntax for declaration of standard and daylight components of timezone def standard(&block) e = Standard.new self.add_component e e.instance_eval &block if block e end def daylight(&block) e = Daylight.new self.add_component e e.instance_eval &block if block e end So those daylight and standard methods act more like a setter than a getter, and you''re right when you call standard to get the standard component, the code is actually setting it to a new instance of Icalendar::Standard with NO attributes. I haven''t read enough of the parsing code to determine what else is going on here. -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Github: http://github.com/rubyredrick Twitter: @RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale
Armand A. Verstappen
2010-Sep-07 18:08 UTC
[iCalendar-devel] trouble parsing timezone data
Hi Rick, For some reason, I did not receive your reply to the list, I just ran into it in the list archive. Thank you for your answer, it confirms my suspicions. I''ll make sure to have a look at ri_cal, as indeed I''m not really interested in the timezones, I just want the event time to be presented to me in local time format, while most of the icalendar requests I receive originate from a different timezone. Timezone parsing took 95% of my code lines, the remainder was spent doing what I actually wanted to do in the first place. From what you say, ri_cal may be much more accomodating to my needs. Cheers, -- Armand. On Fri, Aug 27, 2010 at 21:37:13 +0200, Armand A. Verstappen wrote:> Hi, > > I recently started playing with the ruby icalendar module, and I''m running into some trouble. This may very well be cause by ruby being far from my native language, on the other hand I may have hit a bug. > > >From what I''ve learned so far from reading examples, I believe I should be able to do > > ... > cals = Icalendar.parse($<) > cals.each do |cal| > cal.timezones.earch do |tz| > puts "timezone id is: #{tz.tzid}" > puts "standard offset from is: #{tz.standard.tzoffsetfrom}" > end > end > ... > > With stock icalendar 1.1.5, the tz.standard.tzoffsetfrom is allway empty. While debugging, I came to believe this being cause by: > > ... > # Allow block syntax for declaration of standard and daylight components of timezone > def standard(&block) > e = Standard.new > self.add_component e > > e.instance_eval &block if block > > e > end > > def daylight(&block) > e = Daylight.new > self.add_component e > > e.instance_eval &block if block > > e > end > ... > > in lib/icalendar/component/timezone.rb > > At least, renaming those functions to add_standard and add_daylight will leave me with properly populated standard and daylight objects/components when parsing .ics files. I am of course sure that in renaming those functions, I have broken just about everything on the creation of ical files. Not a real problem for me, as my use case only involves parsing, but I''d like to have my solution work with an unpatched version of icalendar of course. Looking at my problem, I see two possible causes: > > a) I have completely misguessed the proper way to access the ''standard'' and ''daylight'' components of the timezone object/component, thus overwriting the properly parsed versions with empty versions when hitting the ''e=Standard.new'' and ''e=Daylight.new'' lines above. > b) There''s a bug in icalendar, where the daylight and standard methods are designed for creating new components/objects, but accidently block calling of their methods and the retrieval of their attributes. > > As this is the first time I jump into ruby, and I have only taken half the time to breeze through a ruby introduction tutorial, I''m not equipped to decide whether a) or b) is the case, and certainly not to produce a patch should it turn out to be b). > > I would really appreciate if someone could tell me if a) or b) is the case, as is a nice pointer to documentation that would have allowed me to produce the answer myself. > > best regards, > > -- > Armand A. Verstappen <armand at nl.envida.net> > _______________________________________________ > icalendar-devel mailing list > icalendar-devel at rubyforge.org > http://rubyforge.org/mailman/listinfo/icalendar-devel