0
votes

I would like to set java.util.Date object to java.util.Calendar object to change Date object's year as below:

TimezoneId is equal to "Europe/Istanbul".And year is 2018.

Date object is 1969-12-31 22:00:00.0 (1969-12-31T22:00:00.000Z) and zoneinfo is below:

"sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]"

Europe/Istanbul timezone is UTC+3 but when I run code with this date object new GregorianCalendar changes time of date object as -1 hour.Something is working wrongly and I could not find why this not convert UTC to UTC+3.Returned Date object is equals with Sun Dec 31 21:00:00 UTC 2017.UTC-1 is not correct for Europe/Istanbul.How can I fix this ?

public static Date setYearOfDate(Date date, int year, String timeZoneId)
    {
        Calendar customCalendar = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId));
        customCalendar.setTime(date);
        customCalendar.set(Calendar.YEAR, year);

        return customCalendar.getTime();
    }

Note:customCalendar zoneinfo is below: "sun.util.calendar.ZoneInfo[id="Europe/Istanbul",offset=10800000,dstSavings=0,useDaylight=false,transitions=130,lastRule=null]"

2
Which Java version are you using?Rafael Guillen
@RafaelGuillen version is Java8volkan
Istanbul may be on UTC+3 now, but in the past it has been either on UTC+2 or alternating between UTC+2 and UTC+3 for daylight saving time. In 1969 and early parts of 1970, it was UTC+2. See timeanddate.com/time/zone/turkey/…Matt Johnson-Pint
I recommend you don’t use Date and Calendar. Those classes are poorly designed and long outdated. If you really want a date without time of day, use use LocalDate from java.time, the modern Java date and time API. If you want to include time and time zone, use ZonedDateTime from the same API.Ole V.V.
The comment by @MattJohnson-Pint implies that the result you got is correct.Ole V.V.

2 Answers

3
votes

java.time

I recommend you do it using java.time API.

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

class Main {
    public static void main(String[] args) {
        String dateTimeString = "1969-12-31T22:00:00.000Z";
        System.out.println(setYearOfDate(dateTimeString, 2018, "Europe/Istanbul"));
    }

    public static String setYearOfDate(String dateTimeString, int year, String zoneId) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ENGLISH)
                                                    .withZone(ZoneId.of(zoneId));

        ZonedDateTime zdt = ZonedDateTime.parse(dateTimeString);
        zdt = zdt.withYear(year);

        return dtf.format(zdt);
    }
}

Output:

2019-01-01T01:00:00.000+03

The java.util date-time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.

A good example of the erroneous behaviour of SimpleDateFormat is demonstrated below:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;

class Main {
    public static void main(String[] args) throws ParseException {
        String dateTimeString = "1969-12-31T22:00:00.000Z";
        String timeZoneId = "Europe/Istanbul";
        System.out.println(setYearOfDate(dateTimeString, 2018, timeZoneId));
    }

    public static String setYearOfDate(String dateTimeString, int year, String timeZoneId) throws ParseException {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.ENGLISH);
        calendar.setTime(sdf.parse(dateTimeString));
        calendar.set(Calendar.YEAR, year);
        sdf.setTimeZone(TimeZone.getTimeZone(timeZoneId));
        return sdf.format(calendar.getTime());
    }
}

Output:

2019-01-01T02:00:00.000+03

You will observe this behaviour of SimpleDateFormat for the years, 1968 and 1970 as well. However, if you change the year in the input string to some other year, it behaves in the expected manner e.g. the same code will output 2019-01-01T01:00:00.000+03 for the input string, 1979-12-31T22:00:00.000Z.

1
votes

I think @Live_and_Let_Live answer is wright, to print the Date you need to provide the TimeZone to SimpleDateFotmat, actually you don't need to use the SimpleDateFotmat to set the year; using your code and applying the TimeZone correctly you'll get the expected result:

public static void main (String[] args) throws java.text.ParseException
{
    String initialDate = "1969-12-31T22:00:00.000Z";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
    
    Date startDate = sdf.parse(initialDate);
            
    System.out.println("Start date: " + startDate);
    System.out.println("Start date formatted: " + sdf.format(startDate));
    
    Date resultDate = setYearOfDate(startDate, 2018, "Europe/Istanbul");
    System.out.println("Result date: " + resultDate);
    System.out.println("Result date formatted: " + sdf.format(resultDate));
        
    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
    sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Istanbul"));
    System.out.println("Result date formatted with TimeZone: " + sdf2.format(resultDate));
}

Outputs:

Start date: Wed Dec 31 22:00:00 GMT 1969
Start date formatted: 1969-12-31T22:00:00.000Z
Result date: Sun Dec 31 21:00:00 GMT 2017
Result date formatted: 2017-12-31T21:00:00.000Z
Result date formatted with TimeZone: 2018-01-01T00:00:00.000+03

EDIT: Here is the Java 8 Date & Time API alternative:

public static void main(String[] args) {
    String initialDate = "1969-12-31T22:00:00.000Z";
    ZoneId zone = ZoneId.of("Europe/Istanbul");
    DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(zone);
    
    ZonedDateTime startDate = ZonedDateTime.parse(initialDate, formatter);

    System.out.println("Start date: " + startDate);
    System.out.println("Start date formatted with TimeZone: " + formatter.format(startDate));

    ZonedDateTime resultDate = setYearOfDate(startDate, 2018, "Europe/Istanbul");
    System.out.println("Result date: " + resultDate);
    System.out.println("Result date formatted with TimeZone: " + formatter.format(resultDate));
}

static ZonedDateTime setYearOfDate(ZonedDateTime date, int year, String timeZoneId)
{
    GregorianCalendar calendar = GregorianCalendar.from(date);
    calendar.set(GregorianCalendar.YEAR, year); 
    return calendar.toZonedDateTime();
}

Output:

Start date: 1970-01-01T00:00+02:00[Europe/Istanbul]
Start date formatted with TimeZone: 1970-01-01T00:00:00+02:00[Europe/Istanbul]
Result date: 2018-01-01T00:00+03:00[Europe/Istanbul]
Result date formatted with TimeZone: 2018-01-01T00:00:00+03:00[Europe/Istanbul]