SimpleDateFormat 格式化日期

Java常用方法   2025-01-12 12:26   147   0  

一、日期和时间模式字母

字母

释义

yyyy

MM

dd

hh

1~12小时制(1-12)

HH

24小时制(0-23)

mm

ss

S

毫秒

E

星期几

D

一年中的第几天

F

一月中的第几个星期(会把这个月总共过的天数除以7)

w

一年中的第几个星期

W

一月中的第几星期(会根据实际情况来算)

a

上下午标识

k

和HH差不多,表示一天24小时制(1-24)。

K

和hh差不多,表示一天12小时制(0-11)。

z

表示时区  

上面的日期和时间模式  是按我们常用的年月日时分秒来放的,下面是专业的图,供参考。

定义了以下模式字母(所有其他字符 'A' 到 'Z' 和 'a' 到 'z' 都被保留)

37182_romg_2079.png

二、使用

1、Date 转 String

/**
 * java.util.Date转String 
 * 年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
 * @param d
 * @return
 */
public static String DateToString(Date d) {
    //年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm:ss.SSS E zZ");
    return sdf.format(d);
}

2、String 转 Date

/**
 * String转java.util.Date 
 * 年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
 * @param d
 * @return
 */
public static Date StringToDate(String str) {
    //年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm:ss.SSS E zZ");
    Date date = null;
    try {
        date = sdf.parse(str);
    } catch (ParseException e) {
        // TODO 自动生成的 catch 块
        e.printStackTrace();
    }
    return date;
}

3、测试,根据身份证获取出生日期和年龄

/**
 * 输入一个人的身份证号码,输出出身年月日,格式为xx年xx月xx日并求年龄
 * @param idCard 身份证号码
 * @return 格式为xx年xx月xx日并求年龄
 */
public static String getDateAndAgeByIdCard(String idCard) {

    String str = "";
    try {
        // 第一种输出年月日
        String b = idCard.substring(6, 14);
        // 日期对象 要与最开始的日期格式一致不能加年月日
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        // 将字符串转为日期对象 转为日期对象后就可以改变自己想要的格式了(就是sdf1定义)
        Date dt = sdf.parse(b);
        // 定义自己想要的日期格式,转化为日期对象后定义自己想要的格式
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年MM月dd日");
        String f = sdf1.format(dt);// 格式化日期对象
        // 求年龄
        Date now = new Date();
        // getTime()是指自己距离1970年为止的毫秒数
        // 转为 秒 /分/时/日/年
        long age = (now.getTime() - dt.getTime()) / 1000 / 60 / 60 / 24 / 360;
        str = "出生日期: " + f + ",年龄: " + age + "岁";
    } catch (Exception e) {
        // TODO: handle exception
        e.printStackTrace();
    }
    return str;
}

三、注意:线程不安全

由于 SimpleDateFormat 中的 format 方法在执行过程中,会使用一个成员变量 calendar 来保存时间。

private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) {
    this.calendar.setTime(date);
    boolean useDateFormatSymbols = this.useDateFormatSymbols();
    int i = 0;
    ……

由于在声明 SimpleDateFormat 的时候,使用的是 static 定义的。那么这个 SimpleDateFormat 就是一个共享变量,SimpleDateFormat 中的 calendar 也就可以被多个线程访问到。
举个例子:假设一个线程 A 刚执行完 calendar.setTime 把时间设置成 2020-05-07,这个线程还没执行完,线程 B 又执行了 calendar.setTime 把时间改成了 2020-06-07。这时候线程 A 继续往下执行,拿到的 calendar.getTime 得到的时间就是线程 B 改过之后的。
除了 format 方法以外,SimpleDateFormat 的 parse 方法也有同样的问题。
所以,不要把 SimpleDateFormat 作为一个共享变量使用。
如果使用的是Java 8 之前的JDK,可以使 SimpleDateFormat 变成线程安全的,通过加锁的方式来解决:

/**
 * 线程安全的 SimpleDateFormat
 */
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
/**
 * 获取 DateFormat
 * @param pattern 格式化
 * @return DateFormat
 */
public static DateFormat getDateFormat(String pattern) {
    DateFormat df = threadLocal.get();
    if(df == null){
        df = new SimpleDateFormat(pattern);
        threadLocal.set(df);
    }
    return df;
}

如果使用的是Java 8 + 的版本,那么完全可以抛弃这种线程不安全的时间格式化方法。可以使用 DateTimeFormatter 代替 SimpleDateFormat,这是一个线程安全的格式化工具类。

四、示例工具类 SimpleDateFormatSimple.java

package com;

import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatSimple {

    /**
     * 线程安全的 SimpleDateFormat
     */
    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
    /**
     * 获取 DateFormat
     * @param pattern 格式化
     * @return DateFormat
     */
    public static DateFormat getDateFormat(String pattern) {
        DateFormat df = threadLocal.get();
        if(df == null){
            df = new SimpleDateFormat(pattern);
            threadLocal.set(df);
        }
        return df;
    }


    /**
     * java.util.Date转String 
     * 年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
     * @param d
     * @return
     */
    public static String DateToStringByThreadLocal(Date d) {
        return getDateFormat("yyyy-MM-dd a HH:mm:ss.SSS E zZ").format(d);
    }

    /**
     * java.util.Date转String 
     * 年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
     * @param d
     * @return
     */
    public static String DateToString(Date d) {
        //年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm:ss.SSS E zZ");
        return sdf.format(d);
    }

    /**
     * String转java.util.Date 
     * 年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
     * @param d
     * @return
     */
    public static Date StringToDate(String str) {
        //年-月-日 上/下午标识 时:分:秒.毫秒 星期几 时区英文时区数字
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm:ss.SSS E zZ");
        Date date = null;
        try {
            date = sdf.parse(str);
        } catch (ParseException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 输入一个人的身份证号码,输出出身年月日,格式为xx年xx月xx日并求年龄
     * @param idCard 身份证号码
     * @return 格式为xx年xx月xx日并求年龄
     */
    public static String getDateAndAgeByIdCard(String idCard) {

        String str = "";
        try {
            // 第一种输出年月日
            String b = idCard.substring(6, 14);
            // 日期对象 要与最开始的日期格式一致不能加年月日
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            // 将字符串转为日期对象 转为日期对象后就可以改变自己想要的格式了(就是sdf1定义)
            Date dt = sdf.parse(b);
            // 定义自己想要的日期格式,转化为日期对象后定义自己想要的格式
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年MM月dd日");
            String f = sdf1.format(dt);// 格式化日期对象
            // 求年龄
            Date now = new Date();
            // getTime()是指自己距离1970年为止的毫秒数
            // 转为 秒 /分/时/日/年
            long age = (now.getTime() - dt.getTime()) / 1000 / 60 / 60 / 24 / 360;
            str = "出生日期: " + f + ",年龄: " + age + "岁";
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return str;
    }

}


博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。