Java Charset Zone LocalDateTime Best Practise

February 06, 2023 / Administrator / 79阅读 / 0评论/ 分类: Java

CharSet

java.nio.charset.StandardCharsets#UTF_8
 // 根据字符串,获取枚举值    Charset.forName("UTF-8")
        Charset charset = Charset.forName("UTF-8");
        System.out.println(charset);


        // 根据charset枚举值,获取字符串
        String s = StandardCharsets.UTF_8.displayName();
        String beijingZone = URLEncoder.encode("GMT+8", s);
        System.out.println(beijingZone); // GMT%2B8   这下知道mysql连接中时区,需要写成这样了吧,就是因为,需要写成html application/x-www-form-urlencoded MIME format

MediaType

// org.springframework.http.MediaType

static {
		// Not using "valueOf' to avoid static init cost
		ALL = new MediaType("*", "*");
		APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
		APPLICATION_CBOR = new MediaType("application", "cbor");
		APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
		APPLICATION_GRAPHQL = new MediaType("application", "graphql+json");
		APPLICATION_JSON = new MediaType("application", "json");
		APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
		APPLICATION_NDJSON = new MediaType("application", "x-ndjson");
		APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream");
		APPLICATION_PDF = new MediaType("application", "pdf");
		APPLICATION_PROBLEM_JSON = new MediaType("application", "problem+json");
		APPLICATION_PROBLEM_JSON_UTF8 = new MediaType("application", "problem+json", StandardCharsets.UTF_8);
		APPLICATION_PROBLEM_XML = new MediaType("application", "problem+xml");
		APPLICATION_RSS_XML = new MediaType("application", "rss+xml");
		APPLICATION_STREAM_JSON = new MediaType("application", "stream+json");
		APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml");
		APPLICATION_XML = new MediaType("application", "xml");
		IMAGE_GIF = new MediaType("image", "gif");
		IMAGE_JPEG = new MediaType("image", "jpeg");
		IMAGE_PNG = new MediaType("image", "png");
		MULTIPART_FORM_DATA = new MediaType("multipart", "form-data");
		MULTIPART_MIXED = new MediaType("multipart", "mixed");
		MULTIPART_RELATED = new MediaType("multipart", "related");
		TEXT_EVENT_STREAM = new MediaType("text", "event-stream");
		TEXT_HTML = new MediaType("text", "html");
		TEXT_MARKDOWN = new MediaType("text", "markdown");
		TEXT_PLAIN = new MediaType("text", "plain");
		TEXT_XML = new MediaType("text", "xml");
	}

converting a String to the application/x-www-form-urlencoded MIME format---URLEncoder

https://docs.oracle.com/javase/8/docs/api/java/net/URLEncoder.html

public class URLEncoder
extends Object

Utility class for HTML form encoding. This class contains static methods for converting a String to the application/x-www-form-urlencoded MIME format. For more information about HTML form encoding, consult the HTML specification.

When encoding a String, the following rules apply:

  • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the same.
  • The special characters ".", "-", "*", and "_" remain the same.
  • The space character " " is converted into a plus sign "+".
  • All other characters are unsafe and are first converted into one or more bytes using some encoding scheme. Then each byte is represented by the 3-character string "%xy", where xy is the two-digit hexadecimal representation of the byte. The recommended encoding scheme to use is UTF-8. However, for compatibility reasons, if an encoding is not specified, then the default encoding of the platform is used.

For example using UTF-8 as the encoding scheme the string "The string ü@foo-bar" would get converted to "The+string+%C3%BC%40foo-bar" because in UTF-8 the character ü is encoded as two bytes C3 (hex) and BC (hex), and the character @ is encoded as one byte 40 (hex).

Modifier and TypeMethod and Description
static Stringencode(String s, String enc)Translates a string into application/x-www-form-urlencoded format using a specific encoding scheme.
 // 根据charset枚举值,获取字符串
        String s = StandardCharsets.UTF_8.displayName();
        String beijingZone = URLEncoder.encode("GMT+8", s);
        System.out.println(beijingZone); // GMT%2B8   这下知道mysql连接中时区,需要写成这样了吧,就是因为,需要写成html application/x-www-form-urlencoded MIME format

时间戳 & 时区

时间戳,全名叫做Unix timestamp。这个Unix timestamp是一串数字,比如1675653685。

unix timestamp是怎么定义的呢?

unix timestamp定义为,从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数

同一个时刻,在全世界的任何一个国家,unix timestamp的值都是相同的。但是被不同国家的人,解析成不同的时间。

比如unix timestamp为1675653685,

  • 美国人认为,是 2023-02-05 19:21:25
  • 英国人认为,是 2023-02-06 03:21:25
  • 中国人认为,是 2023-02-06 11:21:25

也就是说,不同国家的人,对同一个unix timestamp的值,解读为不同的时间。但是unix timestamp的值,都是相同的。

https://tool.ip138.com/timestamp/

下面,我们再介绍下时区:

时间戳 在地球的每一个角落都是相同的,但是在相同的时间点会有不同的表达方式,所以有了另外一个时间概念,叫时区

时区的划分标准,有哪些呢?

时区的划分标准,分为3种:

  • GMT
  • UTC
  • POSIX

上面的GMT,表示格林威治标准时间;UTC,表示世界协调时间,是用原子钟计算的;

在ISO 8601中,UTC+8表示东八区---北京,UTC-8表示西八区---洛杉矶

但是在POSIX Timezone中,UTC+8表示西八区---洛杉矶,UTC-8表示东八区---北京

也就是说,同样是UTC+8,在2个不同的标准中,表示的完全是相反的地方。

ZoneId & TimeZone

ZoneId 是java8的新时区类,它对应以前的 TimeZone。

它的子类有 ZoneOffset , ZoneRegion

ZoneId---时区id

 /**
     * 获取时区id
     *
     * @return
     */
    public static ZoneId getZoneId() {
        ZoneId currentZoneId = ZoneId.of("Asia/Shanghai");
        return currentZoneId;
    }

