Maxim Blumental
Maxim Blumental

Reputation: 743

Convert milliseconds to date string back and forth using java 7

I have the following code which uses all recommendations discussed in similar questions.

public class DateUtils {
static String secondsToDate(String seconds) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(Long.parseLong(seconds) * 1000);
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DAY_OF_MONTH);

    return String.format("%d-%d-%d", year, month, day);
}

static String dateToSeconds(String date) {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    try {
        Date parsed = format.parse(date);
        long timeInMillis = parsed.getTime();
        return Long.toString(timeInMillis / 1000);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    return null;
}

public static void main(String[] args) {
    String timestamp = "1409515200";
    String date = secondsToDate(timestamp);
    String timestamp2 = dateToSeconds(date);
    System.out.printf("%s  %s", timestamp, timestamp2);
}
}

The result of the code:

1409515200  1406836800

As you can see the conversion back and forth doesn't work. What's wrong?

Upvotes: 2

Views: 996

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338574

The answer by Aurasphere is correct and should be accepted.

Some further tips…

Use date-time classes for date-time values, rather than strings. Perform your business logic using date-time objects, and pass around such objects amongst your code rather than strings.

Avoid tracking date-times as a count-from-epoch. When you do need to serialize to text, use the unambiguous and easy-to-read formats defined by the ISO 8601 standard such as 2016-05-09T16:47:54Z.

You are using old troublesome legacy classes that have been supplanted by the java.time framework built into Java 8 and later. Much of that functionality has been back-ported to Java 6 & 7 in the ThreeTen-Backport project, and further adapted for Android in the ThreeTenABP project.

Using java.time classes will make your work easier and your code easier to comprehend, less likely to encounter the confusion seen in the Question.

An Instant is a moment on the timeline in UTC, with a resolution of nanoseconds. That class offers a convenient factory method ofEpochSecond, so no need to multiply by a thousand for milliseconds.

String input = "1409515200";
long seconds = Long.parseLong( input );
Instant instant = Instant.ofEpochSecond( seconds );

To get the wall-clock time for some locality, assign a time zone to get a ZonedDateTime.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

To generate a string in standard ISO 8601 format, call toString. Note that this method extends that format to append the name of the time zone in square brackets. For example, 2007-12-03T10:15:30+01:00[Europe/Paris].

String output = zdt.toString();

To get the date-only as in the Question, extract a LocalDate object.

LocalDate localDate = zdt.toLocalDate();

From there you can determine the first moment of the day. The first moment is not always the time-of-day 00:00:00.0, so let java.time determine that.

ZonedDateTime zdtStartOfDay = localDate.atStartOfDay( zoneId );

To get the two long integer counts of seconds seen in the Question, extract an Instant from each of our ZonedDateTime objects, and ask for the seconds-since-epoch. Note that you might be losing data as the ZonedDateTime/Instant objects can store values with a resolution up to nanoseconds. The call asking for whole seconds from epoch means any fraction of a second is truncated.

long seconds1 = zdt.toInstant().getEpochSecond();
long seconds2 = zdtStartOfDay.toInstant().getEpochSecond();

Upvotes: 1

Aurasphere
Aurasphere

Reputation: 4011

Your problem here is the rounding. In the first method, you are converting your timestamp (which is the number of milliseconds from 1970) into a date. You are now getting only the date, discarding hours, minutes, seconds and milliseconds and converting it back. This means that you will always have a difference of the amount you are discarding (between 0 at 00:00:00:000 and 86400000 at 23:59:59:999). To fix it, simply change your date format to include the hours with milliseconds precision.

Upvotes: 1

Related Questions