一、背景
实际情况:有一项对接数据,推送的时间格式是如下格式,但是封装的方法有个问题,出现的时间是当天day-1的情况
String format = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
问题如下:
封装情况:
public static String date2UTCDateStr(Date localDate,TimeZone zone ) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(localDate);
int zoneOffset = calendar.get(Calendar.ZONE_OFFSET);
int dstOffset = calendar.get(Calendar.DST_OFFSET);
calendar.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset));
long timeInMillis = calendar.getTimeInMillis();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
if(zone==null){
df.setTimeZone(TimeZone.getTimeZone("GMT-8")) ;
}else{
df.setTimeZone(zone) ;
}
return df.format(timeInMillis);
}
原因分析
1、Calendar获取的是当前时间戳信息
2、拿了zoneOffset和dstOffset 用来计算当前时区的偏移量
3、calendar.add 减去了这部分偏移量,实际来说 这时候的时间戳calendar就是 格林威治的时间戳
4、因为当前是格林威治时间的时间戳,zone传空,代码又设置了一次时区,导致df实际format时候又被减去了 8+8个小时,为什么是8+8 我理解是本地是GMT+8 先扣除8 然后因为 getTimeZone 设置的是GMT-8,所以差值实际是16小时
5、所以这里如果不设置时区 返回格林威治时间,应该直接df.format(timeInMillis)
6、不设置时区 返回北京时间 GMT+8 应该setTimeZone时,GMT+16 原因和上面一致, 本地在GMT+8 会先格林威治时间的数据-8,你要补回来 需要+16
7、总的来说 上面做calendar.add 计算时间差的逻辑,可以不写,用当前时间,直接带着TimeZone去做计算,就不会有这么多麻烦的问题
二、解决方法
默认返回GMT+8 本地时间,如果需要时区返回,自行设置即可。
public static String date2UTCDateStr(Date localDate,TimeZone zone ) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(localDate);
int zoneOffset = calendar.get(Calendar.ZONE_OFFSET);
int dstOffset = calendar.get(Calendar.DST_OFFSET);
//calendar.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset));
long timeInMillis = calendar.getTimeInMillis();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
if(zone==null){
df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
}else{
df.setTimeZone(zone) ;
}
return df.format(timeInMillis);
}
获取测试:
设置GMT时区,返回测试:
三、时间转换相关的一些方法记录
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class DateUtils {
private final static Logger logger = LoggerFactory.getLogger(DateUtils.class);
public static final String DATE_FORMAT_YYYYMMDDHHMMSS = "yyyy-MM-dd HH:mm:ss";
public static final String DATE_FORMAT_YYYYMMDDHHMMSS_SHORT = "yyyyMMddHHmmss";
public static final String DATE_FORMAT_YYYYMMDD = "yyyy-MM-dd";
public static final int defaultTimezoneOffset = TimeZone.getDefault().getRawOffset() / (1000 * 60 * 60);
/**
* 转换日期成指定格式的字符串
*
* @param date
* @param format
* @return
*/
public static String formatDate2String(Date date, String format) {
String dateTimeStr = "";
if (date != null) {
SimpleDateFormat sf = new SimpleDateFormat(format);
dateTimeStr = sf.format(date);
}
return dateTimeStr;
}
/**
* 转换指定格式的字符串成日期
*
* @param dateStr
* @param format
* @return
*/
public static Date formatString2Date(String dateStr, String format) {
Date date = null;
if (dateStr != null && !dateStr.isEmpty()) {
try {
SimpleDateFormat sf = new SimpleDateFormat(format);
return sf.parse(dateStr);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
return date;
}
/**
* 调整日期时间
*
* @param date 当前时间
* @param field 调整域(年月日时分秒)
* @param amount 调整量(正值向后调,负值向前调)
* @return
*/
public static Date adjustDateTime(Date date, int field, int amount) {
Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(field, amount);
return c.getTime();
}
/**
* 调整日期
*
* @param date 当前时间
* @param amount 日期调整量(正值向后调,负值向前调)
* @return
*/
public static Date adjustDay(Date date, int amount) {
return adjustDateTime(date, Calendar.DAY_OF_YEAR, amount);
}
/**
* 转换指定格式的字符串成日期(抛出异常)
*
* @param dateStr
* @param format
* @return
*/
public static Date formatStringToDateThrowE(String dateStr, String format) throws ParseException {
Date date = null;
if (dateStr != null && !dateStr.isEmpty()) {
SimpleDateFormat sf = new SimpleDateFormat(format);
return sf.parse(dateStr);
}
return date;
}
/**
* 调整日期时间(返回字符串格式)
*
* @param date 当前时间
* @param field 调整域(年月日时分秒)
* @param amount 调整量(正值向后调,负值向前调)
* @param format 格式化方式
* @return
*/
public static String adjustDateTimeToString(Date date, int field, int amount, String format) {
Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(field, amount);
Date date1 = c.getTime();
SimpleDateFormat sf = new SimpleDateFormat(format);
return sf.format(date1);
}
/**
* 调整日期时间(返回字符串格式)(抛出异常)
*
* @param dateStr 当前时间(字符串)
* @param field 调整域(年月日时分秒)
* @param amount 调整量(正值向后调,负值向前调)
* @param format 格式化方式
* @return
*/
public static String adjustDateTimeStrToStr(String dateStr, int field, int amount, String format) throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat(format);
Calendar c = Calendar.getInstance();
c.setTime(sf.parse(dateStr));
c.add(field, amount);
Date date1 = c.getTime();
return sf.format(date1);
}
/**
* 根据时区转换成本地时间字符串,需指定时间格式
*
* @param time
* @param offset
* @param dateFormt
* @return
*/
public static String convert2LocalTimeStr(String time, int offset, String dateFormt) {
Date date = formatString2Date(time, dateFormt);
return convert2LocalTimeStr(date, offset, dateFormt);
}
/**
* 根据时区转换成本地时间字符串, 默认格式:yyyy-MM-dd HH:mm:ss
*
* @param time
* @param offset
* @return
*/
public static String convert2LocalTimeStr(String time, int offset) {
return convert2LocalTimeStr(time, offset, DATE_FORMAT_YYYYMMDDHHMMSS);
}
/**
* 把时间对象转换成本地时间字符串,需指定时间格式
*
* @param date
* @param offset
* @param dateFormt
* @return
*/
public static String convert2LocalTimeStr(Date date, int offset, String dateFormt) {
return getSimpleDateFormatWithTimezone(offset, dateFormt).format(date);
}
/**
* 把时间对象转换成本地时间字符串, 默认格式:yyyy-MM-dd HH:mm:ss
*
* @param date
* @param offset
* @return
*/
public static String convert2LocalTimeStr(Date date, int offset) {
return convert2LocalTimeStr(date, offset, DATE_FORMAT_YYYYMMDDHHMMSS);
}
/**
* 得到服务器的时区偏移小时
*
* @return
*/
public static int getDefaultTimeZoneOffset() {
return defaultTimezoneOffset;
}
/**
* 把本地时间字符串转换成时间对象,需指定时间格式
*
* @param timeStr
* @param offset
* @param dateFormt
* @return
* @throws Exception
*/
public static Date convert2LocalTime(String timeStr, int offset, String dateFormt) throws Exception {
return getSimpleDateFormatWithTimezone(offset, dateFormt).parse(timeStr);
}
/**
* 把本地时间字符串转换成时间对象, 默认格式:yyyy-MM-dd HH:mm:ss
*
* @param timeStr
* @param offset
* @return
* @throws Exception
*/
public static Date convert2LocalTime(String timeStr, int offset) throws Exception {
return convert2LocalTime(timeStr, offset, DATE_FORMAT_YYYYMMDDHHMMSS);
}
/**
* 得到带时区的时间格式转换器
*
* @param offset 时区偏移小时
* @param dateFormt 样式
* @return
*/
private static SimpleDateFormat getSimpleDateFormatWithTimezone(int offset, String dateFormt) {
logger.debug("Timezone offset of machine:" + offset);
SimpleDateFormat sf = new SimpleDateFormat(dateFormt);
// machine's time zone is different with server, need convert time to local time
if (getDefaultTimeZoneOffset() != offset) {
String timezoneId = "GMT" + (offset > 0 ? "+" : "") + String.valueOf(offset) + ":00";
logger.debug("timezoneId:" + timezoneId);
sf.setTimeZone(TimeZone.getTimeZone(timezoneId));
logger.debug("Set timezone:" + sf.getTimeZone());
}
return sf;
}
public static boolean isDSTTime(Date time, String dstConfigStr) {
boolean result = false;
if (StringUtils.isNotBlank(dstConfigStr)) {
try {
String[] dstConfigArr = dstConfigStr.split(";");
Date dstBeginTime = getDSTBeginEndTime(time, dstConfigArr[0]);
System.out.println(dstBeginTime);
Date dstEndTime = getDSTBeginEndTime(time, dstConfigArr[1]);
System.out.println(dstEndTime);
if (dstBeginTime.before(dstEndTime)) {
result = (time.compareTo(dstBeginTime) >= 0 && time.compareTo(dstEndTime) < 0);
} else {
result = (time.compareTo(dstBeginTime) >= 0 || time.compareTo(dstEndTime) < 0);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
logger.debug(time + ", dstConfigStr:" + dstConfigStr + ", DST result:" + result);
return result;
}
private static Date getDSTBeginEndTime(Date time, String configStr) {
String[] configArr = configStr.split("_");
Calendar calendar = Calendar.getInstance();
calendar.setTime(time);
//- 几月份
calendar.set(Calendar.MONTH, Integer.parseInt(configArr[0]) - 1);
//- 第几个星期天
calendar.set(Calendar.DAY_OF_WEEK_IN_MONTH, Integer.parseInt(configArr[1]));
calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
//- 几点开始
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(configArr[2]));
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
return calendar.getTime();
}
/*public static void main(String[] args) {
Date time = DateUtils.formatString2Date("2020-04-01 12:30:16", DateUtils.DATE_FORMAT_YYYYMMDDHHMMSS);
System.out.println(convert2LocalTimeStr(new Date(), 7));
}*/
public static String date2UTCDateStr(Date localDate,TimeZone zone ) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(localDate);
//这里不需要做时区偏差值计算,默认当前GMT+8的时间戳返回
//int zoneOffset = calendar.get(Calendar.ZONE_OFFSET);
//int dstOffset = calendar.get(Calendar.DST_OFFSET);
//calendar.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset));
long timeInMillis = calendar.getTimeInMillis();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
//不传zone时,默认返回GMT+8
if(zone==null){
df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
}else{
df.setTimeZone(zone) ;
}
return df.format(timeInMillis);
}
public static String localDateStr2UTCDateStr(String localDateStr,TimeZone zone) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = sdf.parse(localDateStr);
} catch (ParseException e) {
logger.info("时间转化错误 dateStr:{} dateFormat:{}", localDateStr, "yyyy-MM-dd HH:mm:ss");
return localDateStr;
}
return date2UTCDateStr(date,zone);
}
public static void main(String[] args) {
Date localDate = new Date();
String nowStr = DateUtil.format(localDate, DatePattern.NORM_DATETIME_PATTERN);
TimeZone zone = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = sdf.parse(nowStr);
} catch (ParseException e) {
logger.info("时间转化错误 dateStr:{} dateFormat:{}", nowStr, "yyyy-MM-dd HH:mm:ss");
}
System.out.println("时间信息:"+localDate.toString());
Calendar calendar = Calendar.getInstance();
calendar.setTime(localDate);
int zoneOffset = calendar.get(Calendar.ZONE_OFFSET);
int dstOffset = calendar.get(Calendar.DST_OFFSET);
calendar.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset));
long timeInMillis = calendar.getTimeInMillis();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
if(zone==null){
df.setTimeZone(TimeZone.getTimeZone("GMT-8")) ;
}else{
df.setTimeZone(zone) ;
}
System.out.println(df.format(timeInMillis));
}
}