我们也可以使用GMT,来创建一个ZoneId对象

ZoneId currentZoneId = ZoneId.of("GMT+8");

查看所支持的时区代码

public static void main(String[] args) {
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        System.out.println(availableZoneIds);
    }

结果如下:

[Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8, Africa/Nairobi, America/Marigot, Asia/Aqtau, Pacific/Kwajalein, America/El_Salvador, Asia/Pontianak, Africa/Cairo, Pacific/Pago_Pago, Africa/Mbabane, Asia/Kuching, Pacific/Honolulu, Pacific/Rarotonga, America/Guatemala, Australia/Hobart, Europe/London, America/Belize, America/Panama, Asia/Chungking, America/Managua, America/Indiana/Petersburg, Asia/Yerevan, Europe/Brussels, GMT, Europe/Warsaw, America/Chicago, Asia/Kashgar, Chile/Continental, Pacific/Yap, CET, Etc/GMT-1, Etc/GMT-0, Europe/Jersey, America/Tegucigalpa, Etc/GMT-5, Europe/Istanbul, America/Eirunepe, Etc/GMT-4, America/Miquelon, Etc/GMT-3, Europe/Luxembourg, Etc/GMT-2, Etc/GMT-9, America/Argentina/Catamarca, Etc/GMT-8, Etc/GMT-7, Etc/GMT-6, Europe/Zaporozhye, Canada/Yukon, Canada/Atlantic, Atlantic/St_Helena, Australia/Tasmania, Libya, Europe/Guernsey, America/Grand_Turk, US/Pacific-New, Asia/Samarkand, America/Argentina/Cordoba, Asia/Phnom_Penh, Africa/Kigali, Asia/Almaty, US/Alaska, Asia/Dubai, Europe/Isle_of_Man, America/Araguaina, Cuba, Asia/Novosibirsk, America/Argentina/Salta, Etc/GMT+3, Africa/Tunis, Etc/GMT+2, Etc/GMT+1, Pacific/Fakaofo, Africa/Tripoli, Etc/GMT+0, Israel, Africa/Banjul, Etc/GMT+7, Indian/Comoro, Etc/GMT+6, Etc/GMT+5, Etc/GMT+4, Pacific/Port_Moresby, US/Arizona, Antarctica/Syowa, Indian/Reunion, Pacific/Palau, Europe/Kaliningrad, America/Montevideo, Africa/Windhoek, Asia/Karachi, Africa/Mogadishu, Australia/Perth, Brazil/East, Etc/GMT, Asia/Chita, Pacific/Easter, Antarctica/Davis, Antarctica/McMurdo, Asia/Macao, America/Manaus, Africa/Freetown, Europe/Bucharest, Asia/Tomsk, America/Argentina/Mendoza, Asia/Macau, Europe/Malta, Mexico/BajaSur, Pacific/Tahiti, Africa/Asmera, Europe/Busingen, America/Argentina/Rio_Gallegos, Africa/Malabo, Europe/Skopje, America/Catamarca, America/Godthab, Europe/Sarajevo, Australia/ACT, GB-Eire, Africa/Lagos, America/Cordoba, Europe/Rome, Asia/Dacca, Indian/Mauritius, Pacific/Samoa, America/Regina, America/Fort_Wayne, America/Dawson_Creek, Africa/Algiers, Europe/Mariehamn, America/St_Johns, America/St_Thomas, Europe/Zurich, America/Anguilla, Asia/Dili, America/Denver, Africa/Bamako, Europe/Saratov, GB, Mexico/General, Pacific/Wallis, Europe/Gibraltar, Africa/Conakry, Africa/Lubumbashi, Asia/Istanbul, America/Havana, NZ-CHAT, Asia/Choibalsan, America/Porto_Acre, Asia/Omsk, Europe/Vaduz, US/Michigan, Asia/Dhaka, America/Barbados, Europe/Tiraspol, Atlantic/Cape_Verde, Asia/Yekaterinburg, America/Louisville, Pacific/Johnston, Pacific/Chatham, Europe/Ljubljana, America/Sao_Paulo, Asia/Jayapura, America/Curacao, Asia/Dushanbe, America/Guyana, America/Guayaquil, America/Martinique, Portugal, Europe/Berlin, Europe/Moscow, Europe/Chisinau, America/Puerto_Rico, America/Rankin_Inlet, Pacific/Ponape, Europe/Stockholm, Europe/Budapest, America/Argentina/Jujuy, Australia/Eucla, Asia/Shanghai, Universal, Europe/Zagreb, America/Port_of_Spain, Europe/Helsinki, Asia/Beirut, Asia/Tel_Aviv, Pacific/Bougainville, US/Central, Africa/Sao_Tome, Indian/Chagos, America/Cayenne, Asia/Yakutsk, Pacific/Galapagos, Australia/North, Europe/Paris, Africa/Ndjamena, Pacific/Fiji, America/Rainy_River, Indian/Maldives, Australia/Yancowinna, SystemV/AST4, Asia/Oral, America/Yellowknife, Pacific/Enderbury, America/Juneau, Australia/Victoria, America/Indiana/Vevay, Asia/Tashkent, Asia/Jakarta, Africa/Ceuta, Asia/Barnaul, America/Recife, America/Buenos_Aires, America/Noronha, America/Swift_Current, Australia/Adelaide, America/Metlakatla, Africa/Djibouti, America/Paramaribo, Europe/Simferopol, Europe/Sofia, Africa/Nouakchott, Europe/Prague, America/Indiana/Vincennes, Antarctica/Mawson, America/Kralendijk, Antarctica/Troll, Europe/Samara, Indian/Christmas, America/Antigua, Pacific/Gambier, America/Indianapolis, America/Inuvik, America/Iqaluit, Pacific/Funafuti, UTC, Antarctica/Macquarie, Canada/Pacific, America/Moncton, Africa/Gaborone, Pacific/Chuuk, Asia/Pyongyang, America/St_Vincent, Asia/Gaza, Etc/Universal, PST8PDT, Atlantic/Faeroe, Asia/Qyzylorda, Canada/Newfoundland, America/Kentucky/Louisville, America/Yakutat, Asia/Ho_Chi_Minh, Antarctica/Casey, Europe/Copenhagen, Africa/Asmara, Atlantic/Azores, Europe/Vienna, ROK, Pacific/Pitcairn, America/Mazatlan, Australia/Queensland, Pacific/Nauru, Europe/Tirane, Asia/Kolkata, SystemV/MST7, Australia/Canberra, MET, Australia/Broken_Hill, Europe/Riga, America/Dominica, Africa/Abidjan, America/Mendoza, America/Santarem, Kwajalein, America/Asuncion, Asia/Ulan_Bator, NZ, America/Boise, Australia/Currie, EST5EDT, Pacific/Guam, Pacific/Wake, Atlantic/Bermuda, America/Costa_Rica, America/Dawson, Asia/Chongqing, Eire, Europe/Amsterdam, America/Indiana/Knox, America/North_Dakota/Beulah, Africa/Accra, Atlantic/Faroe, Mexico/BajaNorte, America/Maceio, Etc/UCT, Pacific/Apia, GMT0, America/Atka, Pacific/Niue, Australia/Lord_Howe, Europe/Dublin, Pacific/Truk, MST7MDT, America/Monterrey, America/Nassau, America/Jamaica, Asia/Bishkek, America/Atikokan, Atlantic/Stanley, Australia/NSW, US/Hawaii, SystemV/CST6, Indian/Mahe, Asia/Aqtobe, America/Sitka, Asia/Vladivostok, Africa/Libreville, Africa/Maputo, Zulu, America/Kentucky/Monticello, Africa/El_Aaiun, Africa/Ouagadougou, America/Coral_Harbour, Pacific/Marquesas, Brazil/West, America/Aruba, America/North_Dakota/Center, America/Cayman, Asia/Ulaanbaatar, Asia/Baghdad, Europe/San_Marino, America/Indiana/Tell_City, America/Tijuana, Pacific/Saipan, SystemV/YST9, Africa/Douala, America/Chihuahua, America/Ojinaga, Asia/Hovd, America/Anchorage, Chile/EasterIsland, America/Halifax, Antarctica/Rothera, America/Indiana/Indianapolis, US/Mountain, Asia/Damascus, America/Argentina/San_Luis, America/Santiago, Asia/Baku, America/Argentina/Ushuaia, Atlantic/Reykjavik, Africa/Brazzaville, Africa/Porto-Novo, America/La_Paz, Antarctica/DumontDUrville, Asia/Taipei, Antarctica/South_Pole, Asia/Manila, Asia/Bangkok, Africa/Dar_es_Salaam, Poland, Atlantic/Madeira, Antarctica/Palmer, America/Thunder_Bay, Africa/Addis_Ababa, Asia/Yangon, Europe/Uzhgorod, Brazil/DeNoronha, Asia/Ashkhabad, Etc/Zulu, America/Indiana/Marengo, America/Creston, America/Punta_Arenas, America/Mexico_City, Antarctica/Vostok, Asia/Jerusalem, Europe/Andorra, US/Samoa, PRC, Asia/Vientiane, Pacific/Kiritimati, America/Matamoros, America/Blanc-Sablon, Asia/Riyadh, Iceland, Pacific/Pohnpei, Asia/Ujung_Pandang, Atlantic/South_Georgia, Europe/Lisbon, Asia/Harbin, Europe/Oslo, Asia/Novokuznetsk, CST6CDT, Atlantic/Canary, America/Knox_IN, Asia/Kuwait, SystemV/HST10, Pacific/Efate, Africa/Lome, America/Bogota, America/Menominee, America/Adak, Pacific/Norfolk, Europe/Kirov, America/Resolute, Pacific/Tarawa, Africa/Kampala, Asia/Krasnoyarsk, Greenwich, SystemV/EST5, America/Edmonton, Europe/Podgorica, Australia/South, Canada/Central, Africa/Bujumbura, America/Santo_Domingo, US/Eastern, Europe/Minsk, Pacific/Auckland, Africa/Casablanca, America/Glace_Bay, Canada/Eastern, Asia/Qatar, Europe/Kiev, Singapore, Asia/Magadan, SystemV/PST8, America/Port-au-Prince, Europe/Belfast, America/St_Barthelemy, Asia/Ashgabat, Africa/Luanda, America/Nipigon, Atlantic/Jan_Mayen, Brazil/Acre, Asia/Muscat, Asia/Bahrain, Europe/Vilnius, America/Fortaleza, Etc/GMT0, US/East-Indiana, America/Hermosillo, America/Cancun, Africa/Maseru, Pacific/Kosrae, Africa/Kinshasa, Asia/Kathmandu, Asia/Seoul, Australia/Sydney, America/Lima, Australia/LHI, America/St_Lucia, Europe/Madrid, America/Bahia_Banderas, America/Montserrat, Asia/Brunei, America/Santa_Isabel, Canada/Mountain, America/Cambridge_Bay, Asia/Colombo, Australia/West, Indian/Antananarivo, Australia/Brisbane, Indian/Mayotte, US/Indiana-Starke, Asia/Urumqi, US/Aleutian, Europe/Volgograd, America/Lower_Princes, America/Vancouver, Africa/Blantyre, America/Rio_Branco, America/Danmarkshavn, America/Detroit, America/Thule, Africa/Lusaka, Asia/Hong_Kong, Iran, America/Argentina/La_Rioja, Africa/Dakar, SystemV/CST6CDT, America/Tortola, America/Porto_Velho, Asia/Sakhalin, Etc/GMT+10, America/Scoresbysund, Asia/Kamchatka, Asia/Thimbu, Africa/Harare, Etc/GMT+12, Etc/GMT+11, Navajo, America/Nome, Europe/Tallinn, Turkey, Africa/Khartoum, Africa/Johannesburg, Africa/Bangui, Europe/Belgrade, Jamaica, Africa/Bissau, Asia/Tehran, WET, Europe/Astrakhan, Africa/Juba, America/Campo_Grande, America/Belem, Etc/Greenwich, Asia/Saigon, America/Ensenada, Pacific/Midway, America/Jujuy, Africa/Timbuktu, America/Bahia, America/Goose_Bay, America/Virgin, America/Pangnirtung, Asia/Katmandu, America/Phoenix, Africa/Niamey, America/Whitehorse, Pacific/Noumea, Asia/Tbilisi, America/Montreal, Asia/Makassar, America/Argentina/San_Juan, Hongkong, UCT, Asia/Nicosia, America/Indiana/Winamac, SystemV/MST7MDT, America/Argentina/ComodRivadavia, America/Boa_Vista, America/Grenada, Asia/Atyrau, Australia/Darwin, Asia/Khandyga, Asia/Kuala_Lumpur, Asia/Famagusta, Asia/Thimphu, Asia/Rangoon, Europe/Bratislava, Asia/Calcutta, America/Argentina/Tucuman, Asia/Kabul, Indian/Cocos, Japan, Pacific/Tongatapu, America/New_York, Etc/GMT-12, Etc/GMT-11, Etc/GMT-10, SystemV/YST9YDT, Europe/Ulyanovsk, Etc/GMT-14, Etc/GMT-13, W-SU, America/Merida, EET, America/Rosario, Canada/Saskatchewan, America/St_Kitts, Arctic/Longyearbyen, America/Fort_Nelson, America/Caracas, America/Guadeloupe, Asia/Hebron, Indian/Kerguelen, SystemV/PST8PDT, Africa/Monrovia, Asia/Ust-Nera, Egypt, Asia/Srednekolymsk, America/North_Dakota/New_Salem, Asia/Anadyr, Australia/Melbourne, Asia/Irkutsk, America/Shiprock, America/Winnipeg, Europe/Vatican, Asia/Amman, Etc/UTC, SystemV/AST4ADT, Asia/Tokyo, America/Toronto, Asia/Singapore, Australia/Lindeman, America/Los_Angeles, SystemV/EST5EDT, Pacific/Majuro, America/Argentina/Buenos_Aires, Europe/Nicosia, Pacific/Guadalcanal, Europe/Athens, US/Pacific, Europe/Monaco]

