xwss
  1. xwss
  2. XWSS-28

Timestamp in WS-Security layer is one hour out during DST transition

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Blocker Blocker
    • Resolution: Fixed
    • Affects Version/s: current
    • Fix Version/s: milestone 1
    • Component/s: www
    • Labels:
      None
    • Environment:

      Operating System: Solaris
      Platform: Sun

    • Issuezilla Id:
      28

      Description

      Last Saturday morning, my demo system (showing, amongst other things, a WS_Trust
      STS using Metro and its dependencies) worked perfectly correctly. On returning
      to it on Saturday night, messages were being rejected, since their timestamp was
      one hour in the future. After some hours of debugging, I identified the issue as
      com.sun.xml.ws.security.opt.impl.tokens.Timestamp.createDateTime() - repeated
      here for reference:

      public void createDateTime() throws XWSSecurityException {
      if (created == null) {
      Calendar c = new GregorianCalendar();
      int offset = c.get(Calendar.ZONE_OFFSET);
      if (c.getTimeZone().inDaylightTime(c.getTime()))

      { offset += c.getTimeZone().getDSTSavings(); }

      synchronized (calendarFormatter1)

      { calendarFormatter1.setTimeZone(c.getTimeZone()); // always send UTC/GMT time long beforeTime = c.getTimeInMillis(); long currentTime = 0; currentTime = beforeTime - offset; c.setTimeInMillis(currentTime); // finished UTC/GMT adjustment setCreated(calendarFormatter1.format(c.getTime())); c.setTimeInMillis(currentTime + timeout); setExpires(calendarFormatter1.format(c.getTime())); }

      }
      }

      createDateTime() obtains the time in UTC via getTimeInMillis(), then converts it
      to local DST time, then formats it to a string, in UTC.

      Preamble: the JVM was pre-DST change, so this bug became apparent for me on
      Saturday 4/4, when the DST change would have happened before it was moved into
      March. THIS BUG WILL ARISE EVERY YEAR, ON THE EVENING OF THE DST CHANGE!!!

      On Saturday night, c.getTimeZone().inDaylightTime(c.getTime()) was returning
      false, so the offset (I'm in US Pacific time zone) was calculated as -28800000
      (-8 hours). Now, this is subtracted from the UTC time in milliseconds, to
      correct for the fact that calendarFormatter1 has been set to the local timezone.
      When this corrected time is formatted, the calendar formatter thinks it should
      apply DST, since we pushed it 8 hours into the future, across the DST changeover!

      By Sunday morning, the situation had corrected itself, since the current time
      and time + 8 hours were both in DST, according to the JVM.

      This short program shows the bug (on a current JVM that has the correct DST
      changeover) and a fixed createDateTime that does not perform the unnecessary
      conversion to local DST and back (I don't know if I'll be able to create an
      attachment, so I'm just pasting):

      ----BEGIN Main.java----
      package testtime;

      import java.text.ParseException;
      import java.text.SimpleDateFormat;
      import java.util.Calendar;
      import java.util.Date;
      import java.util.GregorianCalendar;
      import java.util.TimeZone;
      import java.util.logging.Level;
      import java.util.logging.Logger;

      /**

      • Extract from com.sun.xml.ws.security.opt.impl.tokens.Timestamp to show DST
      • changeover bug
      • @author Pat Patterson
        */
        public class Main {
        public static final SimpleDateFormat calendarFormatter1
        = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        /**
      • @param args the command line arguments
        */
        public static void main(String[] args)
        Unknown macro: { try { // US DST Change 2008 - Sunday 9 March // Bug will occur late on Saturday night calendarFormatter1.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); Date saturdayNight = calendarFormatter1.parse("2008-03-08T23:00:00Z"); System.out.println("DST Saturday night, buggy:"); createDateTime(saturdayNight.getTime()); System.out.println("DST Saturday night, correct:"); createDateTime2(saturdayNight.getTime()); long timeNow = System.currentTimeMillis(); System.out.println("Now, buggy:"); createDateTime(timeNow); System.out.println("Now, correct:"); createDateTime2(timeNow); } catch (ParseException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } }

      public static void createDateTime(long time) {
      Calendar c = new GregorianCalendar();
      // Set calendar to incoming time - don't need to do this if we're
      working with
      // the real current time
      c.setTimeInMillis(time);

      int offset = c.get(Calendar.ZONE_OFFSET);
      Date date = new Date(time);
      if (c.getTimeZone().inDaylightTime(date))

      { offset += c.getTimeZone().getDSTSavings(); }

      synchronized (calendarFormatter1)

      { calendarFormatter1.setTimeZone(c.getTimeZone()); // always send UTC/GMT time long currentTime = 0; currentTime = time - offset; c.setTimeInMillis(currentTime); // finished UTC/GMT adjustment System.out.println(time + " is " + calendarFormatter1.format(c.getTime())); }

      }

      public static void createDateTime2(long time) {
      TimeZone utc = TimeZone.getTimeZone("UTC");
      Calendar c = new GregorianCalendar(utc);
      // Set calendar to incoming time - don't need to do this if we're
      working with
      // the real current time
      c.setTimeInMillis(time);

      synchronized (calendarFormatter1)

      { calendarFormatter1.setTimeZone(utc); // always send UTC/GMT time c.setTimeInMillis(time); System.out.println(time + " is " + calendarFormatter1.format(c.getTime())); }

      }
      }
      ----END Main.java----

        Activity

        Hide
        superpat7 added a comment -

        You can further simplify my example by moving the
        calendarFormatter1.setTimeZone(utc) call to a static initializer and making the
        GregorianCalendar object static too, since access to it is in the synchronized
        block:

        ----BEGIN Main.java----
        package testtime;

        import java.text.ParseException;
        import java.text.SimpleDateFormat;
        import java.util.Calendar;
        import java.util.Date;
        import java.util.GregorianCalendar;
        import java.util.TimeZone;
        import java.util.logging.Level;
        import java.util.logging.Logger;

        /**

        • Extract from com.sun.xml.ws.security.opt.impl.tokens.Timestamp to show DST
        • changeover bug
        • @author Pat Patterson
          */
          public class Main {
          private static final TimeZone utc = TimeZone.getTimeZone("UTC");
          private static Calendar utcCalendar = new GregorianCalendar(utc);
          private static final SimpleDateFormat calendarFormatter1
          = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
          private static final SimpleDateFormat utcCalendarFormatter1
          = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

        static

        { utcCalendarFormatter1.setTimeZone(utc); }

        /**

        • @param args the command line arguments
          */
          public static void main(String[] args)
          Unknown macro: { try { // US DST Change 2008 - Sunday 9 March // Bug will occur late on Saturday night calendarFormatter1.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); Date saturdayNight = calendarFormatter1.parse("2008-03-08T23:00:00Z"); System.out.println("DST Saturday night, buggy:"); createDateTime(saturdayNight.getTime()); System.out.println("DST Saturday night, correct:"); createDateTime2(saturdayNight.getTime()); long timeNow = System.currentTimeMillis(); System.out.println("Now, buggy:"); createDateTime(timeNow); System.out.println("Now, correct:"); createDateTime2(timeNow); } catch (ParseException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } }

        public static void createDateTime(long time) {
        Calendar c = new GregorianCalendar();
        // Set calendar to incoming time - don't need to do this if we're
        working with
        // the real current time
        c.setTimeInMillis(time);

        int offset = c.get(Calendar.ZONE_OFFSET);
        Date date = new Date(time);
        if (c.getTimeZone().inDaylightTime(date))

        { offset += c.getTimeZone().getDSTSavings(); }

        synchronized (calendarFormatter1)

        { calendarFormatter1.setTimeZone(c.getTimeZone()); // always send UTC/GMT time long currentTime = 0; currentTime = time - offset; c.setTimeInMillis(currentTime); // finished UTC/GMT adjustment System.out.println(time + " is " + calendarFormatter1.format(c.getTime())); }

        }

        public static void createDateTime2(long time) {
        // maybe better style to sync on utcCalendar, since we use it first
        synchronized (utcCalendar)

        { // Using time parameter for this example - would simply use // System.currentTimeMillis() in // com.sun.xml.ws.security.opt.impl.tokens.Timestamp.createDateTime() utcCalendar.setTimeInMillis(time); System.out.println(time + " is " + utcCalendarFormatter1.format(utcCalendar.getTime())); }

        }
        }
        ----END Main.java----

        Show
        superpat7 added a comment - You can further simplify my example by moving the calendarFormatter1.setTimeZone(utc) call to a static initializer and making the GregorianCalendar object static too, since access to it is in the synchronized block: ---- BEGIN Main.java ---- package testtime; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; import java.util.logging.Level; import java.util.logging.Logger; /** Extract from com.sun.xml.ws.security.opt.impl.tokens.Timestamp to show DST changeover bug @author Pat Patterson */ public class Main { private static final TimeZone utc = TimeZone.getTimeZone("UTC"); private static Calendar utcCalendar = new GregorianCalendar(utc); private static final SimpleDateFormat calendarFormatter1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); private static final SimpleDateFormat utcCalendarFormatter1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); static { utcCalendarFormatter1.setTimeZone(utc); } /** @param args the command line arguments */ public static void main(String[] args) Unknown macro: { try { // US DST Change 2008 - Sunday 9 March // Bug will occur late on Saturday night calendarFormatter1.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); Date saturdayNight = calendarFormatter1.parse("2008-03-08T23:00:00Z"); System.out.println("DST Saturday night, buggy:"); createDateTime(saturdayNight.getTime()); System.out.println("DST Saturday night, correct:"); createDateTime2(saturdayNight.getTime()); long timeNow = System.currentTimeMillis(); System.out.println("Now, buggy:"); createDateTime(timeNow); System.out.println("Now, correct:"); createDateTime2(timeNow); } catch (ParseException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } } public static void createDateTime(long time) { Calendar c = new GregorianCalendar(); // Set calendar to incoming time - don't need to do this if we're working with // the real current time c.setTimeInMillis(time); int offset = c.get(Calendar.ZONE_OFFSET); Date date = new Date(time); if (c.getTimeZone().inDaylightTime(date)) { offset += c.getTimeZone().getDSTSavings(); } synchronized (calendarFormatter1) { calendarFormatter1.setTimeZone(c.getTimeZone()); // always send UTC/GMT time long currentTime = 0; currentTime = time - offset; c.setTimeInMillis(currentTime); // finished UTC/GMT adjustment System.out.println(time + " is " + calendarFormatter1.format(c.getTime())); } } public static void createDateTime2(long time) { // maybe better style to sync on utcCalendar, since we use it first synchronized (utcCalendar) { // Using time parameter for this example - would simply use // System.currentTimeMillis() in // com.sun.xml.ws.security.opt.impl.tokens.Timestamp.createDateTime() utcCalendar.setTimeInMillis(time); System.out.println(time + " is " + utcCalendarFormatter1.format(utcCalendar.getTime())); } } } ---- END Main.java ----
        Hide
        kumarjayanti added a comment -

        Pat,

        Thanks for the issue and the fix.

        Show
        kumarjayanti added a comment - Pat, Thanks for the issue and the fix.
        Hide
        ashutoshshahi added a comment -

        Fixed in trunk as well as 1.2 branch of WSIT

        Show
        ashutoshshahi added a comment - Fixed in trunk as well as 1.2 branch of WSIT

          People

          • Assignee:
            xwss-issues
            Reporter:
            superpat7
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: