javadatetimetimezoneemail-attachmentsinvite

Java Mail invite attachment issue with time zone


beginner here but Im using this example to send a java email invite. If you look at the last example in that link I have that working in my Spring CRUD app when a User creates a meeting an email gets sent with the meeting details and everything is good. The dateTime that is entered is correct in the database, however, the meeting invite is always 1 hour later than entered. I understand it has something to do with TimeZones but looking at the code I have no idea where to begin. So what do I have to change to set the daylight savings time for Berlin Germany?

Heres the code:

public class Index {

public static void main(String[] args) {

     try {
            final String username = "sender@mail.com";
            final String password = "xxxxx";
            
            String from = "sender@mail.com";
            String to = "attendee@mail.com";
            String subject = "Meeting Subject";
            String startDate = "20201208"; // Date Formate: YYYYMMDD
            String endDate = "20201208"; // Date Formate: YYYYMMDD
            String startTime = "0400"; // Time Formate: HHMM
            String endTime = "0600"; // Time Formate: HHMM
            String emailBody = "Hi Team, This is meeting description. Thanks"; 
            
            Properties prop = new Properties();
            prop.put("mail.smtp.auth", "true");
            prop.put("mail.smtp.starttls.enable", "true");
            prop.put("mail.smtp.host", "smtp.gmail.com");
            prop.put("mail.smtp.port", "25");

            Session session = Session.getDefaultInstance(prop,  new javax.mail.Authenticator() {
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
              });
            
            MimeMessage message = new MimeMessage(session);
            message.addHeaderLine("method=REQUEST");
            message.addHeaderLine("charset=UTF-8");
            message.addHeaderLine("component=VEVENT");

            message.setFrom(new InternetAddress(from, "New Outlook Event"));
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
            message.setSubject(subject);

            StringBuffer sb = new StringBuffer();

            StringBuffer buffer = sb.append("BEGIN:VCALENDAR\n" +
                    "PRODID:-//Microsoft Corporation//Outlook 9.0 MIMEDIR//EN\n" +
                    "VERSION:2.0\n" +
                    "METHOD:REQUEST\n" +
                    "BEGIN:VEVENT\n" +
                    "ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:"+ to +"\n" +
                    "DTSTART:"+ startDate +"T"+ startTime +"00Z\n" +
                    "DTEND:"+ endDate +"T"+ endTime +"00Z\n" +
                    "LOCATION:Conference room\n" +
                    "TRANSP:OPAQUE\n" +
                    "SEQUENCE:0\n" +
                    "UID:040000008200E00074C5B7101A82E00800000000002FF466CE3AC5010000000000000000100\n" +
                    " 000004377FE5C37984842BF9440448399EB02\n" +
                    "CATEGORIES:Meeting\n" +
                    "DESCRIPTION:"+ emailBody +"\n\n" +
                    "SUMMARY:Test meeting request\n" +
                    "PRIORITY:5\n" +
                    "CLASS:PUBLIC\n" +
                    "BEGIN:VALARM\n" +
                    "TRIGGER:PT1440M\n" +
                    "ACTION:DISPLAY\n" +
                    "DESCRIPTION:Reminder\n" +
                    "END:VALARM\n" +
                    "END:VEVENT\n" +
                    "END:VCALENDAR");

            // Create the message part
            BodyPart messageBodyPart = new MimeBodyPart();

            // Fill the message
            messageBodyPart.setHeader("Content-Class", "urn:content-classes:calendarmessage");
            messageBodyPart.setHeader("Content-ID", "calendar_message");
            messageBodyPart.setDataHandler(new DataHandler(
                    new ByteArrayDataSource(buffer.toString(), "text/calendar")));// very important

            // Create a Multipart
            Multipart multipart = new MimeMultipart();

            // Add part one
            multipart.addBodyPart(messageBodyPart);

            // Put parts in message
            message.setContent(multipart);

            // send message
            Transport.send(message);
            
            System.out.println("Email sent!");
        } catch (MessagingException me) {
            me.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
}
}

Solution

  • The problem is that both DTSTART and DTEND are being pinned to UTC (as they end with the Z character).

    Here's an excerpt from the original RFC:

      Example:  The following represents July 14, 1997, at 1:30 PM in New
      York City in each of the three time formats, using the "DTSTART"
      property.
    
       DTSTART:19970714T133000                   ; Local time
       DTSTART:19970714T173000Z                  ; UTC time
       DTSTART;TZID=America/New_York:19970714T133000
                                                 ; Local time and time
                                                 ; zone reference
    

    Here is some Java code for both local and timezone supplied start dates:

    final var start = ZonedDateTime.of(LocalDate.of(2020, Month.DECEMBER, 8), LocalTime.of(4, 0), ZoneId.of("Europe/Berlin"));
    final var startDateString = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss").format(start);
    
    // local date
    System.out.println(String.format("DTSTART:%s%n", startDateString));
    
    // timezone specified
    System.out.println(String.format("DTSTART;TZID=%s:%s%n", start.getZone().getId(), startDateString));
    

    DISCLAIMER: I've not tested the above, but it provides a starting point.