上面的结果中,我们最常用的,就是Asia/ShanghaiHongkongAmerica/New_YorkAmerica/Los_Angeles



时区ID
该ID在系统内是唯一的.ID有三种类型。

  • 最简单的ID类型是ZoneOffset中的ID,它由’Z’和以’+‘或’-'开头的ID组成。

  • 下一类ID是带有某种形式的前缀的偏移样式ID,例如’GMT + 2’或’UTC + 01:00’。可识别的前缀为’UTC’,‘GMT’和’UT’。是后缀,将在创建过程中进行规范化。可以使用normalized()将这些ID规范化为ZoneOffset。

  • ID的第三种类型是基于区域的ID。基于区域的ID必须包含两个或多个字符,并且不能以’UTC’,‘GMT’,‘UT’,’+‘或’-'开头。基于区域的ID由配置定义,请参见ZoneRulesProvider。提供从ID到底层ZoneRules的查找。

时区规则由政府定义并经常更改。有许多组织(在此称为组)来监视时区更改并进行整理。默认组是IANA时区数据库(TZDB)。其他组织包括IATA (航空业团体)和Microsoft。

每个组为其提供的区域ID定义其自己的格式.TZDB组定义诸如欧洲/伦敦’‘或美国/纽约’'的ID.TZDB ID优先于其他组。



