Coder Social home page Coder Social logo

icalendar-recurrence's Introduction

iCalendar -- Internet calendaring, Ruby style

Ruby Code Climate

http://github.com/icalendar/icalendar

Upgrade from 1.x

Better documentation is still to come, but in the meantime the changes needed to move from 1.x to 2.0 are summarized by the diff needed to update the README

DESCRIPTION

iCalendar is a Ruby library for dealing with iCalendar files in the iCalendar format defined by RFC-5545.

EXAMPLES

Creating calendars and events

require 'icalendar'

# Create a calendar with an event (standard method)
cal = Icalendar::Calendar.new
cal.event do |e|
  e.dtstart     = Icalendar::Values::Date.new('20050428')
  e.dtend       = Icalendar::Values::Date.new('20050429')
  e.summary     = "Meeting with the man."
  e.description = "Have a long lunch meeting and decide nothing..."
  e.ip_class    = "PRIVATE"
end

cal.publish

Or you can make events like this

event = Icalendar::Event.new
event.dtstart = DateTime.civil(2006, 6, 23, 8, 30)
event.summary = "A great event!"
cal.add_event(event)

event2 = cal.event  # This automatically adds the event to the calendar
event2.dtstart = DateTime.civil(2006, 6, 24, 8, 30)
event2.summary = "Another great event!"

Support for property parameters

params = {"altrep" => "http://my.language.net", "language" => "SPANISH"}

event = cal.event do |e|
  e.dtstart = Icalendar::Values::Date.new('20050428')
  e.dtend   = Icalendar::Values::Date.new('20050429')
  e.summary = Icalendar::Values::Text.new "This is a summary with params.", params
end
event.summary.ical_params #=> {'altrep' => 'http://my.language.net', 'language' => 'SPANISH'}

# or

event = cal.event do |e|
  e.dtstart = Icalendar::Values::Date.new('20050428')
  e.dtend   = Icalendar::Values::Date.new('20050429')
  e.summary = "This is a summary with params."
  e.summary.ical_params = params
end
event.summary.ical_params #=> {'altrep' => 'http://my.language.net', 'language' => 'SPANISH'}

Support for Dates or DateTimes

Sometimes we don't care if an event's start or end are Date or DateTime objects. For this, we can use DateOrDateTime.new(value). Calling .call on the returned DateOrDateTime will immediately return the underlying Date or DateTime object.

event = cal.event do |e|
  e.dtstart = Icalendar::Values::DateOrDateTime.new('20140924')
  e.dtend   = Icalendar::Values::DateOrDateTime.new('20140925').call
  e.summary = 'This is an all-day event, because DateOrDateTime will return Dates'
end

Support for URLs

For clients that can parse and display a URL associated with an event, it's possible to assign one.

event = cal.event do |e|
  e.url = 'https://example.com'
end

We can output the calendar as a string

cal_string = cal.to_ical
puts cal_string

ALARMS

Within an event

cal.event do |e|
  # ...other event properties
  e.alarm do |a|
    a.action          = "EMAIL"
    a.description     = "This is an event reminder" # email body (required)
    a.summary         = "Alarm notification"        # email subject (required)
    a.attendee        = %w(mailto:[email protected] mailto:[email protected]) # one or more email recipients (required)
    a.append_attendee "mailto:[email protected]"
    a.trigger         = "-PT15M" # 15 minutes before
    a.append_attach   Icalendar::Values::Uri.new("ftp://host.com/novo-procs/felizano.exe", "fmttype" => "application/binary") # email attachments (optional)
  end

  e.alarm do |a|
    a.action  = "DISPLAY" # This line isn't necessary, it's the default
    a.summary = "Alarm notification"
    a.trigger = "-P1DT0H0M0S" # 1 day before
  end

  e.alarm do |a|
    a.action        = "AUDIO"
    a.trigger       = "-PT15M"
    a.append_attach "Basso"
  end
end

Output

