1
votes

I get a string in the format yyyy-MM-dd HH:mm which represents a Date and Time in UTC. Next step is to put this into a Calendar (with TimeZone UTC).

In addition need to also create a separate Calendar which has converted the UTC to "Europe/London" timeZone (GMT/BST).

After that I need to be able to detect if the "Europe/London" calendar has a DST (Day Light Savings Offset).

The code below will show you how far I have got and it runs ok on a UK based computer whose default System Timezone is GMT. However when I run it on a pc whose timezone is UTC it fails. It seems to not be able to tell me if there is an DST_offset (its always zero).

      Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    cal.set(Calendar.YEAR, 2016);
    cal.set(Calendar.MONTH, 1);
    cal.set(Calendar.DAY_OF_MONTH, 27);
    cal.set(Calendar.HOUR_OF_DAY, 23);
    cal.set(Calendar.MINUTE, 35);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);

    //This is required as Java Date JAN starts at 0.
    int MonthCon = cal.get(Calendar.MONTH)-1;
    cal.set(Calendar.MONTH, MonthCon);
    Date d = cal.getTime();



    SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssz");
    f.setTimeZone(TimeZone.getTimeZone("UTC"));
    System.out.println("UTC:    " + f.format(d));
    f.setTimeZone(TimeZone.getTimeZone("Europe/London"));
    System.out.println("BST:    " + f.format(d));

    //Creates a BST calendar of the same UTC time
    String dateStrBST = f.format(d);

    SimpleDateFormat curFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssz");
    curFormater.setTimeZone(TimeZone.getTimeZone("Europe/London"));
    Date dateObjBST = curFormater.parse(dateStrBST);


    System.out.println("BSTNewDate:    " + f.format(dateObjBST));


    Calendar calBST = Calendar.getInstance(TimeZone.getTimeZone("BST"));
    calBST.setTime(dateObjBST);

    System.out.println("Current TimeZone is : " +  calBST.getTimeZone());

    int offset = calBST.get(Calendar.DST_OFFSET);
    System.out.println("Day Light Savings: "+offset);
    System.out.println("Transition Day: "+isDSTTransitionDay(cal.get(Calendar.YEAR),cal.get(Calendar.MONTH),cal.get(Calendar.DAY_OF_MONTH))+"   Transition Type: "+DSTtransitionType(cal.get(Calendar.YEAR),cal.get(Calendar.MONTH),cal.get(Calendar.DAY_OF_MONTH)));

Unfortunately I need to be able to detect if any particular day is a transition day, that is to say a day that changes from DST on/off or off/on. Again this works on the local computer but not on the UTC timezone one.

 private static boolean isDSTTransitionDay(int year, int month, int day) throws ParseException
 {
         Calendar calStartofDay = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
         calStartofDay.set(Calendar.YEAR, year);
         calStartofDay.set(Calendar.MONTH, month);
         calStartofDay.set(Calendar.DAY_OF_MONTH, day);
         calStartofDay.set(Calendar.HOUR_OF_DAY, 00);
         calStartofDay.set(Calendar.MINUTE, 0);
         calStartofDay.set(Calendar.SECOND, 1);            
         Date dStartofDay = calStartofDay.getTime();            

         SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssz");                        
         f.setTimeZone(TimeZone.getTimeZone("Europe/London"));            
         String dateStrUTCtoBST = f.format(dStartofDay);


         SimpleDateFormat curFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssz");
         curFormater.setTimeZone(TimeZone.getTimeZone("Europe/London"));
         Date dateObjBST = curFormater.parse(dateStrUTCtoBST);
         Calendar calBST = Calendar.getInstance();
         calBST.setTime(dateObjBST);            

         int offsetStart = calBST.get(Calendar.DST_OFFSET);

         calBST.add(Calendar.HOUR_OF_DAY, 23);

         int offsetEnd = calBST.get(Calendar.DST_OFFSET);
         //System.out.println("Start: "+offsetStart+" End: "+offsetEnd);




         if (offsetEnd == offsetStart) 
         {
             return false;
         }else
             {
                 //if(offsetStart<offsetEnd) {System.out.println("Transition to BST");}else{System.out.println("Transition to UTC/GMT");};
                 return true;
             }
 }