ZoneId的实例可能是ZoneOffset或ZoneRegion

static ZoneId of(String zoneId, boolean checkAvailable) {
        Objects.requireNonNull(zoneId, "zoneId");
        if (zoneId.length() <= 1 || zoneId.startsWith("+") || zoneId.startsWith("-")) {
            return ZoneOffset.of(zoneId);
        } else if (zoneId.startsWith("UTC") || zoneId.startsWith("GMT")) {
            return ofWithPrefix(zoneId, 3, checkAvailable);
        } else if (zoneId.startsWith("UT")) {
            return ofWithPrefix(zoneId, 2, checkAvailable);
        }
        return ZoneRegion.ofId(zoneId, checkAvailable);
    }
    

可以看出,

  • ZoneId.of(“Asia/Shanghai”) 得到ZoneRegion
  • 如果以{GMT|UTC|UT}开头, 则创建ZoneRegion实例
  • 如果以加或减{+|-}开头, 则创建ZoneOffset实例,比如ZoneId.of("+8")

TimeZone

TimeZone 和 ZoneId,这2个类的功能,基本都差不多,都是用来表示时区的,也能互相转换

TimeZone timeZone = TimeZone.getTimeZone("GMT+8");

TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai");

TimeZone,是一个抽象类,常用实现类,是SimpleTimeZone

abstract public class TimeZone implements Serializable, Cloneable



public class SimpleTimeZone extends TimeZone 


TimeZone和 ZoneId的互相转换

public static void main(String[] args) {
        TimeZone timeZone = TimeZone.getTimeZone("GMT+8");
//        TimeZone timeZone = TimeZone.getTimeZone("GMT+08:00");

        ZoneId zoneId = timeZone.toZoneId();
        System.out.println(zoneId.getId());

        System.out.println("------------");

        TimeZone nextTimeZone = TimeZone.getTimeZone(zoneId);
        System.out.println(nextTimeZone.getDisplayName());
    }