# BEGIN:VALARM
# ACTION:EMAIL
# ATTACH;FMTTYPE=application/binary:ftp://host.com/novo-procs/felizano.exe
# TRIGGER:-PT15M
# SUMMARY:Alarm notification
# DESCRIPTION:This is an event reminder
# ATTENDEE:mailto:[email protected]
# ATTENDEE:mailto:[email protected]
# END:VALARM
#
# BEGIN:VALARM
# ACTION:DISPLAY
# TRIGGER:-P1DT0H0M0S
# SUMMARY:Alarm notification
# END:VALARM
#
# BEGIN:VALARM
# ACTION:AUDIO
# ATTACH;VALUE=URI:Basso
# TRIGGER:-PT15M
# END:VALARM

Checking for an Alarm

Calling the event.alarm method will create an alarm if one doesn't exist. To check if an event has an alarm use the has_alarm? method.

event.has_alarm?
# => false

event.alarm
# => #<Icalendar::Alarm ... >

event.has_alarm?
#=> true

TIMEZONES

cal = Icalendar::Calendar.new
cal.timezone do |t|
  t.tzid = "America/Chicago"

  t.daylight do |d|
    d.tzoffsetfrom = "-0600"
    d.tzoffsetto   = "-0500"
    d.tzname       = "CDT"
    d.dtstart      = "19700308T020000"
    d.rrule        = "FREQ=YEARLY;BYMONTH=3;BYDAY=2SU"
  end

  t.standard do |s|
    s.tzoffsetfrom = "-0500"
    s.tzoffsetto   = "-0600"
    s.tzname       = "CST"
    s.dtstart      = "19701101T020000"
    s.rrule        = "FREQ=YEARLY;BYMONTH=11;BYDAY=1SU"
  end
end

Output

# BEGIN:VTIMEZONE
# TZID:America/Chicago
# BEGIN:DAYLIGHT
# TZOFFSETFROM:-0600
# TZOFFSETTO:-0500
# TZNAME:CDT
# DTSTART:19700308T020000
# RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
# END:DAYLIGHT
# BEGIN:STANDARD
# TZOFFSETFROM:-0500
# TZOFFSETTO:-0600
# TZNAME:CST
# DTSTART:19701101T020000
# RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
# END:STANDARD
# END:VTIMEZONE

iCalendar has some basic support for creating VTIMEZONE blocks from timezone information pulled from tzinfo. You must require tzinfo support manually to take advantage.

iCalendar has been tested and works with tzinfo versions 0.3, 1.x, and 2.x. The tzinfo-data gem may also be required depending on your version of tzinfo and potentially your operating system.

Example

require 'icalendar/tzinfo'

cal = Icalendar::Calendar.new

event_start = DateTime.new 2008, 12, 29, 8, 0, 0
event_end = DateTime.new 2008, 12, 29, 11, 0, 0

tzid = "America/Chicago"
tz = TZInfo::Timezone.get tzid
timezone = tz.ical_timezone event_start
cal.add_timezone timezone

cal.event do |e|
  e.dtstart = Icalendar::Values::DateTime.new event_start, 'tzid' => tzid
  e.dtend   = Icalendar::Values::DateTime.new event_end, 'tzid' => tzid
  e.summary = "Meeting with the man."
  e.description = "Have a long lunch meeting and decide nothing..."
  e.organizer = "mailto:[email protected]"
  e.organizer = Icalendar::Values::CalAddress.new("mailto:[email protected]", cn: 'John Smith')
end

Parsing iCalendars

# Open a file or pass a string to the parser
cal_file = File.open("single_event.ics")

# Parser returns an array of calendars because a single file
# can have multiple calendars.
cals = Icalendar::Calendar.parse(cal_file)
cal = cals.first

# Now you can access the cal object in just the same way I created it
event = cal.events.first

puts "start date-time: #{event.dtstart}"
puts "start date-time timezone: #{event.dtstart.ical_params['tzid']}"
puts "summary: #{event.summary}"

You can also create a Parser instance directly, this can be used to enable strict parsing:

# Sometimes you want to strongly verify only rfc-approved properties are
# used
strict_parser = Icalendar::Parser.new(cal_file, true)
cal = strict_parser.parse

Parsing Components (e.g. Events)

# Open a file or pass a string to the parser
event_file = File.open("event.ics")

# Parser returns an array of events because a single file
# can have multiple events.
events = Icalendar::Event.parse(event_file)
event = events.first

puts "start date-time: #{event.dtstart}"
puts "start date-time timezone: #{event.dtstart.ical_params['tzid']}"
puts "summary: #{event.summary}"

Finders

Often times in web apps and other interactive applications you'll need to lookup items in a calendar to make changes or get details. Now you can find everything by the unique id automatically associated with all components.

cal = Calendar.new
10.times { cal.event } # Create 10 events with only default data.
some_event = cal.events[5] # Grab it from the array of events

# Use the uid as the key in your app
key = some_event.uid

# so later you can find it.
same_event = cal.find_event(key)

Examples

Check the unit tests for examples of most things you'll want to do, but please send me example code or let me know what's missing.

Download

The latest release version of this library can be found at

Installation

It's all about rubygems:

$ gem install icalendar

Testing

To run the tests:

$ bundle install
$ rake spec

License

This library is released under the same license as Ruby itself.

Support & Contributions

Please submit pull requests from a rebased topic branch and include tests for all bugs and features.

Contributor Code of Conduct

As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.

We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.

Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.

This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.

This Code of Conduct is adapted from the Contributor Covenant, version 1.1.0, available at http://contributor-covenant.org/version/1/1/0/

icalendar-recurrence's People

Contributors

espen avatar jnraine avatar jon-sully avatar mrdougwright avatar paultyng avatar paulusasol avatar rahearn avatar skoushan avatar stephenbinns avatar va7map avatar warmwaffles avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

icalendar-recurrence's Issues

`Time#force_zone` doesn't work

If I type the following In a Rails console:

t = Time.current
t.force_zone("UTC")

I'm getting:

NameError: uninitialized constant Time::TimeUtil

I think that time_util.rb:92 should use the complete namespace: Icalendar::Recurrence::TimeUtil.force_zone(self, tzid).

convert_ice_cube_occurrence should not use "warn"

Currently TZInfo::InvalidTimezoneIdentifier errors are rescued in this method and "warn" is used to output a message. I am using this code in a cron job where I have no influence over the icalendar files I'm processing, and some events have invalid timezone identifiers. The result of this code is that I now get output for each cron run, making the output meaningless. I would rather just handle the errors myself and a way that works for my situation, and I also feel that this would be a good default case here.

The code change would then be to simply remove both the rescue and the warn method. I'd be happy to provide a PR if needed.

Alternatively I suppose the warn could be directed to a logger, or be made optional, but I'm not sure if this if worth the added complexity.

List tzinfo as runtime dependency

Currently, tzinfo is listed as a development dependency.

But from its usage in time_util.rb we can see, it should be a runtime dependency.

This will cause issues, when the gem is used in a plain ruby project, where tzinfo is not required by activesupport for example.

For anyone encountering this issue, you can temporarily fix this by just requiring it alongside this gem yourself:

gem 'icalendar-recurrence'
gem 'tzinfo'

`all_occurrences` doesn't honor exdates across daylight savings transitions

It looks like TimeUtil.to_time is forcing everything to be a Time object with offset calculated. That causes issues for honoring exdate that are across DST boundaries. I think that if active support is available on the system then everything passed to ice cube should likely be TimeWithZone objects.

More information and a reproducible test file is at icalendar/icalendar#221

I'll try to submit a PR for this, but it'll likely be a while until I get to it so if anyone else has the time to tackle it go for it.

Failing test

Failures:

  1. Icalendar::Recurrence::TimeUtil timezone_offset handles daylight savings
    Failure/Error: expect(TimeUtil.timezone_offset("America/Los_Angeles", moment: minute_after_clocks_change)).to eq("-08:00")
   expected: "-08:00"
        got: "-07:00"

   (compared using ==)
 # ./spec/lib/time_util_spec.rb:88:in `block (3 levels) in <top (required)>'

Issues when installing the gem with gemfile

Hi. I just installed icalendar-recurrence today with gemfile:

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
gem "icalendar-recurrence"

with

bundle install

and tried to run the example code

require 'date' # for parse method
require 'icalendar/recurrence'

ics_string = <<-EOF
BEGIN:VCALENDAR
X-WR-CALNAME:Test Public
X-WR-CALID:f512e378-050c-4366-809a-ef471ce45b09:101165
PRODID:Zimbra-Calendar-Provider
VERSION:2.0
METHOD:PUBLISH
BEGIN:VEVENT
UID:efcb99ae-d540-419c-91fa-42cc2bd9d302
RRULE:FREQ=DAILY;INTERVAL=1
SUMMARY:Every day, except the 28th
DTSTART;VALUE=DATE:20140101
DTEND;VALUE=DATE:20140102
STATUS:CONFIRMED
CLASS:PUBLIC
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
TRANSP:TRANSPARENT
LAST-MODIFIED:20140113T200625Z
DTSTAMP:20140113T200625Z
SEQUENCE:0
EXDATE;VALUE=DATE:20140128
END:VEVENT
END:VCALENDAR
EOF

# An event that occurs every day, starting January 1, 2014 with one excluded 
# date. January 28, 2014 will not appear in the occurrences.
calendars = Icalendar.parse(ics_string)
every_day_except_jan_28 = Array(calendars).first.events.first
puts "Every day except January 28, 2014, occurrences from 2014-01-01 to 2014-02-01:"
puts every_day_except_jan_28.occurrences_between(Date.parse("2014-01-01"), Date.parse("2014-02-01"))

and I got the following error:

Traceback (most recent call last):
        2: from c:/Users/MSI GF/Desktop/Swinburne/COS10009/Test/calendar.rb:3:in `<main>'
        1: from C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'
C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require': cannot load such file -- icalendar/recurrence (LoadError)
        9: from c:/Users/MSI GF/Desktop/Swinburne/COS10009/Test/calendar.rb:3:in `<main>'
        8: from C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:156:in `require'
        7: from C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:168:in `rescue in require'
        6: from C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:168:in `require'
        5: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/icalendar-recurrence-1.1.3/lib/icalendar/recurrence.rb:6:in `<top (required)>'
        4: from C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'
        3: from C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'
        2: from C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/icalendar-recurrence-1.1.3/lib/icalendar/recurrence/time_util.rb:1:in `<top (required)>'
        1: from C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'
C:/Ruby27-x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require': cannot load such file -- tzinfo (LoadError)
PS C:\Users\MSI GF\Desktop\Swinburne\COS10009\Test>

I tried to create a new ruby bundle going to a different folder and using bundle init, tried importing different files into the code such as icalendar, rubygems, and tried installing icalendar in gemfile but to no avail. What am I doing wrong here?

Version number needs bumping

Hiya, please could you bump the version number now that there's a fix for convert_duration_to_seconds? Thanks!

Do not fail when rrule is not valid

Using invalid RRULE results in undefined local variable or method rrule' for #Icalendar::Recurrence::Schedule..`

Will provide a failing test later.

Failure to load gem with icalendar 2.1.2

I'm new to ruby, so I could very well be doing something wrong for the gem install or calling the code. Is this expected behavior?

/usr/lib/ruby/2.0.0/rubygems/specification.rb:2007:in `raise_if_conflicts': Unable to activate icalendar-recurrence-0.0.1, because icalendar-2.1.2 conflicts with icalendar (~> 2.0.0.beta.1) (Gem::LoadError)


$ gem query --local | grep -i icalendar
icalendar (2.1.2, 2.0.1)
icalendar-recurrence (0.0.1)

Handle RDATE as specified in RFC 5545

As the gem extends icalendar, which implements RFC 5545, there are three recurrence properties that must be considered to determine all occurrences of a calendar event.

Expected:

Recurrence properties as specified in 3.8.5 Recurrence Component Properties of RFC 5545 are passed on to ice-cube.
These are:

  • RRULE
  • RDATE
  • EXDATE

Actual:

In Icalendar::Recurrence::Schedule#ice_cube_schedule only RRULE and EXDATE are passed on to ice-cube.

Todo:

  • Add spec for testing EXDATE
  • Add call to IceCube::Schedule#add_recurrence_time (similar as used for EXDATE)

Add all_occurrences

Any reason for the requirement to specify occurrences between dates? How about adding all_occurrences?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.