Changeset 446

Show
Ignore:
Timestamp:
10/16/07 01:34:11 (15 months ago)
Author:
ogawa
Message:

Add '--output' option to specify the output file.
Add '--compat-google-calendar' option to choose whether RRULE would be compatible with Google Calendar or not.
Support more complicated RRULEs. Thanks for Higuchi-san. (http://higuchi.com/item/394)

Location:
cybozu2ical/trunk
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • cybozu2ical/trunk/cybozu2ical

    r376 r446  
    77use lib 'lib'; 
    88 
    9 use YAML; 
    109use Encode qw/decode_utf8 encode/; 
    1110use WWW::CybozuOffice6::Calendar; 
     
    2019our $VERSION = '0.13'; 
    2120 
     21### 
    2222### TRICK (stop escaping for 'exdate' property) 
     23### 
    2324*Data::ICal::Property::_value_as_string = sub { 
    2425    my $self = shift; 
     
    3839 
    3940}; 
    40 ### END 
    41  
    42 my %opt = (conf => 'config.yaml'); 
    43 GetOptions(\%opt, 'help', 'debug', 'conf=s') or pod2usage(2); 
     41 
     42### 
     43### Utility subroutines 
     44### 
     45sub to_icaldate { 
     46    my($dt, $is_full_day) = @_; 
     47    $is_full_day ? 
     48        $dt->ymd('') : 
     49        $dt->ymd('') . 'T' . $dt->hms('') . ($dt->time_zone->is_utc ? 'Z' : ''); 
     50} 
     51 
     52sub encode_string { 
     53    my($enc, $text) = @_; 
     54    if ($enc eq 'ncr') { 
     55        $text =~ s/(\P{ASCII})/sprintf("&#%d;", ord($1))/eg; 
     56    } else { 
     57        $text = encode($enc, $text); 
     58    } 
     59    $text; 
     60} 
     61 
     62sub read_yaml { 
     63    my $file = shift; 
     64    my $yaml; 
     65    if (eval('require YAML::Tiny')) { 
     66        ($yaml) = YAML::Tiny::LoadFile($file); 
     67    } elsif (eval('require YAML')) { 
     68        ($yaml) = YAML::LoadFile($file); 
     69    } 
     70    if ($@) { 
     71        die "Faild to read yaml file: $@"; 
     72    } 
     73    $yaml; 
     74} 
     75 
     76### 
     77### Main part 
     78### 
     79 
     80# Handle command-line options 
     81my %opt = ( 
     82    conf                        => 'config.yaml', 
     83    'compat-google-calendar'    => 0, 
     84); 
     85GetOptions(\%opt, 
     86           'output=s', 
     87           'conf=s', 
     88           'compat-google-calendar', 
     89           'debug', 
     90           'help', 
     91       ) or pod2usage(2); 
    4492pod2usage(1) if $opt{help}; 
    4593 
    46 my $cfg = YAML::LoadFile($opt{conf}); 
    47  
     94# Read configuration file 
     95my $cfg = read_yaml($opt{conf}); 
    4896my $time_zone = $cfg->{time_zone} || 'Asia/Tokyo'; 
    4997 
     98# Generate a root 'calendar' object 
    5099my $vcalendar = Data::ICal->new(); 
    51100$vcalendar->add_properties( 
     
    57106); 
    58107 
     108# Obtain Cybozu Office 6 Calendar items 
    59109my $cal = WWW::CybozuOffice6::Calendar->new(%$cfg); 
     110 
     111# For each items, generate an 'event entry' and append it to the calendar 
    60112for my $item ($cal->get_items()) { 
    61113    my $vevent = Data::ICal::Entry::Event->new(); 
     
    83135        my $freq = $item->frequency; 
    84136        my %rrules = $freq ne 'WEEKDAYS' ? 
    85             ( FREQ => $freq, WKST => 'SU' ) : 
    86             ( FREQ => 'WEEKLY', WKST => 'SU', BYDAY => 'MO,TU,WE,TH,FR' ); 
     137            ( FREQ => $freq ) : 
     138            ( FREQ => 'WEEKLY', BYDAY => 'MO,TU,WE,TH,FR' ); 
     139        if ($item->frequency_value =~ /^\d(SU|MO|TU|WE|TH|FR|SA)$/) { 
     140            $rrules{BYDAY} = $item->frequency_value; 
     141            $rrules{INTERVAL} = 1; 
     142        } 
    87143        $rrules{UNTIL} = to_icaldate($item->until, $item->is_full_day) 
    88144            if $item->until; 
    89         $args{rrule} = join ';', map { $_ . '=' . $rrules{$_} } keys %rrules; 
     145        $rrules{WKST} = 'SU' 
     146            if $opt{'compat-google-calendar'}; 
     147 
     148        my @rrules; 
     149        for (qw(FREQ COUNT INTERVAL BYMONTH BYMONTHDAY WKST BYDAY UNTIL)) { 
     150            push @rrules, $_ . '=' . $rrules{$_} 
     151                if exists $rrules{$_}; 
     152        } 
     153        $args{rrule} = join ';', @rrules; 
    90154 
    91155        # exdate 
    92156        if ($item->exdates) { 
    93             my $exdate; 
    94157            if ($item->is_full_day) { 
    95                 $exdate = join ',', map { to_icaldate($_, 1) } $item->exdates; 
     158                my $exdate = join ',', map { to_icaldate($_, 1) } $item->exdates; 
    96159                $args{exdate} = [ $exdate, { VALUE => 'DATE' } ]; 
    97160            } else { 
    98                 $exdate = join ',', map { to_icaldate($_, 0) } $item->exdates; 
     161                my $exdate = join ',', map { to_icaldate($_, 0) } $item->exdates; 
    99162                $args{exdate} = [ $exdate, { TZID => $time_zone } ]; 
    100163            } 
     
    110173} 
    111174 
     175# Generate a 'timezone entry' and append it to the calendar 
    112176my $vtimezone = Data::ICal::Entry::TimeZone->new(); 
    113177$vtimezone->add_properties(tzid => $time_zone); 
    114178 
    115 # probably we need to support the Daylight Saving Time 
     179# probably we need to support the Daylight Saving Time, but not yet supported. 
    116180my $standard = Data::ICal::Entry::TimeZone::Standard->new(); 
    117  
    118181my $std = DateTime->new(year => 1970, month => 1, day => 1, 
    119182                        hour => 0, minute => 0, second => 0, 
     
    132195$vcalendar->add_entry($vtimezone); 
    133196 
    134 print encode_($cfg->{output_encoding} || 'utf8', $vcalendar->as_string); 
    135  
    136 sub to_icaldate { 
    137     my($dt, $is_full_day) = @_; 
    138     $is_full_day ? 
    139         $dt->ymd('') : 
    140         $dt->ymd('') . 'T' . $dt->hms('') . ($dt->time_zone->is_utc ? 'Z' : ''); 
    141 } 
    142  
    143 sub encode_ { 
    144     my($enc, $text) = @_; 
    145     if ($enc eq 'ncr') { 
    146         $text =~ s/(\P{ASCII})/sprintf("&#%d;", ord($1))/eg; 
    147     } else { 
    148         $text = encode($enc, $text); 
    149     } 
    150     $text; 
     197# Outputs the calendar as a string 
     198my $text = encode_string($cfg->{output_encoding} || 'utf8', $vcalendar->as_string); 
     199if ($opt{output}) { 
     200    local *FH; 
     201    open FH, ">$opt{output}" or die "Failed to write $opt{output}"; 
     202    print FH $text; 
     203    close FH; 
     204} else { 
     205    print $text; 
    151206} 
    152207 
     
    198253=over 4 
    199254 
     255=item --output /path/to/output.ics 
     256 
     257Specifies the output file.  By default, this application outputs to 
     258STDOUT. 
     259 
    200260=item --conf /path/to/config.yaml 
    201261 
    202 Specifies the path to a configuration file. By default, C<config.yaml> 
    203 in the current directory. 
     262Specifies the configuration file.  By default, C<config.yaml> in the 
     263current directory will be used. 
     264 
     265=item --compat-google-calendar 
     266 
     267To be compatible with Google Calendar. 
     268 
     269=item --debug 
     270 
     271Outputs Cybozu Office 6 CSV data as a COMMENT field of each events. 
     272It is just for debugging. 
     273 
     274=item --help 
     275 
     276Prints out this message. 
    204277 
    205278=back 
  • cybozu2ical/trunk/lib/WWW/CybozuOffice6/Calendar.pm

    r379 r446  
    8787                $param{exdates} = \@exdates; 
    8888            } 
     89            my $freq = $param{freq}; 
     90            if ($freq =~ /^[1-5]$/) { 
     91                $param{freq} = 'm'; 
     92                my @week_str = ('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); 
     93                $param{freq_value} = $freq . $week_str[$param{freq_value}]; 
     94            } 
    8995            $item = WWW::CybozuOffice6::Calendar::RecurrentEvent->new(%param); 
    9096        }