调用亚马逊MWS接口遇到的时区问题

处理数据很多时候都用到时间,通常是不用考虑时间不一致的情况,但在做国际化应用时就不得不考虑这些问题了。因为一旦时间错误很可能引发各种问题。这时候就引入一个概念来解决这个问题。时区。

References:

时区的概念
之所以有时区的概念是因为住在地球上不同地方的人看到太阳升起的时间是不一样的。我们假设北京人民在早上8:00看到了太阳刚刚升起,而此刻欧洲人民还在夜里,他们还需要再过7个小时才能看到太阳升起,所以,此刻欧洲人民的手表上显示的是凌晨1:00。如果你强迫他们用北京时间那他们每天看到日出的时间就是下午3点。

也就是说,东8区的北京人民的手表显示的8:00和东1区欧洲人民手表显示的1:00是相同的时刻:

"2014-10-14 08:00 +8:00" = "2014-10-14 01:00 +1:00"

这就是本地时间的概念。
但是,在计算机中,如果用本地时间来存储日期和时间,在遇到时区转换的问题上,即便你非常清楚地知道如何转换,也非常麻烦,尤其是矫情的美国人还在采用夏令时。
所以我们需要引入“绝对时间”的概念。绝对时间不需要年月日,而是以秒来计时。当前时间是指从一个基准时间(1970-1-1 00:00:00 +0:00),到现在的秒数,用一个整数表示。(就我个人的了解,应该是一个long类型的毫秒数吧,比如Java的System.currentTimeMillis()方法)

正确的存储方式
基于“数据的存储和显示相分离”的设计原则,我们只要把表示绝对时间的时间戳(无论是Long型还是Float)存入数据库,在显示的时候根据用户设置的时区格式化为正确的字符串。
所以,数据库存储时间和日期时,只需要把Long或者Float表示的时间戳存到BIGINT或REAL类型的列中,完全不用管数据库自己提供的DATETIME或TIMESTAMP,也不用担心应用服务器和数据库服务器的时区设置问题,遇到Oracle数据库你不必去理会with timezone和with local timezone到底有啥区别。

读取时间时,读到的是一个Long或Float,只需要按照用户的时区格式化为字符串就能正确地显示出来:

long t = System.currentTimeMillis();
System.out.println("long = " + t);

// current time zone:
SimpleDateFormat sdf_default = new SimpleDateFormat("yyyy-MM-dd HH:mm");
System.out.println(sdf_default.format(t));

// +8:00 time zone:
SimpleDateFormat sdf_8 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_8.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
System.out.println("GMT+8:00 = " + sdf_8.format(t));

// +7:00 time zone:
SimpleDateFormat sdf_7 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_7.setTimeZone(TimeZone.getTimeZone("GMT+7:00"));
System.out.println("GMT+7:00 = " + sdf_7.format(t));

// -9:00 time zone:
SimpleDateFormat sdf_la = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_la.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println("America/Los_Angeles = " + sdf_la.format(t));

以上摘自廖雪峰老师的网站。
了解了时区的基本用法之后就是调用亚马逊的接口了。
需求是这样的,从给定时间开始,持续拉取某个店铺的订单。
打算做成定时任务,每天跑。由于亚马逊订单接口需要传递时间,所以这里就有了时间一致的问题。看了亚马逊关于时区的解答,亚马逊统一用ISO 8601标准,传递的都是UTC时间。这样就简单多了。只要把当前时间格式化成UTC时间就可以了。参考几篇文章,写了个转换方法如下:

/**
 * 将给定的时间转换成给定时区的UTC时间,时间格式为ISO8601标准
 * @param time 时间毫秒数
 * @return ISO8601格式的时间
 */
public static String timeToISO8601UTC(Calendar time) {
    TimeZone tz = TimeZone.getTimeZone("UTC");
    String pattern = "yyyy-MM-dd'T'HH:mm:ss:SSS'Z'";
    return org.apache.commons.lang.time.DateFormatUtils.format(time, pattern, tz);
}

这里使用Calendar而不是Date,是因为Date不包含时区信息,而Calendar有。这样将本地时间转换成UTC时间后就可以直接丢到接口里,也不用考虑时间的问题了。因为所有的时间都是以当前时间为准,再转换成UTC时间。

差不多就这样吧,其他都是些数据处理的问题,注意点应该没什么大问题。

\>\> update20210207 使用Java8中的DateTimeFormatter可以更方便一点

ZonedDateTime now = ZonedDateTime.now();
String formatTime = DateTimeFormatter.ISO_INSTANT.format(now);
System.out.println(formatTime); // 2021-02-07T06:05:24.847499Z

标签: none

添加新评论

ali-01.gifali-58.gifali-09.gifali-23.gifali-04.gifali-46.gifali-57.gifali-22.gifali-38.gifali-13.gifali-10.gifali-34.gifali-06.gifali-37.gifali-42.gifali-35.gifali-12.gifali-30.gifali-16.gifali-54.gifali-55.gifali-59.gif

加载中……