TimeZone,用于什么场景呢?
当我们使用@JsonFormat注解时,可以指定timezone

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonFormat
{

    /**
     * {@link java.util.TimeZone} to use for serialization (if needed).
     * Special value of {@link #DEFAULT_TIMEZONE}
     * can be used to mean "just use the default", where default is specified
     * by the serialization context, which in turn defaults to system
     * default (UTC) unless explicitly set to another timezone.
     */
    public String timezone() default DEFAULT_TIMEZONE;


样例如下:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS", timezone = "GMT+8")
    private Timestamp ts;

ZonedDateTime & 时区转换

LocalDateTime总是表示本地日期和时间,要表示一个带时区的日期和时间,我们就需要ZonedDateTime

可以简单地把ZonedDateTime理解成LocalDateTimeZoneIdZoneIdjava.time引入的新的时区类

new_york时间,转为北京时间

public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);

        ZonedDateTime newyorkZoneDateTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
        System.out.println("newyorkZoneDateTime: "+newyorkZoneDateTime);


        ZonedDateTime beijingZoneDateTime = newyorkZoneDateTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
        System.out.println("beijingZoneDateTime: "+beijingZoneDateTime);
        System.out.println("beijingLocalDateTime: "+beijingZoneDateTime.toLocalDateTime());
    }

输出

2023-02-06T12:44:52.023
newyorkZoneDateTime: 2023-02-05T23:44:52.024-05:00[America/New_York]
beijingZoneDateTime: 2023-02-06T12:44:52.024+08:00[Asia/Shanghai]
beijingLocalDateTime: 2023-02-06T12:44:52.024

获取指定日期的毫秒值

/**
     * 获取指定日期的毫秒值
     *
     * @param localDateTime
     * @return
     */
    public static long getMillSecondByLocalDateTime(LocalDateTime localDateTime) {
        return localDateTime.atZone(getZoneId()).toInstant().toEpochMilli();
    }

根据毫秒值,获取localDateTime

 /**
     * 根据毫秒值,获取localDateTime
     *
     * @param millSeconds
     * @return
     */
    public static LocalDateTime getLocalDateTimeByMillSeconds(long millSeconds) {
        return Instant.ofEpochMilli(millSeconds).atZone(getZoneId()).toLocalDateTime();
    }

这2个时间,是否是同一天

 /**
     * 这2个时间,是否是同一天
     *
     * @param firstMillSeconds
     * @param secondMillSeconds
     * @return
     */
    public static boolean isSameDay(String firstMillSeconds, String secondMillSeconds) {
        if (StringUtils.isEmpty(firstMillSeconds) || StringUtils.isEmpty(secondMillSeconds)) {
            return false;
        }
        LocalDateTime firstDateTime = getLocalDateTimeByMillSeconds(Long.parseLong(firstMillSeconds));
        LocalDateTime secondDateTime = getLocalDateTimeByMillSeconds(Long.parseLong(secondMillSeconds));
        return firstDateTime.toLocalDate().equals(secondDateTime.toLocalDate());
    }

获取指定时间,是几号

/**
     * 获取指定时间,是几号
     *
     * @param localDateTime
     * @return
     */
    public static int getDayOfMonth(LocalDateTime localDateTime) {
        return localDateTime.getDayOfMonth();
    }

获取指定时间,是周几

/**
     * 获取指定时间,是周几
     *
     * @param localDateTime
     * @return
     */
    public static int getDayOfWeek(LocalDateTime localDateTime) {
        DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
        return dayOfWeek.getValue();
    }

DateTimeFormatter

/**
     * 完整的日期时间格式
     */
    public static final String COMPLETE_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * 时分秒格式
     */
    public static final String HOUR_MINUTE_SECOND_PATTERN = "HH:mm:ss";

    /**
     * 时分格式
     */
    public static final String HOUR_MINUTE_PATTERN = "HH:mm";

    /**
     * 年月日格式
     */
    public static final String YEAR_MONTH_DAY_PATTERN = "yyyy-MM-dd";

    /**
     * prime减外送费的时间格式
     */
    public static final String PRIME_REDUCE_DELIVERY_HOUR_MINUTE_PATTERN = "HHmm";

    /**
     * 日期时间格式化器
     */
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(COMPLETE_DATE_TIME_PATTERN)
            .withZone(PromoTimeUtil.getZoneId());

    /**
     * 时分秒格式化器
     */
    public static final DateTimeFormatter HOUR_MINUTE_SECOND_FORMATTER = DateTimeFormatter.ofPattern(HOUR_MINUTE_SECOND_PATTERN)
            .withZone(PromoTimeUtil.getZoneId());

    /**
     * 时分秒格式化器
     */
    public static final DateTimeFormatter HOUR_MINUTE_FORMATTER = DateTimeFormatter.ofPattern(HOUR_MINUTE_PATTERN)
            .withZone(PromoTimeUtil.getZoneId());

    /**
     * 年月日格式化器
     */
    public static final DateTimeFormatter YEAR_MONTH_DAY_FORMATTER = DateTimeFormatter.ofPattern(YEAR_MONTH_DAY_PATTERN)
            .withZone(PromoTimeUtil.getZoneId());

    /**
     * prime减外送费的时分格式化器
     */
    public static final DateTimeFormatter PRIME_REDUCE_DELIVERY_FORMATTER = DateTimeFormatter.ofPattern(PRIME_REDUCE_DELIVERY_HOUR_MINUTE_PATTERN)
            .withZone(PromoTimeUtil.getZoneId());

获取日期时间的字符串

/**
     * 获取日期时间的字符串
     *
     * @param localDateTime
     * @return
     */
    public static String getDateTimeStr(LocalDateTime localDateTime) {
        return localDateTime.format(PromoTimeConstants.DATE_TIME_FORMATTER);
    }

获取时间的字符串

/**
     * 获取时间的字符串
     *
     * @param localTime
     * @return
     */
    public static String getCompleteTimeStr(LocalTime localTime) {
        return localTime.format(PromoTimeConstants.HOUR_MINUTE_SECOND_FORMATTER);
    }

将字符串,转为localTime

/**
     * 将字符串,转为localTime
     *
     * @param timeStr
     * @return
     */
    public static LocalTime getLocalTimeByString(String timeStr) {
        return LocalTime.parse(timeStr, PromoTimeConstants.HOUR_MINUTE_FORMATTER);
    }

获取当前年月日

 /**
     * 获取当前年月日
     *
     * @return
     */
    public static LocalDate getNowDate() {
        return LocalDateTime.now().atZone(getZoneId()).toLocalDate();
    }