So on the UTC computer it fails miserably as it always puts Calendar.DST_OFFSET at zero. I have clearly misunderstood something along the way so any help/clarity would be good.

I pretty much have to keep Calendars as the rest of the code uses it but I realise the Java8 has many more fancy ways to do things.

2
AFAIK, UTC doesn't ever have a DST. GMT is not equal to UTC.SlumpA
"UTC computer it fails miserably as it always puts Calendar.DST_OFFSET at zero" its should always be an offset of zero by definition. What do you expect it to do? BTW in French, UTC is defined at 1 in Paris, rather than London.Peter Lawrey
Aside, why are you subtracting one from month after setting all the values? In other words, you are first setting month incorrectly, then correcting. Better set it correctly from the start: cal.set(Calendar.MONTH, 0) or even better cal.set(Calendar.MONTH, Calendar.JANUARY).Ole V.V.

2 Answers

2
votes

Please allow me to be honest, I tried to read your code and didn’t really understand your way of trying to obtain what you want. If you can use Java 8, I recommend switching to using the Java 8 date and time classes. With these your job isn’t very complicated. For the demonstration I have chosen last October 30, the date when Britain (and EU) changed back from summer time (daylight saving time) to standard time.

String originalDate = "2016-10-30 23:35";
LocalDateTime localTime = LocalDateTime.parse(originalDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
ZonedDateTime utcTime = localTime.atZone(ZoneOffset.UTC);
ZonedDateTime bstTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/London"));
// the summer time offset is how many milliseconds?
long dstOffset = ChronoUnit.MILLIS.between(utcTime.toLocalDateTime(), bstTime.toLocalDateTime());
System.out.println(dstOffset); // prints 0

// try the same at start of day (midnight)
utcTime = utcTime.toLocalDate().atStartOfDay(ZoneOffset.UTC);
bstTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/London"));
dstOffset = ChronoUnit.MILLIS.between(utcTime.toLocalDateTime(), bstTime.toLocalDateTime());
System.out.println(dstOffset); // prints 3600000

// and next midnight
utcTime = utcTime.plusDays(1);
bstTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/London"));
dstOffset = ChronoUnit.MILLIS.between(utcTime.toLocalDateTime(), bstTime.toLocalDateTime());
System.out.println(dstOffset); // prints 0
1
votes

You are using

Calendar calBST = Calendar.getInstance();

That sets calBST to the computer's timezone (on the UTC computer, it will be UTC).

calBST.setTime(dateObjBST); sets the time, not the timezone.

Try using getInstance(TimeZone zone) there also.


In any case, I would replace your code like this:

Calendar calStartofDay = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calStartofDay.set(Calendar.YEAR, 2017);
calStartofDay.set(Calendar.MONTH, 0);
calStartofDay.set(Calendar.DAY_OF_MONTH, 21);
calStartofDay.set(Calendar.HOUR_OF_DAY, 00);
calStartofDay.set(Calendar.MINUTE, 0);
calStartofDay.set(Calendar.SECOND, 1);            

Calendar calBST = Calendar.getInstance(TimeZone.getTimeZone("Europe/London"));
calBST.setTimeInMillis(calStartofDay.getTimeInMillis());
// use this to check the time
System.out.printf("%tc%n", calBST);

Also, from the Calendar docs, be careful about this:

set(f, value) changes calendar field f to value. In addition, it sets an internal member variable to indicate that calendar field f has been changed. Although calendar field f is changed immediately, the calendar's time value in milliseconds is not recomputed until the next call to get(), getTime(), getTimeInMillis(), add(), or roll() is made. Thus, multiple calls to set() do not trigger multiple, unnecessary computations. As a result of changing a calendar field using set(), other calendar fields may also change, depending on the calendar field, the calendar field value, and the calendar system. In addition, get(f) will not necessarily return value set by the call to the set method after the calendar fields have been recomputed. The specifics are determined by the concrete calendar class.