| 53 | | my $item = $fields[7] ? |
| 54 | | $this->_parse_recurrent_event(@fields) : |
| 55 | | $this->_parse_general_event(@fields); |
| 56 | | push @items, $item if $item; |
| | 53 | # Cybozu Calendar CSV Format |
| | 54 | # GENERIC | RECCURENT |
| | 55 | # [ 0] id? | id? |
| | 56 | # [ 1] created | created |
| | 57 | # [ 2] <BLANK> x start_date |
| | 58 | # [ 3] start_date x end_date |
| | 59 | # [ 4] end_date x until_date |
| | 60 | # [ 5] start_time | start_time |
| | 61 | # [ 6] end_time | end_time |
| | 62 | # [ 7] <BLANK> | freq |
| | 63 | # [ 8] <BLANK> | freq_value |
| | 64 | # [ 9] ??? | ??? |
| | 65 | # [10] ??? | ??? |
| | 66 | # [11] abbrev | abbrev |
| | 67 | # [12] summary | summary |
| | 68 | # [13] description | description |
| | 69 | |
| | 70 | my %param; |
| | 71 | @param{qw(created start_time end_time freq freq_value abbrev summary description)} = @fields[1,5..8,11..13]; |
| | 72 | $param{created} =~ s/^ts\.//; |
| | 73 | $param{time_zone} = $this->{time_zone} || 'Asia/Tokyo'; |
| | 74 | |
| | 75 | my $item; |
| | 76 | if (!$param{freq}) { |
| | 77 | @param{qw(start_date end_date)} = @fields[3,4]; |
| | 78 | $item = WWW::CybozuOffice6::Calendar::Event->new(%param); |
| | 79 | } else { |
| | 80 | @param{qw(start_date end_date until_date)} = @fields[2..4]; |
| | 81 | $item = WWW::CybozuOffice6::Calendar::RecurentEvent->new(%param); |
| | 82 | } |
| | 83 | |
| | 84 | next unless $item; |
| | 85 | $item->comment($line); # save the CSV line as for debug info. |
| | 86 | push @items, $item; |
| 74 | | # handle non-recurrent events |
| 75 | | sub _parse_general_event { |
| 76 | | my $this = shift; |
| 77 | | my @fields = @_; |
| 78 | | |
| 79 | | my $now = DateTime->now; |
| 80 | | my $is_full_day = 0; |
| 81 | | |
| 82 | | my $start = $this->to_datetime($fields[3], $fields[5]); |
| 83 | | my $end = $this->to_datetime($fields[4], $fields[6]); |
| | 104 | package WWW::CybozuOffice6::Calendar::Event; |
| | 105 | |
| | 106 | sub new { |
| | 107 | my $class = shift; |
| | 108 | my $self = { |
| | 109 | is_full_day => 0, |
| | 110 | # modified => DateTime->now, |
| | 111 | }; |
| | 112 | bless $self, $class; |
| | 113 | return unless $self->parse(@_); |
| | 114 | $self; |
| | 115 | } |
| | 116 | |
| | 117 | sub start { shift->_accessor('start', @_) } |
| | 118 | sub end { shift->_accessor('end', @_) } |
| | 119 | sub summary { shift->_accessor('summary', @_) } |
| | 120 | sub description { shift->_accessor('description', @_) } |
| | 121 | sub created { shift->_accessor('created', @_) } |
| | 122 | sub modified { shift->_accessor('modified', @_) } |
| | 123 | sub is_full_day { shift->_accessor('is_full_day', @_) } |
| | 124 | sub comment { shift->_accessor('comment', @_) } |
| | 125 | sub _accessor { |
| | 126 | my $this = shift; |
| | 127 | my $key = shift; |
| | 128 | $this->{$key} = shift if @_; |
| | 129 | $this->{$key}; |
| | 130 | } |
| | 131 | |
| | 132 | sub parse { |
| | 133 | my($this, %param) = @_; |
| | 134 | |
| | 135 | $this->{time_zone} = $param{time_zone} || 'Asia/Tokyo'; |
| | 136 | |
| | 137 | my $start = $this->to_datetime($param{start_date}, $param{start_time}); |
| | 138 | my $end = $this->to_datetime($param{end_date}, $param{end_time}); |
| 92 | | |
| 93 | | my($created) = $fields[1] =~ m/^ts\.(\d+)$/; |
| 94 | | $created = DateTime->from_epoch(epoch => $created || 0); |
| 95 | | |
| 96 | | my $summary = $fields[11] || ''; |
| 97 | | $summary .= ': ' if $summary; |
| 98 | | $summary .= $fields[12] || ''; |
| 99 | | |
| 100 | | my $item = { |
| 101 | | start => $start, |
| 102 | | end => $end, |
| 103 | | is_full_day => $is_full_day, |
| 104 | | summary => $summary, |
| 105 | | description => $fields[13] || $summary, |
| 106 | | created => $created, |
| 107 | | modified => $now, |
| 108 | | }; |
| 109 | | } |
| 110 | | |
| 111 | | # handle recurrent events |
| 112 | | our %FREQUENCY = ( y => 'YEARLY', m => 'MONTHLY', w => 'WEEKLY', |
| 113 | | d => 'DAILY', n => 'WEEKDAYS' ); |
| 114 | | sub _parse_recurrent_event { |
| 115 | | my $this = shift; |
| 116 | | my @fields = @_; |
| 117 | | |
| 118 | | # arrange for _parse_general_event |
| 119 | | my @f = @fields; |
| 120 | | $f[4] = $f[3]; |
| 121 | | $f[3] = $f[2]; |
| 122 | | my $item = $this->_parse_general_event(@f); |
| 123 | | |
| 124 | | # frequency |
| 125 | | my $freq = $fields[7]; |
| 126 | | if ($freq && exists $FREQUENCY{$freq}) { |
| 127 | | $item->{frequency} = $FREQUENCY{$freq}; |
| 128 | | $item->{frequency_value} = $fields[8] || 0; |
| 129 | | if ($fields[4] =~ m!^(\d+)/(\d+)/(\d+)$!) { |
| 130 | | my %args = (year => $1, month => $2, day => $3); |
| 131 | | my $until; |
| 132 | | if ($item->{is_full_day}) { |
| 133 | | $until = $this->to_datetime($fields[4], ':'); |
| 134 | | } else { |
| 135 | | $until = $item->{end}->clone->set(%args); |
| 136 | | $until->set_time_zone('UTC'); # timezone must be UTC |
| 137 | | } |
| 138 | | $item->{until} = $until; |
| 139 | | } |
| 140 | | } |
| 141 | | |
| 142 | | $item; |
| 143 | | } |
| 144 | | |
| | 150 | $this->{start} = $start; |
| | 151 | $this->{end} = $end; |
| | 152 | |
| | 153 | $this->{created} = DateTime->from_epoch(epoch => $param{created} || 0); |
| | 154 | $this->{modified} = DateTime->from_epoch(epoch => $param{created} || 0); |
| | 155 | |
| | 156 | my $summary = ($param{abbrev} ? $param{abbrev} . ': ' : '') . $param{summary}; |
| | 157 | $this->{summary} = $summary; |
| | 158 | $this->{description} = $param{description} || $summary; |
| | 159 | 1; |
| | 160 | } |
| | 161 | |
| | 162 | # convert (ymd, hms) pair to a DateTime object (timezone: localtime) |
| | 182 | } |
| | 183 | |
| | 184 | package WWW::CybozuOffice6::Calendar::RecurrentEvent; |
| | 185 | |
| | 186 | @WWW::CybozuOffice6::Calendar::RecurrentEvent::ISA = qw( WWW::CybozuOffice6::Calendar::Event ); |
| | 187 | |
| | 188 | sub frequency { shift->_accessor('frequency', @_) } |
| | 189 | sub frequency_value { shift->_accessor('frequency_value', @_) } |
| | 190 | |
| | 191 | our %FREQUENCY = ( y => 'YEARLY', m => 'MONTHLY', w => 'WEEKLY', |
| | 192 | d => 'DAILY', n => 'WEEKDAYS' ); |
| | 193 | sub parse { |
| | 194 | my($this, %param) = @_; |
| | 195 | $this->SUPER::parse(%param); |
| | 196 | |
| | 197 | # frequency |
| | 198 | my $freq = $param{freq}; |
| | 199 | return unless $freq && exists $FREQUENCY{$freq}; |
| | 200 | |
| | 201 | $this->{frequency} = $FREQUENCY{$freq}; |
| | 202 | $this->{frequency_value} = $param{freq_value} || 0; |
| | 203 | |
| | 204 | if ($param{until_date} =~ m!^(\d+)/(\d+)/(\d+)$!) { |
| | 205 | my %args = (year => $1, month => $2, day => $3); |
| | 206 | my $until; |
| | 207 | if ($this->{is_full_day}) { |
| | 208 | $until = $this->to_datetime($param{until_date}, ':'); |
| | 209 | } else { |
| | 210 | $until = $this->{end}->clone->set(%args); |
| | 211 | $until->set_time_zone('UTC'); # timezone must be UTC |
| | 212 | } |
| | 213 | $this->{until} = $until; |
| | 214 | } |
| | 215 | 1; |