String.format, long и DateTime конвертация - PullRequest
2 голосов
/ 29 апреля 2020

Если вопрос дубликат, прошу прощения, я не нашел ничего через Google.

К моей проблеме:

У меня есть следующий тест:

public void testSecondsToMinutes() {
    long zero = 0;
    long thirty_seconds = 30;
    long one_minute = 60;
    long thirty_minutes_fiftynine_seonds = 1859;
    long two_hours_ten_miutes_fourty_seconds = 7840;

    String format1 = "00:00:00";
    String format2 = "00:00:30";
    String format3 = "00:01:00";
    String format4 = "00:30:59";
    String format5 = "02:10:40";

    assertEquals(format1,Entrypoint.secondsToMinutes(zero));
    assertEquals(format2,Entrypoint.secondsToMinutes(thirty_seconds));
    assertEquals(format3,Entrypoint.secondsToMinutes(one_minute));
    assertEquals(format4,Entrypoint.secondsToMinutes(thirty_minutes_fiftynine_seonds));
    assertEquals(format5,Entrypoint.secondsToMinutes(two_hours_ten_miutes_fourty_seconds)); 
}

и следующая функция:

public static String secondsToMinutes(long seconds)
{
    return String.format("%TH:%TM:%TS", seconds, seconds, seconds);
}

документация Java для Formatter гласит следующее:

The following conversion characters are used for formatting common date/time compositions.
This conversion may be applied to long, Long, Calendar, and Date.
[...]
'R' '\u0052'    Time formatted for the 24-hour clock as "%tH:%tM"
'T' '\u0054'    Time formatted for the 24-hour clock as "%tH:%tM:%tS".

Однако я получаю следующий ComparisionFailure:

expected 00:00:00 but was 01:00:00

Я должен упустить что-то очевидное здесь ...

Ответы [ 3 ]

2 голосов
/ 29 апреля 2020

Итак, чтобы ответить на это:

Я на самом деле попробовал нативный strftime в C:

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char*argv[])
{
        time_t t = 0;
        struct tm * tmp = localtime(&t);
        char outstr[200];
        strftime(outstr,sizeof(outstr),"%T",tmp);
        printf(outstr);
        exit(EXIT_SUCCESS);
}

, который, очевидно, вернул 01:00:00 из-за использования localtime(), а не gmtime(). Поскольку временные метки не содержат никакого часового пояса, для меня теперь стало очевидным, что время действительно приспособилось к моему часовому поясу, который был CET на 1 января 1970 года.

Грязный обходной путь для этого работа будет такой:

public static String secondsToTwentyFourHourString(long seconds)
{
    TimeZone current = TimeZone.getDefault();
    TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
    seconds *= 1000;
    try
    {return String.format("%1$TH:%1$TM:%1$TS", seconds);}
    finally
    {TimeZone.setDefault(current);}
}

лучшее решение - использовать Java 8 классы времени:

public static String secondsToTwentyFourHourString(long seconds)
{
    ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochSecond(seconds), ZoneId.of("UTC"));
    return zdt.format(DateTimeFormatter.ofPattern("HH:mm:ss")); 
}
2 голосов
/ 29 апреля 2020

Когда вы передаете long в String.format, оно интерпретируется как миллисекунды с эпохи, что является совершенно другим объектом, чем ваши предполагаемые секунды с полуночи.

Ваша проблема решается проще всего с помощью прямого кодирования Ваше намерение:

public static String secondsToTwentyFourHourString(long seconds) {
    return String.format("%1$TH:%1$TM:%1$TS", LocalTime.ofSecondOfDay(seconds));
}

Таким образом, вы указываете операцию, которая не зависит от часовых поясов.

Тот же лог c также работает с DateTimeFormatter:

public static String secondsToTwentyFourHourString(long seconds) {
    return LocalTime.ofSecondOfDay(seconds)
        .format(DateTimeFormatter.ofPattern("HH:mm:ss"));
}
1 голос
/ 29 апреля 2020

Для работы метода формата String требуется календарь. Вы не можете просто дать секунды. На самом деле, если вы запускаете каждый из ваших тестов по отдельности, ни один из них не будет зеленым.

Я использовал ответ, данный здесь , чтобы написать более классическое c решение:

public class Entrypoint {
    public static String secondsToMinutes(long seconds)
    {
        long absSeconds = Math.abs(seconds);
        String positive = String.format(
                "%02d:%02d:%02d",
                absSeconds / 3600,
                (absSeconds % 3600) / 60,
                absSeconds % 60);
        return seconds < 0 ? "-" + positive : positive;
    }
}
...