none
iCal Exporter Problem

    Question

  • I've downloaded the iCal Exporter from the web site http://blogs.msdn.com/b/sharepoint/archive/2007/05/14/understanding-the-sharepoint-calendar-and-how-to-export-it-to-ical-format.aspx?PageIndex=1#comments and installed. When I tried to export from the sharepoint calendar the error message shows like this: Year, Month, and Day parameters describe an un-representable DateTime.But I have limited knowledge to check the asp.net code. If anyone can help to check and debug the code in asp file as follow, it will be greatful for me. Thanks in advance. Here is the iCal.as p .net page code:

    <%@ Page Language="C#" %>
    <%@ Assembly Name="Microsoft.SharePoint" %>
    <%@ Import Namespace="Microsoft.SharePoint" %>
    <%@ Import Namespace="Microsoft.SharePoint.Utilities" %>
    <%@ Import Namespace="System.Globalization" %>
    
    <script runat="server">
    
      void Page_Load(Object Sender, EventArgs e)
      {
        // Check the URL parameters and return the correct list, or error out if necessary
        SPList list = GetListFromUrl();
        
        // If we got null, return an error to the user
        if (list == null)
        {
          Response.ContentType = "text/html";
          Response.Write("<html><body><h1>Error</h1>Missing or incorrect query parameter: List.</body></html>");
          return;
        }
    
        // Set the correct content type for the document (now that we know it isn't an error)
        Response.ContentType = "text/calendar";
    
        // Write the VCalendar header
        WriteHeader(list);
        
        // Write the timezone section and return the tz alias
        String timeZone = WriteTimeZones();
        
        // Holds GUIDs for recurrence/exception mappings
        Hashtable idToGuid = new Hashtable();
    
        // Loop through each item and write it to the file
        foreach (SPListItem item in list.Items)
        {
          WriteEvent(item, timeZone, idToGuid);
        }
        
        // Wrap up the file
        Write("END", "VCALENDAR");
      }
    
      // Return the SPList object corresponding to the guid passed
      // in the url "list" parameter. Returns null if the url 
      // parameter is missing or if no list with that guid can be found.
      private SPList GetListFromUrl()
      {
        // Check for the List URL parameter
        String listGuid = Request.Params["List"];
    
        // If we didn't pass one, return
        if (listGuid == null)
        {
          return null;
        }
    
        // Turn the passed-in guid into a real one
        Guid g = new Guid(listGuid);
    
        // Get the current SPContext object
        SPContext wssContext = SPContext.GetContext(Context);
    
        // Get the current web
        SPWeb web = wssContext.Web;
    
        SPList list;
    
        try
        {
          // Get the WSS list that corresponds to this guid
          list = web.Lists[g];
        }
        catch (SPException)
        {
          // If the list guid is incorrect, return
          return null;
        }
    
        return list;
      }
    
      // Escape text as needed for compliance with RFC 2445 (Sec 4.3.11)
      private String Escape(Object obj)
      {
        if (obj == null)
          return null;
    
        String text = Convert.ToString(obj);
    
        if (text.Equals(""))
          return null;
        
        text = text.Replace(@"\", "\\\\");
        text = text.Replace(";", "\\;");
        text = text.Replace(",", "\\,");
        text = text.Replace("\n", "\\n");
        return text;
      }
      
      private void Write(String propName, Object value)
      {
        int i;
        
        if (value == null)
          return;
        
        String line = propName + ":" + Convert.ToString(value);
        
        // Fold the line after every 70 chars (RFC 2445 Sec 4.1)
        for (i = 69; i < line.Length; i = i + 69)
        {
          line.Insert(i, "\r\n ");
        }
        
        // Write the line out
        Response.Write(line + "\r\n");
      }
    
      private void WriteHeader(SPList list)
      {
        // vCalendar header
        Write("BEGIN", "VCALENDAR");
        Write("VERSION", "2.0");
    
        // List name
        Write("X-WR-CALNAME", Escape(list.Title));
    
        // Product id -- ?
        //Write("PRODID", "");
    
        // Calendar scale -- only Gregorian is supported? (RFC 2445 Section 4.7.1)
        Write("CALSCALE", "GREGORIAN");
    
        // Method
        Write("METHOD", "PUBLISH");
      }
    
      // Returns the day at which the timezone specified by tzRule began in the year (this + yearOffset)
      private DateTime TimeZoneStart(SPSystemTime tzRule, int yearOffset)
      {
        // Start at day one of the month
        DateTime startDate = new DateTime(
          SPUtility.GetServerNow(SPContext.Current.Web).AddYears(yearOffset).Year,
          tzRule.Month,
          1,
          tzRule.Hour,
          tzRule.Minute,
          tzRule.Second);
    
        // Offset from the first ? to the first Mon/Tues/Wed, depending on the day we want to end up on
        int numToAdd = Convert.ToInt32(tzRule.DayOfWeek) - Convert.ToInt32(startDate.DayOfWeek);
    
        // Go to the first instance of the correct day of the week
        startDate = startDate.Add(new TimeSpan(numToAdd, 0, 0, 0));
    
        // Go to the correct occurrence of the day
        startDate = startDate.Add(new TimeSpan(7 * tzRule.Day, 0, 0, 0));
    
        return startDate;
      }
      
      // Turns RecurrenceData options into value for which day of the month (-1, 1, 2, 3, 4)
      private int GetWeekdayOfMonthValue(string options)
      {
        int i = 0;
        
        if (options.Contains(@"weekdayofmonth=""first"""))
          i = 1;
        else if (options.Contains(@"weekdayofmonth=""second"""))
          i = 2;
        else if (options.Contains(@"weekdayofmonth=""third"""))
          i = 3;
        else if (options.Contains(@"weekdayofmonth=""fourth"""))
          i = 4;
        else if (options.Contains(@"weekdayofmonth=""last"""))
          i = -1;
        
        return i;
      }
      
    
      // Turns RecurrenceData field XML into iCalendar recurrence string
      private String ParseRecurrence(String recurrence)
      {
        String recurValue;
    
        // Normalize the recurrence XML
        recurrence = recurrence.ToLower();
    
        // Build a regex to parse out the pattern
        Regex pattern = new Regex(@"<repeat><(?<FREQ>\w+)\s(?<OPTIONS>.*)/></repeat>");
    
        // Match against the XML
        Match match = pattern.Match(recurrence);
    
        // Pull out the recurrence type and associated options
        String freq = match.Groups["FREQ"].ToString().ToUpper();
        String options = match.Groups["OPTIONS"].ToString().ToLower();
        String byday = null;
    
        // Is there a weekly="true" flag in the options?
        if (freq.Equals("DAILY") && options.Contains(@"weekday=""true"""))
        {
          // There's no support for daily-weekday patterns in this format, so we have to do some mangling
          freq = "WEEKLY";
          byday = "MO,TU,WE,TH,FR";
        }
        
        // Put the frequency into the return value, but handle special cases for tricky month/year patterns
        if (freq.Equals("MONTHLYBYDAY"))
        {
          recurValue = "FREQ=MONTHLY";
        }
        else if (freq.Equals("YEARLYBYDAY"))
        {
          recurValue = "FREQ=YEARLY";
        }
        else
        {
          recurValue = "FREQ=" + freq;
        }
    
        // Build a regex to parse out the interval
        pattern = new Regex(@"(day|week|month|year)frequency=""(?<INTERVAL>\d+)""");
    
        // Match against the XML
        match = pattern.Match(options);
    
        // Pull out the interval
        String interval = match.Groups["INTERVAL"].ToString();
    
        // If we found an interval, add it to the recur pattern
        if (!interval.Equals(""))
        {
          recurValue = recurValue + ";INTERVAL=" + interval;
        }
        
        // Include the starting day of the week if we're a weekly pattern
        if (freq.Equals("WEEKLY"))
        {
          // Only set up the byday list if we didn't already mangle it to support weekday recurrences
          if (byday == null)
          {
            // Initialize it so it's no longer null
            byday = "";
    
            // The list of day values we're going to scan for
            String[] days = new String[] { "SU", "MO", "TU", "WE", "TH", "FR", "SA" };
    
            foreach (String d in days)
            {
              // Is this day set in the recurrence options?
              if (options.Contains(d.ToLower() + @"=""true"""))
              {
                // Add a comma if we're sticking our value on the end of an existing one
                if (!byday.Equals(""))
                {
                  byday = byday + ",";
                }
    
                // Add it to the list
                byday = byday + d;
              }
            }
          }
    
          // Add the day pattern to the recur pattern
          recurValue = recurValue + ";BYDAY=" + byday;
          
          // Build a regex to parse out the first day of the week
          pattern = new Regex(@"<firstdayofweek>(?<FIRST>\w\w)</firstdayofweek>");
    
          // Match against the XML
          match = pattern.Match(recurrence);
    
          // Pull out the first day of the week
          String first = match.Groups["FIRST"].ToString().ToUpper();
    
          // Add it to the recur pattern
          recurValue = recurValue + ";WKST=" + first;
        }
        
        // Parse out monthly-specific options
        if (freq.Equals("MONTHLY"))
        {
          // Build a regex to parse out the day in a "every N months on the Nth" pattern
          pattern = new Regex(@"\sday=""(?<BYDAY>\d+)""");
    
          // Match against the pattern options XML
          match = pattern.Match(options);
    
          // Pull out the by day option
          String byDay = match.Groups["BYDAY"].ToString().ToUpper();
          
          // Add it to the recur pattern
          if (!byDay.Equals(""))
          {
            recurValue = recurValue + ";BYMONTHDAY=" + byDay;
          }
        }
    
        // Parse out yearly-specific options
        if (freq.Equals("YEARLY"))
        {
          // Build a regex to parse out the month in a "every N (month) Nrd" pattern
          pattern = new Regex(@"\smonth=""(?<BYMONTH>\d+)""");
    
          // Match against the pattern options XML
          match = pattern.Match(options);
    
          // Pull out the by month option
          String byMonth = match.Groups["BYMONTH"].ToString().ToUpper();
    
          // Add it to the recur pattern
          if (!byMonth.Equals(""))
          {
            recurValue = recurValue + ";BYMONTH=" + byMonth;
          }
        }
    
        // Parse out more complex monthly-and-yearly-specific options
        if (freq.Equals("MONTHLYBYDAY") || freq.Equals("YEARLYBYDAY"))
        {
          // Parse out the weekdayOfMonth property into an index we can use
          int i = GetWeekdayOfMonthValue(options);
    
          // Is this a yearly pattern?
          if (freq.Equals("YEARLYBYDAY"))
          {
            // Yes, so parse out the month that it's supposed to occur in
            pattern = new Regex(@"\smonth=""(?<MONTH>\d+)""");
    
            // Match against the XML
            match = pattern.Match(options);
    
            // Pull out the number of occurrences
            String monthIndex = match.Groups["MONTH"].ToString();
    
            // Add it to the recurrence pattern
            recurValue = recurValue + ";BYMONTH=" + monthIndex;
          }
    
          // Is this a "the Nth day" pattern?
          if (options.StartsWith(@"day=""true"""))
          {
            recurValue = recurValue + ";BYMONTHDAY=" + i;
          }
          
          // Is this a "the Nth weekday" pattern?
          else if (options.Contains(@"weekday=""true"""))
          {
            recurValue = recurValue + ";BYDAY=MO,TU,WE,TH,FR;BYSETPOS=" + i;
          }
    
          // Is this a "the Nth weekend day" pattern?
          // NOTE: this only works if the month doesn't start on a Sunday. Since the
          // spec doesn't seem to support this pattern, I have to make it up.
          else if (options.Contains(@"weekend_day=""true"""))
          {
            recurValue = recurValue + ";BYDAY=SU,SA;BYSETPOS=" + i;
          }
    
          // This must be a "the Nth D (day name)" pattern, then...
          else
          {
            // The list of day values we're going to scan for
            String[] days = new String[] { "SU", "MO", "TU", "WE", "TH", "FR", "SA" };
    
            foreach (String d in days)
            {
              // Is this day set in the recurrence options?
              if (options.Contains(d.ToLower() + @"=""true"""))
              {
                // It is -- and there can be only one of them -- so tack it on and exit the loop
                recurValue = recurValue + ";BYDAY=" + i + d;
                break;
              }
            }
          }
        }
    
        // Build a regex to parse out the number of occurrences
        pattern = new Regex(@"<repeatinstances>(?<REPEAT>\d+)</repeatinstances>");
    
        // Match against the XML
        match = pattern.Match(recurrence);
    
        // Pull out the number of occurrences
        String repeat = match.Groups["REPEAT"].ToString();
    
        // If we found a number of occurrences, add it to the recur pattern
        if (!repeat.Equals(""))
        {
          recurValue = recurValue + ";COUNT=" + repeat;
        }
    
        // Build a regex to parse out the end by date
        pattern = new Regex(@"<windowend>(?<ENDDATE>.+)</windowend>");
    
        // Match against the XML
        match = pattern.Match(recurrence);
    
        // Pull out the end date
        String endDate = match.Groups["ENDDATE"].ToString();
    
        // If we found a value, add it to the recur pattern
        if (!endDate.Equals(""))
        {
          DateTime endDT = Convert.ToDateTime(endDate);
          recurValue = recurValue + ";UNTIL=" + endDT.ToUniversalTime().ToString("yyyyMMddTHHmmssZ");
        }
    
        return recurValue;
      }
    
      private SPListItem GetFirstInstance(SPListItem item)
      {
        DateTime itemStartDate = (DateTime)item["EventDate"];
        SPQuery query = new SPQuery();
        query.ExpandRecurrence = true;
        query.Query = "<Where><DateRangesOverlap><FieldRef Name=\"EventDate\" /><FieldRef Name=\"EndDate\" /><FieldRef Name=\"RecurrenceID\" /><Value Type=\"DateTime\"><Month /></Value></DateRangesOverlap></Where>";
        SPListItem i = null;
        int monthOffset = 0;
        SPListItemCollection allItems;
    
        // Look up to a little over a month ahead
        while (monthOffset < 14)
        {
          // Start from (start date + offset*30) and look ahead a month
          query.CalendarDate = ((DateTime)item["EventDate"]).AddMonths(monthOffset);
          
          // Get all the items
          allItems = item.ParentList.GetItems(query);
          
          // These items are sorted earliest to latest by start date
          foreach (SPListItem expandedItem in allItems)
          {
            // Did we find an instance that matches our series ID?
            if (expandedItem.ID == item.ID)
            {
              return expandedItem;
            }
          }
    
          // If we didn't find anything, look in the next month
          monthOffset++;
        }
        
        // Return null if we looked all over and couldn't find anything.
        return null;
      }
    
      private ArrayList GetDeletedInstancesOfSeries(SPListItem item)
      {
        ArrayList deletedInstances = new ArrayList();
    
        // Get the ID of the master series
        int id = item.ID;
        
        // Loop through each item in the list
        foreach (SPListItem possibleItem in item.ParentList.Items)
        {
          // Find all items marked as a deleted recurrence item with a matching master series ID 
          if (((bool)possibleItem["Recurrence"])
            && (((Int32)possibleItem["Event Type"]) == 3)
            && (((int)possibleItem["MasterSeriesItemID"]) == id))
          {
            deletedInstances.Add(possibleItem);
          }
        }
    
        return deletedInstances;
      }
    
      private String WriteTimeZones()
      {
        String year, month, day, hour, minute, second;
        
        // Get the timezone in the web
        SPTimeZone tz = SPContext.Current.Web.RegionalSettings.TimeZone;
        
        // Get the timezone description
        String tzid = "tz"; // Escape(tz.Description);
    
        // If the bias from 'now' to standard time is 0, then we're in standard time
        bool isStandardTime = (tz.Information.StandardBias == 0);
    
        // Get the date that standard time began last and this year
        DateTime stdStartLastYear = TimeZoneStart(tz.Information.StandardDate, -1);
        DateTime stdStartThisYear = TimeZoneStart(tz.Information.StandardDate, 0);
    
        // Get the date that daylight time begins this year and next
        DateTime dayStartThisYear = TimeZoneStart(tz.Information.DaylightDate, 0);
        DateTime dayStartNextYear = TimeZoneStart(tz.Information.DaylightDate, 1);
    
        // Get the time that the server thinks it is
        DateTime now = SPUtility.GetServerNow(SPContext.Current.Web);
    
        // Get the date that standard time and daylight time begin
        DateTime standardBegin, daylightBegin;
    
        if (isStandardTime)
        {
          // If we're in standard time, we need to return when the current std time started.
          // This may have been just a few months ago (if we're in Nov/Dev), or last year
          // (if we're in Jan/Feb). If the std time start date is ahead of us, then we want the one
          // for last year
          standardBegin = now.CompareTo(stdStartThisYear) <= 0 ? stdStartLastYear : stdStartThisYear;
    
          // If we're in standard time, we need to return when the next daylight time starts.
          // This may be in just a few months (if we're in Jan/Feb) or next year (if we're in Nov/Dec).
          // If we haven't gotten to the standard time start for this year yet, we want this year's daylight time.
          daylightBegin = now.CompareTo(stdStartThisYear) <= 0 ? dayStartThisYear : dayStartNextYear;
        }
        else
        {
          // We're in daylight time. Std time start is always ahead of us.
          standardBegin = stdStartThisYear;
    
          // We're in daylight time. It just started!
          daylightBegin = dayStartThisYear;
        }
    
        // Get the standard time bias (480 minutes -> 8 hours 'til UTC -> -8 hours behind)
        String standardBias = Convert.ToString((tz.Information.StandardBias == 0 ?
          tz.Information.Bias : tz.Information.Bias + tz.Information.StandardBias) / 60 * -1);
        
        // Pad correctly
        if (standardBias.Length == 1)
        {
          standardBias = standardBias.Insert(0, "+0");
        }
        else if ((standardBias.Length == 2) && (standardBias.Substring(0, 1).Equals("-")))
        {
          standardBias = standardBias.Insert(1, "0");
        }
        else if (standardBias.Length == 2)
        {
          standardBias = standardBias + "+";
        }
    
        standardBias = standardBias + "00";
    
        // Get the daylight time bias
        String daylightBias = Convert.ToString((tz.Information.DaylightBias == 0 ?
          tz.Information.Bias : tz.Information.Bias + tz.Information.DaylightBias) / 60 * -1);
    
        // Pad correctly
        if (daylightBias.Length == 1)
        {
          daylightBias = daylightBias.Insert(0, "+0");
        }
        else if ((daylightBias.Length == 2) && (daylightBias.Substring(0, 1).Equals("-")))
        {
          daylightBias = daylightBias.Insert(1, "0");
        }
        else if (daylightBias.Length == 2)
        {
          daylightBias = standardBias + "+";
        }
    
        daylightBias = daylightBias + "00";
        
        // Timezone header
        Write("BEGIN", "VTIMEZONE");
        
        // Timezone ID (to identify this timezone later in the document)
        Write("TZID", tzid);
    
        // Last modified
        Write("LAST-MODIFIED", DateTime.Now.ToUniversalTime().ToString("yyyyMMddTHHmmssZ"));
        
        // Standard time header
        Write("BEGIN", "STANDARD");
        
        // Date and time this timezone started (if we're currently in it) or will start (if we're not in it)
        Write("DTSTART", standardBegin.ToString("yyyyMMddTHHmmss"));
        
        // Timeshift during this timezone
        Write("TZOFFSETTO", standardBias);
        
        // Timeshift outside of this timezone -- ternary to match iCal output
        Write("TZOFFSETFROM", isStandardTime ? "+0000" : daylightBias);
        
        // Standard time footer
        Write("END", "STANDARD");
    
        // Daylight time header
        Write("BEGIN", "DAYLIGHT");
    
        // Date and time this timezone started (if we're currently in it) or will start (if we're not in it)
        Write("DTSTART", daylightBegin.ToString("yyyyMMddTHHmmss"));
    
        // Timeshift during this timezone
        Write("TZOFFSETTO", daylightBias);
    
        // Timeshift outside of this timezone
        Write("TZOFFSETFROM", !isStandardTime ? "+0000" : standardBias);
    
        // Daylight time footer
        Write("END", "DAYLIGHT");
    
        // Timezone footer
        Write("END", "VTIMEZONE");
        
        return tzid;
      }
    
      private void WriteEvent(SPListItem item, String timeZone, Hashtable idToGuid)
      {
        bool allDay = (bool)item["All Day Event"];
        bool recur = ((bool)item["Recurrence"]) && (((Int32)item["Event Type"]) == 1);
        bool exception = (((Int32)item["Event Type"]) == 4);
        bool deleted = (((Int32)item["Event Type"]) == 3);
        String guid = ((String)item["GUID"]).Trim(new char[] {'{', '}'});
        DateTime created = Convert.ToDateTime(item["Created"]);
        String url = SPContext.Current.Web.Url + item.ParentList.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl + "?ID=" + item.ID;
        DateTime start = Convert.ToDateTime(item["EventDate"]);
        DateTime end = Convert.ToDateTime(item["EndDate"]);
        String recurValue = null;
        
        // Skip deleted instances, since they get handled when the master series is written out
        if (deleted)
          return;
    
        // Special handling for recurrences
        if (recur)
        {
          Write("type", item["Event Type"]);
          
          // Get the item's recurrence XML
          String recurrence = ((String)item["RecurrenceData"]);
    
          // Parse it and generate a recurrence value
          recurValue = ParseRecurrence(recurrence);
    
          // We can't rely on the start date of the master, so get the first instance of the series
          SPListItem rItem = GetFirstInstance(item);
    
          // Did we find an instance?
          if (rItem == null)
          {
            // We couldn't find an instance of this recurrence, so let's fudge and call it a normal event
            recur = false;
          }
          else
          {
            // Correct the start date and calculate the end date by adding the duration to the start date
            start = Convert.ToDateTime(rItem["EventDate"]);
            end = start.AddSeconds((int)rItem["Duration"]);
          }
        }
        
        // Event header
        Write("BEGIN", "VEVENT");
    
        // Title
        Write("SUMMARY", Escape(item.Title));
    
        // Location
        Write("LOCATION", Escape(item["Location"]));
    
        // Description
        Write("DESCRIPTION", Escape(item.Fields["Description"].GetFieldValueAsText(item["Description"])));
        
        // Created date
        Write("DTSTAMP", created.ToString("yyyyMMddTHHmmssZ"));
        
        // Unique ID
        if (recur)
        {
          // Add the ID-to-GUID mapping for later, but only if it's a recurrence
          idToGuid.Add(item.ID, guid);
        }
        else if (exception)
        {
          // Replace this item's ID with the ID of the master series if it's an exception
          guid = (String)idToGuid[item["MasterSeriesItemID"]];
        }
        
        Write("UID", guid);
        
        // Sequence (aka version)
        Write("SEQUENCE", Convert.ToString(item["Version"]).Replace(".",""));
    
        // URL
        Write("URL;VALUE=URI", url);
    
        // Duration if we're an all-day event
        if (allDay)
        {
          // All-day events always end at :59, so we have to add one minute for this to work correctly
          Write("DURATION", "P" + ((TimeSpan)(end.AddMinutes(1) - start)).Days + "D");
        }
        
        // Start date (different format if all-day event)
        if (allDay)
        {
          Write("DTSTART;VALUE=DATE", start.ToString("yyyyMMdd"));
        }
        else
        {
          Write("DTSTART;TZID=" + timeZone, start.ToString("yyyyMMddTHHmmss"));
        }
     
        // End date (only if not an all-day event)
        if (!allDay)
        {
          Write("DTEND;TZID=" + timeZone, end.ToString("yyyyMMddTHHmmss"));
        }
        
        // Time of the instance we replaced, if we're an exception
        if (exception)
        {
          Write("RECURRENCE-ID;TZID=" + timeZone, (Convert.ToDateTime(item["Recurrence ID"])).ToString("yyyyMMddTHHmmss"));
        }
    
        // Recurrence rule and deleted instances
        if (recur)
        {
          Write("RRULE", recurValue);
    
          // Get all the deleted instances of this series
          ArrayList deletedInstances = GetDeletedInstancesOfSeries(item);
    
          // Loop through them
          foreach (SPListItem deletedInstance in deletedInstances)
          {
            // Write out an exception rule for each one
            Write("EXDATE;TZID=" + timeZone, (Convert.ToDateTime(deletedInstance["Recurrence ID"])).ToString("yyyyMMddTHHmmss"));
          }
        }
    
        // Event footer
        Write("END", "VEVENT");
      }
      
    </script>
    
    
    

    Thursday, October 14, 2010 6:50 AM