常用DateUtil逻辑-问题处理(时区问题)

一、背景

实际情况:有一项对接数据,推送的时间格式是如下格式,但是封装的方法有个问题,出现的时间是当天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));
        }

}

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值