年月日字符串,转LocalDate

public static LocalDate getDateByStr(String dateStr) {
        if (StringUtils.isEmpty(dateStr)) {
            return null;
        }
        return LocalDate.parse(dateStr, PromoTimeConstants.YEAR_MONTH_DAY_FORMATTER);
    }

时间字符串,转LocalTime

public static LocalTime getTimeByStr(String timeStr) {
        if (StringUtils.isEmpty(timeStr)) {
            return null;
        }
        return LocalTime.parse(timeStr, PromoTimeConstants.HOUR_MINUTE_FORMATTER);
    }

获取指定某天的开始时间

/**
     * 获取指定某天的开始时间
     * @param localDate
     * @return
     */
    public static LocalDateTime getStartOfDay(LocalDate localDate) {
        return LocalDateTime.of(localDate, LocalTime.MIN);
    }

获取指定某天的结束时间

/**
     * 获取指定某天的结束时间
     * @param localDate
     * @return
     */
    public static LocalDateTime getEndOfDay(LocalDate localDate) {
        return LocalDateTime.of(localDate, LocalTime.MAX);
    }

校验时间字符串,是否是指定格式

/**
     * 校验时间字符串,是否是指定格式
     * @param dateStr
     * @param dateTimeFormatter
     * @return
     */
    public static boolean validTimeDatePattern(String dateStr, DateTimeFormatter dateTimeFormatter) {
        if (StringUtils.isBlank(dateStr)) {
            return false;
        }
        TemporalAccessor date = null;
        try {
            date = dateTimeFormatter.parse(dateStr);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return dateStr.equals(dateTimeFormatter.format(date));
    }

计算2个时间的差值

Duration,Period和ChronoUnit,这3个类,都可以用于计算2个时间的差值

其中,Duration精度更高,能计算到相差了多少纳秒;而Period类精度低,能计算到相差了多少天;ChronoUnit是用来表示时间单位的,但是也提供了一些非常有用的between方法来计算两个时间的差值:

Duration的示例如下:

 LocalDateTime now = LocalDateTime.now();


LocalDateTime next = now.plusDays(3).plusHours(1).plusMinutes(10);

Duration between = Duration.between(now, next);
System.out.println(between.toDays());
System.out.println(between.toHours());

输出

3
73

Period的示例如下:

public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();

        LocalDateTime next = now.plusDays(3).plusHours(1).plusMinutes(10);

        Period between = Period.between(now.toLocalDate(), next.toLocalDate());
        System.out.println(between.getDays());

    }

输出

3

ChronoUnit的示例如下:

public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();

        LocalDateTime next = now.plusDays(3).plusHours(1).plusMinutes(10);

        long betweenDay = ChronoUnit.DAYS.between(now, next);
        System.out.println(betweenDay);
        long betweenHour = ChronoUnit.HOURS.between(now, next);
        System.out.println(betweenHour);
    }

输出

3
73

Period 与 Duration

Period

在Java中,Period类是用于表示时间段或周期的类。它通常用于日期和时间的计算和操作,可以方便地处理年、月、日等时间单位。下面是一个如何定义Period的示例:

@Test
public void test() {
    Period period = Period.of(1, 1, 1);//获得一个表示一年一个月一天的时间段
    LocalDate localDate = LocalDate.of(2023, 1, 1);
    LocalDate plus = localDate.plus(period);//计算当前日期加上指定的时间段后的日期
    System.out.println(plus);//输出结果:2024-02-02
    Period period1 = Period.ofYears(1);//获得一个表示一年的时间段
    Period period2 = Period.ofMonths(12);//获得一个表示十二个月的时间段
    Period period3 = Period.ofDays(30);//获得一个表示三十天的时间段
    Period period4 = Period.ofWeeks(1);//获得一个表示两周的时间段
}

核心方法:

Period的核心方法包括:

Period.of(LocalDate startDate, LocalDate endDate):创建一个表示给定时间段的Period对象。
Period.between(LocalDate startDate, LocalDate endDate):创建一个表示给定时间段的Period对象,与起始日期和结束日期的时间顺序无关。
Period.getYears():返回时间段中的年数。
Period.getMonths():返回时间段中的月数。
Period.getDays():返回时间段中的天数。
Period.plus(Period period):将给定的Period对象加到当前Period对象上,返回一个新的Period对象。
Period.minus(Period period):将给定的Period对象从当前Period对象中减去,返回一个新的Period对象。
Period.between(LocalDateTime startDateTime, LocalDateTime endDateTime):创建一个表示给定时间段的Period对象,与起始日期和结束日期的时间顺序无关。

示例:

@Test
public void test1() {
    LocalDate localDate1 = LocalDate.of(2022, 1, 1);
    LocalDate localDate2 = LocalDate.of(2023, 12, 1);
    Period period = Period.between(localDate1, localDate2);
    System.out.println(period.getYears());//输出结果:1
    System.out.println(period.getMonths());//输出结果:11
    System.out.println(period.getDays());//输出结果:0,注意:这里的结果是对应的年、月、日的字段的值的差,与起始日期和结束日期的时间顺序无关
    //如果想要获取两个日期之间的差是多少天,可以这样:
    long days = localDate1.until(localDate2, ChronoUnit.DAYS);
    System.out.println(days);//输出结果:699
}


@Test
public void test2() {
    Period period = Period.ofYears(1);//获得一个表示一年的时间段
    period.plus(Period.ofYears(1));//计算当前时间段加上一个1年时间后的时间段
    period.plusYears(1);//计算当前时间段加上一个1年时间后的时间段
    period.plusMonths(12);//计算当前时间段加上一个12个月时间后的时间段
    period.plusDays(365);//计算当前时间段加上一个365天时间后的时间段
}


@Test
public void test3() {
    Period period = Period.ofYears(1);//获得一个表示一年的时间段
    period.minus(Period.ofYears(1));//计算当前时间段减去一个1年时间后的时间段
    period.minusYears(1);//计算当当前时间段减去一个1年时间周期后的时间段
    period.minusMonths(12);//计算当前时间段减去一个12个月时间周期后的时间段
    period.minusDays(365);//计算当前时间段减去一个365天时间周期后的时间段
}

