将1900年1月1日以来的秒转换为巴西的时间戳

时间:2018-11-28 10:38:40

标签: java datetime

我正在管理将其系统时钟报告为自午夜01-01-1900的秒数的设备。
我需要将其转换为时间戳。

到目前为止,我正在执行以下操作:

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class TestTime
{
  // Pass seconds since 01-01-1900 00:00:00 on the command line
  public static void main(String[] args)
  {
    // ---------------------
    // Create time formatter
    // ---------------------
    SimpleDateFormat format;
    format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // ---------------------------
    // Compose 01-01-1900 00:00:00
    // ---------------------------
    Calendar cal;
    cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, 1900);
    cal.set(Calendar.MONTH, Calendar.JANUARY);
    cal.set(Calendar.DAY_OF_MONTH, 1);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);

    // -------------------
    // Show what we've got
    // -------------------
    System.out.println(format.format(cal.getTime()));

    // ---------------------------------------------
    // Add the seconds as passed on the command line
    // ---------------------------------------------
    long secs = Long.parseLong(args[0]);
    while (secs > Integer.MAX_VALUE)
    {
      cal.add(Calendar.SECOND, Integer.MAX_VALUE);
      secs -= Integer.MAX_VALUE;
    }
    cal.add(Calendar.SECOND, (int)secs);

    // -------------------
    // Show what we've got
    // -------------------
    System.out.println(args[0] + " corresponds to " + format.format(cal.getTime()));

  } // main

} // class TestTime

在本地PC(意大利,Windows 7)上运行此程序时,我得到以下信息:

java -cp . TestTime 3752388800
1900-01-01 00:00:00
3752388800 corresponds to 2018-11-28 10:13:20

这是完全正确的。
在Linux机器(仍在意大利)上运行时,得到的结果相同。

但是,在巴西的Linux机器上运行相同的程序,却得到了不同的结果:

java -cp . TestTime 3752388800
1900-01-01 00:00:00
3752388800 corresponds to 2018-11-28 11:19:48

无论我在命令行中传递什么值,区别始终是01:06:28。
知道这种差异来自何处吗?

顺便说一句,我不在乎时区。我只需要一个时间戳

更新1:
使用Java 6(这是我们在巴西的生产环境中使用的实际版本)时,也会发生同样的事情。
因此,问题不取决于Java版本

更新2:
输入低于441763200(对应于01-01-1914 00:00:00)的秒数时,不会发生此问题。 问题仍然是为什么我们对巴西有所作为?

3 个答案:

答案 0 :(得分:4)

java.time

一种解决方案是确保您使用UTC进行转换:

    Instant base = LocalDate.of(1900, Month.JANUARY, 1)
            .atStartOfDay(ZoneOffset.UTC)
            .toInstant();

    String secsSince1900String = "3752388800";
    long secsSince1900 = Long.parseLong(secsSince1900String);
    Instant target = base.plusSeconds(secsSince1900);
    System.out.println(target);

输出(独立于JVM时区):

  

2018-11-28T10:13:20Z

输出中的结尾Z表示UTC。在将JVM的默认时区设置为America / Sao_Paulo时,我进行了测试,这没有什么区别。如果需要,可以根据自己的喜好设置日期和时间,例如:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String formattedDateTime = target.atOffset(ZoneOffset.UTC).format(formatter);
    System.out.println(formattedDateTime);
  

2018-11-28 10:13:20

在巴西跑步时出了什么问题?

巴西有多个时区。我以圣保罗为例,并很轻松地复制了您的输出。 1900年世纪之交,圣保罗与格林尼治标准时间-03:06:28偏移。您的Calendar使用JVM的默认时区,因此您确实将其一天中的时间设置为格林尼治标准时间03:06:28,这可以解释两者之间的区别。

也就是说,您使用的日期时间类(SimpleDateFormatCalendar)存在设计问题,幸运的是,现代的Java日期和时间API java.time已替换为Java。近5年前的8年。现代API的一个特点是,我们自然而然地将时区明确化了,这使得避免像您这样的问题变得更容易,而且如果我们遇到这些问题,也可以更轻松地解决它们。

问题:我们的生产Java版本是Java6。我可以使用java.time吗?

是的,java.time在Java 6上运行良好。已经被反向移植。

  • 在Java 8和更高版本以及更新的Android设备(API级别26以上)中,内置了现代API。
  • 在Java 6和7中,获得ThreeTen Backport,即新类的backport(JSR 310的ThreeTen;请参见底部的链接)。
  • 在(较旧的)Android上,使用Android版本的ThreeTen Backport。叫做ThreeTenABP。并确保您使用子包从org.threeten.bp导入日期和时间类。

链接

答案 1 :(得分:3)

1914年1月1日,巴西更改了时间,从LMT到BRT的时间增加了6分28秒(参见Time Changes in São Paulo Over the Years, 1900-1924)。

另外一小时的差异可能是巴西跨越3个时区(UTC-4UTC-3UTC-2),而您并未在代码中设置时区,具体取决于在JVM系统时区上。

答案 2 :(得分:3)

看看这个站点:https://www.timeanddate.com/time/zone/brazil/sao-paulo并导航到1900-1924的时区更改。在此,您可以看到在01-01-1914之前与UTC的偏移量为-03:06:28。这与Why is subtracting these two times (in 1927) giving a strange result?

中的原因完全相同