使用场景:

Period类通常在需要表示和操作年、月、日等较大时间单位的情况下使用。例如,在日历应用程序中,可以使用Period类来计算两个日期之间的差值,或者在计划任务中,可以使用Period类来表示一个任务需要执行的时间段。此外,Period类还可以用于日期时间的格式化和解析,以及进行日历计算等操作。


总结
Period类的优点包括:

易于使用:Period类提供了许多简便的方法来操作时间段,例如plus()、minus()、multiply()、divide()等,使得操作变得更加简单。
适合表示较大时间单位:与Duration类相比,Period类更适合表示年、月、日等较大的时间单位,因为它的粒度更粗,不会因为纳秒等较小的单位而产生精度问题。


Period类的缺点包括:

精度问题:Period类的比较粒度较粗,只能精确到天、月、年等较大的单位,对于需要更高精度的时间段,例如毫秒、纳秒等,Period类无法满足需求。
不支持时区:Period类没有提供时区支持,因此在处理涉及不同时区的日期时间时,Period类无法很好地处理。
无法表示时间点的具体数值:Period类只能表示时间段,无法表示时间点的具体数值,例如当前的日期和时间等。


总之,Period类适用于需要表示和操作年、月、日等较大时间单位的情况,但在需要更高精度的时间段或者涉及不同时区的日期时间处理时,需要考虑使用其他类库。

Duration

Duration 是 Java 中表示时间段的类,用于计算两个时间点之间的间隔。它包含秒(seconds)和纳秒(nanos)两个部分,可以表示毫秒及更小的时间单位。

与 Java 中的其他时间类不同,Duration 不包含毫秒这个属性。

@Test
public void test(){
    Duration duration=Duration.of(60, ChronoUnit.SECONDS);
    LocalDateTime localDateTime = LocalDateTime.of(2023, 1, 1, 13, 1, 1);
    LocalDateTime plus = localDateTime.plus(duration);//计算当前日期时间,加上指定时间段后的日期时间
    System.out.println(plus);//输出结果:2023-01-01T13:02:01
}

与Period类相比,Duration类和Period类都用于表示时间段,但它们在表示的时间单位和使用方式上有所不同。Period类主要用于表示年、月、日等较大的时间单位,而Duration类主要用于表示更小的时间单位,例如毫秒、纳秒等。

此外,Period类提供了一些方法来获取时间段中的年、月、日等信息,而Duration类则提供了更多的方法来进行时间计算和操作。

在实际使用过程中,Duration类和Period类都可以用于表示天的时间段,其他的不行,会抛出异常。

@Test
public void test2(){
    Duration duration=Duration.of(1,ChronoUnit.DAYS);
    Period period = Period.ofDays(1);
    System.out.println(period.getDays() == duration.toDays());
}

核心方法

Duration的核心方法包括:

Duration#of(...)

Duration#of(long duration):这个方法用于创建一个表示给定持续时间的Duration对象,单位为纳秒。持续时间可以是从零到Long.MAX_VALUE之间的任何值。
Duration.of(long amount, TemporalUnit unit) 用于创建表示特定时间单位的持续时间对象。该方法接受两个参数:amount:表示持续时间的长整型数值。unit:表示时间单位的 TemporalUnit 枚举类型或其子类。
Duration#ofDays(long days):这个方法用于创建一个表示给定天数的Duration对象。
Duration#ofHours(long hours):这个方法用于创建一个表示给定小时数的Duration对象。
Duration#ofMinutes(long minutes):这个方法用于创建一个表示给定分钟数的Duration对象。
Duration#ofSeconds(long seconds):这个方法用于创建一个表示给定秒数的Duration对象。
Duration#ofMillis(long millis):这个方法用于创建一个表示给定毫秒数的Duration对象。
Duration#ofNanos(long nanos):这个方法用于创建一个表示给定纳秒数的Duration对象。
@Test
public void test2() {
    Duration duration = Duration.of(1, ChronoUnit.DAYS);
    duration = Duration.ofDays(1);
    duration = Duration.ofHours(24);
    duration = Duration.ofMinutes(60);
    duration = Duration.ofSeconds(60);
    duration = Duration.ofMillis(1000);
    LocalDateTime localDateTime = LocalDateTime.of(2023, 1, 1, 13, 1, 1);
    LocalDateTime plus = localDateTime.plus(duration);//计算当前日期时间加上指定时间段后的日期时间
    System.out.println(plus);//输出结果:2023-01-01T13:01:02
}

Duration#between(...)

Duration.between(LocalDateTime start, LocalDateTime end)用于计算两个 LocalDateTime 对象之间的持续时间。该方法接受两个参数:start:表示起始时间的 LocalDateTime 对象。end:表示结束时间的 LocalDateTime 对象,返回值是Duration对象;
@Test
public void test3() {
    LocalDateTime localDateTime1 = LocalDateTime.of(2023, 1, 1, 13, 1, 1);
    LocalDateTime localDateTime2 = LocalDateTime.of(2023, 1, 1, 15, 1, 1);
    Duration duration = Duration.between(localDateTime1, localDateTime2);
    long seconds = duration.getSeconds();
    System.out.println(seconds);//输出结果:7200
}

Duration#get(...)

Duration#get(...): 此方法返回在给定单位中的持续时间。它接受一个 java.time.temporal.TemporalUnit 参数,并返回该单位的数量。例如,如果你使用 java.time.temporal.ChronoUnit.SECONDS,此方法将返回持续时间中的秒数。
Duration#getUnits(...): 此方法返回此持续时间包含的单位数。这通常与 get() 方法一起使用,以确定持续时间的长度,但不具体到秒或纳秒。例如,如果你有一个持续时间为2天、3小时、4分钟和5秒,那么 getUnits() 将返回一个包含这些单位的 long[] 数组。
Duration#getSeconds(...): 此方法返回此持续时间中的秒数部分。需要注意的是,这与 get(java.time.temporal.ChronoUnit.SECONDS) 方法略有不同,后者将考虑任何溢出到分钟、小时或天数的秒数。
Duration#getNano(...): 此方法返回此持续时间中的纳秒数部分。与秒一样,这不会考虑任何溢出到更高单位的纳秒数。

@Test
public void test4() {
    Duration duration = Duration.ofHours(2);
    List<TemporalUnit> units = duration.getUnits();
    System.out.println(units.toString());//输出结果:[Seconds, Nanos]
    long seconds = duration.get(ChronoUnit.SECONDS);
    System.out.println(seconds);//输出结果:7200
    long seconds1 = duration.getSeconds();
    System.out.println(seconds1);//输出结果:7200
    duration = Duration.ofNanos(10000);
    int nano = duration.getNano();
    System.out.println(nano);//输出结果:10000

Duration#plus(...)

Duration#plus(...)用于将当前Duration对象与另一个Duration对象相加,返回一个新的Duration对象,表示两个时间段的总和。
Duration#plusDays(...)用于将当前Duration对象与指定的天数相加,返回一个新的Duration对象,表示增加天数后的时间段。
Duration#plusHours(...)用于将当前Duration对象与指定的小时数相加,返回一个新的Duration对象,表示增加小时数后的时间段。
Duration#plusMinutes(...)用于将当前Duration对象与指定的分钟数相加,返回一个新的Duration对象,表示增加分钟数后的时间段。
Duration#plusSeconds(...)用于将当前Duration对象与指定的秒数相加,返回一个新的Duration对象,表示增加秒数后的时间段。
Duration#plusMillis(...)用于将当前Duration对象与指定的毫秒数相加,返回一个新的Duration对象,表示增加毫秒数后的时间段。
Duration#plusNanos(...)用于将当前Duration对象与指定的纳秒数相加,返回一个新的Duration对象,表示增加纳秒数后的时间段。
@Test
public void test5() {
    LocalDateTime localDateTime = LocalDateTime.of(2023, 1, 1, 15, 1, 1);
    Duration duration = Duration.ofHours(2);//定义一个2小时的时间段
    duration=duration.plus(60,ChronoUnit.MINUTES);//当前时间段加上60分钟
    duration=duration.plusDays(1);//当前时间段加上1天
    duration=duration.plusHours(1);//当前时间段加上1小时
    duration=duration.plusMinutes(60);//当前时间段加上60分钟
    duration=duration.plusSeconds(60);//当前时间段加上60秒
    LocalDateTime plus = localDateTime.plus(duration);//计算当前日期时间,加上当前的时间段后的日期时间
    System.out.println(plus);//输出结果:2023-01-02T20:02:01
}

Duration#minus(...)

Duration#minus(...): 用于从一个Duration对象中减去另一个Duration对象。它返回一个新的Duration对象,表示当前Duration对象与减去的那一个之间的差值。
Duration#minusDays(...): 用于从一个Duration对象中减去一定数量的天数。它返回一个新的Duration对象,表示当前Duration对象减去指定天数后的结果。
Duration#minusHours(...): 用于从一个Duration对象中减去一定数量的小时数。它返回一个新的Duration对象,表示当前Duration对象减去指定小时数后的结果。
Duration#minusMinutes(...): 用于从一个Duration对象中减去一定数量的分钟数。它返回一个新的Duration对象,表示当前Duration对象减去指定分钟数后的结果。
Duration#minusSeconds(...): 用于从一个Duration对象中减去一定数量的秒数。它返回一个新的Duration对象,表示当前Duration对象减去指定秒数后的结果。
Duration#minusMillis(...): 用于从一个Duration对象中减去一定数量的毫秒数。它返回一个新的Duration对象,表示当前Duration对象减去指定毫秒数后的结果。
Duration#minusNanos(...): 用于从一个Duration对象中减去一定数量的纳秒数。它返回一个新的Duration对象,表示当前Duration对象减去指定纳秒数后的结果。

@Test
public void test6() {
    LocalDateTime localDateTime = LocalDateTime.of(2023, 1, 10, 15, 1, 1);
    Duration duration = Duration.ofHours(2);//定义一个2小时的时间段
    duration = duration.minus(60, ChronoUnit.MINUTES);//当前时间段减去60分钟
    duration = duration.minusDays(1);//当前时间段减去1天
    duration = duration.minusHours(1);//当前时间段减去1小时
    duration = duration.minusMinutes(60);//当前时间段减去60分钟
    duration = duration.minusSeconds(60);//当前时间段减去60秒
    LocalDateTime minus = localDateTime.minus(duration);//计算当前日期时间,减去当前的时间段后的日期时间
    System.out.println(minus);//输出结果:2023-01-11T16:02:01
}

Duration#toDays()

Duration#toDays(): 将Duration对象转换为天数。它返回一个整数值,表示时间段中包含的天数。
Duration#toHours(): 将Duration对象转换为小时数。它返回一个整数值,表示时间段中包含的小时数。
Duration#toMinutes(): 将Duration对象转换为分钟数。它返回一个整数值,表示时间段中包含的分钟数。
Duration#toSeconds(): 将Duration对象转换为秒数。它返回一个整数值,表示时间段中包含的秒数。
Duration#toMillis(): 将Duration对象转换为毫秒数。它返回一个整数值,表示时间段中包含的毫秒数。
Duration#toNanos(): 将Duration对象转换为纳秒数。它返回一个整数值,表示时间段中包含的纳秒数。
@Test
public void test7() {
    Duration duration = Duration.ofDays(1);//定义一个24小时的时间段
    long days = duration.toDays();//结果:1
    long hours = duration.toHours();//结果:24
    long minutes = duration.toMinutes();//结果:1440
    long seconds = duration.toSeconds();//结果:86400
}

文章作者:Administrator

文章链接:http://localhost:8090//archives/javacharsetzonelocaldatetimebestpractise

版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0 许可协议,转载请注明出处!


评论