?【Java深層系列】「技術(shù)盲區(qū)」讓我們一起完全吃透針對(duì)于時(shí)間和日期相關(guān)的API指南

技術(shù)簡(jiǎn)介

java中的日期處理一直是個(gè)問題,沒有很好的方式去處理,所以才有第三方框架的位置比如joda。文章主要對(duì)java日期處理的詳解,用1.8可以不用joda。

時(shí)間概念

首先我們對(duì)一些基本的概念做一些介紹,其中可以將GMT和UTC表示時(shí)刻大小等同。

UT時(shí)間

UT反應(yīng)了地球自轉(zhuǎn)的平均速度。是通過觀測(cè)星星來測(cè)量的。

UTC

UTC是用原子鐘時(shí)間做參考,但保持和UT1在0.9秒內(nèi)的時(shí)間,也就是說定時(shí)調(diào)整。

目前采用的時(shí)間標(biāo)準(zhǔn)是世界協(xié)調(diào)時(shí)UTC(Universal Time Coordinated)。如果計(jì)算機(jī)不聯(lián)網(wǎng)即使再精確也是不準(zhǔn)的,因?yàn)閁TC會(huì)進(jìn)行調(diào)整,而且一般走的時(shí)間也是不精確的。

NTP

現(xiàn)在計(jì)算機(jī)一般用的網(wǎng)絡(luò)時(shí)間協(xié)議NTP(Network Time Protocol)是用于互聯(lián)網(wǎng)中時(shí)間同步的標(biāo)準(zhǔn)互聯(lián)網(wǎng)協(xié)議。用途是把計(jì)算機(jī)的時(shí)間同步到某些時(shí)間標(biāo)準(zhǔn)。

GMT(UT1)

GMT是完全符合地球自轉(zhuǎn)的時(shí)間,也被稱為UT1,格林尼治標(biāo)準(zhǔn)時(shí)間被用作英國(guó)的民用時(shí)間,或UTC。GMT被稱為“UT1”,它直接對(duì)應(yīng)于地球的自轉(zhuǎn),并受到該自轉(zhuǎn)輕微不規(guī)則的影響。正是UT1和UTC之間的差異通過應(yīng)用閏秒保持>低于0.9秒。

ISO 8601

一種時(shí)間交換的國(guó)際格式。有些接口調(diào)用表示UTC/GMT時(shí)間的時(shí)候用"yyyy-MM-dd'T'HH:mm:ss'Z'"格式顯示。帶毫秒格式"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"。

joda中實(shí)現(xiàn)如下
// Alternate ISO 8601 format without fractional seconds
private static final String ALTERNATIVE_ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
  private static DateFormat getAlternativeIso8601DateFormat() {
        SimpleDateFormat df = new SimpleDateFormat(ALTERNATIVE_ISO8601_DATE_FORMAT, Locale.US);
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df;
  }

RFC 822

STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES

其中ARPA網(wǎng)絡(luò)其實(shí)就是互聯(lián)網(wǎng)的前身。

有些地方會(huì)用RFC 822里的時(shí)間格式,格式如下

 date-time = [ day "," ] date time ; dd mm yy
                                                     ; hh:mm:ss zzz
第二個(gè)相當(dāng)于現(xiàn)在格式
"EEE, dd MMM yyyy HH:mm:ss z"

有些頭設(shè)置采用該格式。

joda中實(shí)現(xiàn)如下

// RFC 822 Date Format
private static final String RFC822_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
private static DateFormat getRfc822DateFormat() {
        SimpleDateFormat rfc822DateFormat =
                new SimpleDateFormat(RFC822_DATE_FORMAT, Locale.US);
        rfc822DateFormat.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return rfc822DateFormat;
}

創(chuàng)建SimpleDateFormat的Locale.US可以決定格式字符串某些字符的代替用哪個(gè)語言,比如EEE等。

SimpleDateFormat df1=new SimpleDateFormat("GGGG yyyy/MMMM/dd HH:mm:ss EEE aaa zzzz",Locale.CHINA);
SimpleDateFormat df2=new SimpleDateFormat("GGGG yyyy/MMMM/dd HH:mm:ss EEE aaa zzzz",Locale.US);
//公元 2016/三月/27 23:32:10 星期日 下午 中國(guó)標(biāo)準(zhǔn)時(shí)間
//AD 2016/March/27 23:32:10 Sun PM China Standard Time

gregorian Calendar, julian Calendar:這是兩種歷法,我們一般用的通用的gregorian Calendar。

jdk1.8之前

主要的類有記錄時(shí)間戳的Date,時(shí)間和日期進(jìn)行轉(zhuǎn)換的Calendar,用來格式化和解析時(shí)間字符串的DateFormat

java.util.Date

使用前要注意時(shí)間表示的規(guī)則。

還有這個(gè)類有很多過期方法不推薦使用,很多已經(jīng)被Calendar代替。

構(gòu)造方法

這個(gè)類代表某個(gè)時(shí)刻的毫秒值,既然是毫秒值也就說需要有一個(gè)參考值。

在接受或返回年、月、日期、小時(shí)、分鐘和秒值的所有類日期方法中,使用以下表示形式:

年份y由整數(shù)y-1900表示。一個(gè)月由0到11的整數(shù)表示;0是一月,1是二月,依此類推;因此,11月是12月。日期(月的某一天)通常由1到31之間的整數(shù)表示。小時(shí)由0到23之間的整數(shù)表示。因此,從午夜到凌晨1點(diǎn)的時(shí)間是0小時(shí),從中午到下午1點(diǎn)的時(shí)間是12小時(shí)。一分鐘通常由0到59之間的整數(shù)表示。第二個(gè)由0到61之間的整數(shù)表示;值60和61僅在閏秒內(nèi)出現(xiàn),甚至僅在實(shí)際正確跟蹤閏秒的Java實(shí)現(xiàn)中出現(xiàn)。由于目前引入閏秒的方式,在同一分鐘內(nèi)出現(xiàn)兩個(gè)閏秒的可能性極低,但本規(guī)范遵循ISO C的日期和時(shí)間約定。

當(dāng)我們創(chuàng)建一個(gè)Date的時(shí)候獲取的是哪一個(gè)毫秒值?

public Date() {
        this(System.currentTimeMillis());
 }
 public Date(long date) {
      fastTime = date;
}

System.currentTimeMillis()是本地方法,the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC。

這個(gè)可能會(huì)因?yàn)椴僮飨到y(tǒng)的時(shí)間而不準(zhǔn)。有些操作系統(tǒng)不一定是用毫秒表示的。這個(gè)時(shí)間都是用的UTC時(shí)間,不和時(shí)區(qū)有關(guān)的,這個(gè)無關(guān)的意思是同一時(shí)刻每個(gè)時(shí)區(qū)下獲得的值應(yīng)該是一致的,可以簡(jiǎn)單用程序驗(yàn)證一下獲取的時(shí)間表達(dá)內(nèi)容。

long time = System.currentTimeMillis();
System.out.println(time=(time/1000));
System.out.println("秒:"+ time%60);
System.out.println(time=(time/60));
System.out.println("分鐘:"+time%60);
System.out.println(time=(time/60));
System.out.println("小時(shí):"+time%24);

可以理解成和UTC的1970年1月1日零點(diǎn)的差值。而fastTime就是Date類保存這個(gè)時(shí)刻的變量。

成員變量

Date對(duì)象打印出來是本地時(shí)間,而構(gòu)造方法是沒有時(shí)區(qū)體現(xiàn)的。那么哪里體現(xiàn)了時(shí)區(qū)呢?

下面是Date的成員變量

gcal

獲取的是以下的對(duì)象。其中并沒有自定義字段。可以說只是一個(gè)gregorian(公歷)時(shí)間工廠獲取CalendarDate的子類。

jcal

在以下方法中用到

private static final BaseCalendar getCalendarSystem(BaseCalendar.Date cdate) {
        if (jcal == null) {
            return gcal;
        }
        if (cdate.getEra() != null) {
            return jcal;
        }
        return gcal;
    }
    synchronized private static final BaseCalendar getJulianCalendar() {
        if (jcal == null) {
            jcal = (BaseCalendar) CalendarSystem.forName("julian");
        }
        return jcal;
    }

當(dāng)時(shí)間戳在以下情況下用儒略歷,并且,在用到的時(shí)候會(huì)自動(dòng)設(shè)置儒略歷,所以在clone的時(shí)候也沒有這個(gè)參數(shù)。所以這個(gè)可以忽略。

 private static final BaseCalendar getCalendarSystem(int year) {
        if (year >= 1582) {
            return gcal;
        }
        return getJulianCalendar();
    }
    private static final BaseCalendar getCalendarSystem(long utc) {
        // Quickly check if the time stamp given by `utc' is the Epoch
        // or later. If it's before 1970, we convert the cutover to
        // local time to compare.
        if (utc >= 0
            || utc >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER
                        - TimeZone.getDefaultRef().getOffset(utc)) {
            return gcal;
        }
        return getJulianCalendar();
    }

fastTime

保存了一個(gè)時(shí)間戳表示時(shí)刻。最重要的參數(shù)。創(chuàng)建Date就是對(duì)這個(gè)值的賦值。

cdate

保存了時(shí)間相關(guān)內(nèi)容,包括時(shí)區(qū),語言等

    public static final int FIELD_UNDEFINED = -2147483648;
    public static final long TIME_UNDEFINED = -9223372036854775808L;
    private Era era;
    private int year;
    private int month;
    private int dayOfMonth;
    private int dayOfWeek;
    private boolean leapYear;
    private int hours;
    private int minutes;
    private int seconds;
    private int millis;
    private long fraction;
    private boolean normalized;
    private TimeZone zoneinfo;
    private int zoneOffset;
    private int daylightSaving;
    private boolean forceStandardTime;
    private Locale locale;
defalutCenturyStart

這個(gè)值可以忽略,在過期方法中用到。

@Deprecated
    public static long parse(String s) {
   ... ...
            // Parse 2-digit years within the correct default century.
            if (year < 100) {
                synchronized (Date.class) {
                    if (defaultCenturyStart == 0) {
                        defaultCenturyStart = gcal.getCalendarDate().getYear() - 80;
                    }
                }
                year += (defaultCenturyStart / 100) * 100;
                if (year < defaultCenturyStart) year += 100;
            }
            ... ...
    }

serialVersionUID

驗(yàn)證版本一致性的UID

wtb

保存toString格式化用到的值

ttb

保存toString 格式化用到的值

主要方法

image

java.util.Calendar

主要也是其中保存的毫秒值time字段,下面是我們常用的方法,用了默認(rèn)的時(shí)區(qū)和區(qū)域語言:

public static Calendar getInstance() {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }

國(guó)內(nèi)環(huán)境默認(rèn)GregorianCalendar,但是TH-th用的BuddhistCalendar等
一些坑:

set(int,int,int,int,int,int)方法

方法不能設(shè)置毫秒值,所以當(dāng)用getInstance后即使用設(shè)置相同的值,最后毫秒值也是不一致的。所以如果有需要,將MILLISECOND清零。

set,add,get,roll

set方法不會(huì)馬上計(jì)算時(shí)間,指是修改了對(duì)應(yīng)的成員變量,只有g(shù)et()、getTime()、getTimeInMillis()、add() 或 roll()的時(shí)候才會(huì)做調(diào)整

        //2000-8-31
        Calendar cal1 = Calendar.getInstance();
        cal1.set(2000, 7, 31, 0, 0 , 0);
        //應(yīng)該是 2000-9-31,也就是 2000-10-1
        cal1.set(Calendar.MONTH, Calendar.SEPTEMBER);
        //如果 Calendar 轉(zhuǎn)化到 2000-10-1,那么現(xiàn)在的結(jié)果就該是 2000-10-30
        cal1.set(Calendar.DAY_OF_MONTH, 30);
        //輸出的是2000-9-30,說明 Calendar 不是馬上就刷新其內(nèi)部的記錄
        System.out.println(cal1.getTime());

也就是說多次設(shè)置的時(shí)候如果中間有需要調(diào)整的時(shí)間,但是實(shí)際是不會(huì)做調(diào)整的。所以盡量將無法確定的設(shè)置之后不要再進(jìn)行其他調(diào)整,防止最后實(shí)際值與正常值不準(zhǔn)。

add方法會(huì)馬上做時(shí)間修改

roll與add類似,但是roll不會(huì)修改更大的字段的值。

java.text.SimpleDateFormat

創(chuàng)建設(shè)置pattern字符串,可以表示的格式如下:

image

日期格式是不同步的。建議為每個(gè)線程創(chuàng)建獨(dú)立的格式實(shí)例。如果多個(gè)線程同時(shí)訪問一個(gè)格式,則它必須是外部同步的。

SimpleDateFormat 是線程不安全的類,其父類維護(hù)了一個(gè)Calendar,調(diào)用相關(guān)方法有可能會(huì)修改Calendar。一般不要定義為static變量,如果定義為 static,必須加鎖,或者使用 DateUtils 工具類。 正例:注意線程安全,使用 DateUtils。org.apache.commons.lang.time.DateUtils,也推薦如下處理:

private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { 
        @Override 
        protected DateFormat initialValue() { 
                return new SimpleDateFormat("yyyy-MM-dd"); 
        }
 };

java.sql.Date/Time/Timestamp

這幾個(gè)類都繼承了java.util.Date。

相當(dāng)于將java.util.Date分開表示了。Date表示年月日等信息。Time表示時(shí)分秒等信息。Timestamp多維護(hù)了納秒,可以表示納秒。

如果是 JDK8 的應(yīng)用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替SimpleDateFormat,官方給出的解釋:simple beautiful strong immutable thread-safe。

jdk1.8的時(shí)間類

1.8增加了新的date-time包,遵循JSR310。核心代碼主要放在java.time包下。默認(rèn)的日歷系統(tǒng)用的ISO-8601(基于格里高利歷)。
java.time下主要內(nèi)容包括:

java.time -主要包括,日期,時(shí)間,日期時(shí)間,時(shí)刻,期間,和時(shí)鐘相關(guān)的類。

  • java.time.chrono -其他非ISO標(biāo)準(zhǔn)的日歷系統(tǒng)可以用java.time.chrono,里面已經(jīng)定義了一部分年表,你也可以自定義。
  • java.time.format -格式化和解析日期時(shí)間的類
  • java.time.temporal -擴(kuò)展API,主要是提供給寫框架和寫庫(kù)的人,允許日期時(shí)間相互操作,訪問,和調(diào)整。字段和單位在這個(gè)包下定義。
  • java.time.zone -定義了時(shí)區(qū),相對(duì)于時(shí)區(qū)的偏移量,時(shí)區(qū)規(guī)則等。

該包的API提供了大量相關(guān)的方法,這些方法一般有一致的方法前綴:

  • of:靜態(tài)工廠方法。
  • parse:靜態(tài)工廠方法,關(guān)注于解析。
  • get:獲取某些東西的值。
  • is:檢查某些東西的是否是true。
  • with:不可變的setter等價(jià)物。
  • plus:加一些量到某個(gè)對(duì)象。
  • minus:從某個(gè)對(duì)象減去一些量。
  • to:轉(zhuǎn)換到另一個(gè)類型。
  • at:把這個(gè)對(duì)象與另一個(gè)對(duì)象組合起來,例如: date.atTime(time)。

相互轉(zhuǎn)化和Instant

可以看到老的時(shí)間日期類里面都有了Instant的轉(zhuǎn)化。Instant可以說是新舊轉(zhuǎn)換的中轉(zhuǎn)站。Instant主要維護(hù)了秒和納秒字段,可以表示納秒范圍。當(dāng)然不支持的話會(huì)拋出異常。主要還是java.util.Date轉(zhuǎn)換成新的時(shí)間類。

Clock

提供了訪問當(dāng)前時(shí)間的方法,也可以獲取當(dāng)前Instant。Clock是持有時(shí)區(qū)或者時(shí)區(qū)偏移量的。如果只是獲取當(dāng)前時(shí)間戳,推薦還是用System.currentTimeMillis()

ZoneId/ZoneOffset/ZoneRules

zone id 主要包括兩個(gè)方面,一個(gè)是相對(duì)于對(duì)于UTC/Greenwich的固定偏移量相當(dāng)于一個(gè)大時(shí)區(qū),另一個(gè)是時(shí)區(qū)內(nèi)有特殊的相對(duì)于UTC/Greenwich偏移量的地區(qū)。通常固定偏移量部分可以用ZoneOffset表示,用normalized()判斷是否可以用ZoneOffset表示。判斷主要用到了時(shí)區(qū)規(guī)則ZoneRules。時(shí)區(qū)的真正規(guī)則定義在ZoneRules中,定義了什么時(shí)候多少偏移量。使用這種方式是因?yàn)镮D是固定不變的,但是規(guī)則是政府定義并且經(jīng)常變動(dòng)。

LocalDateTime/LocalTime/LocalDate/ZoneDateTime

LocalDateTIme/LocalTime/LocalDate都是沒有時(shí)區(qū)概念的。這句話并不是
說不能根據(jù)時(shí)區(qū)獲取時(shí)間,而是因?yàn)檫@些類不持有表示時(shí)區(qū)的變量。而
ZoneDateTime持有時(shí)區(qū)和偏移量變量。

這些類都可以對(duì)時(shí)間進(jìn)行修改其實(shí)都是生成新對(duì)象。所以這里的時(shí)間類都是天然支持多線程的。

這些時(shí)間類中都提供了獲取時(shí)間對(duì)象,修改時(shí)間獲取新的時(shí)間對(duì)象,格式化時(shí)間等。

注意點(diǎn)

LocaDateTime的atZone是調(diào)整本地時(shí)間的時(shí)區(qū)的。并不會(huì)改變時(shí)間。要使用其他時(shí)間需要獲取的LocalDateTime.now的時(shí)候的就要傳入時(shí)區(qū)變量。

DateTimeFormatter

時(shí)間對(duì)象進(jìn)行格式化時(shí)間的需要用到格式化和解析日期和時(shí)間的時(shí)候需要用到DateTimeFormatter。

擴(kuò)展及思考

用SimpleDateFormat格式化的時(shí)候不要用12小時(shí)制即hh,因?yàn)楹苋菀讓?dǎo)致上午下午不分,比如“2017-01-01 00:00:00“可能就變顯示成”2017-01-01 12:00:00”
::符號(hào)

LocalDateTime的方法
public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) {
    Objects.requireNonNull(formatter, "formatter");
    return formatter.parse(text, LocalDateTime::from);
}
parse調(diào)用的方法是
public <T> T parse(CharSequence text, TemporalQuery<T> query) {
   ... ...
}
LocalDateTime::from調(diào)用的方法是
public static LocalDateTime from(TemporalAccessor temporal) {
    .... ...     
}

其中temporal是LocalDateTime的接口

這里其實(shí)大家都有一個(gè)疑問就是LocalDateTime::from到底代表什么意思。

LocalDateTime::from
//與下列表示相同
x ->  LocalDateTime.from(x)
//相當(dāng)于
new TemporalQuery<LocalDateTime>(){
       @Override
        public LocalDateTime queryFrom(TemporalAccessor temporal) {
             return LocalDateTime.from(temporal);
        }
};
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,481評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,697評(píng)論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,182評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,406評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,933評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,772評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,973評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,209評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評(píng)論 1 285
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,644評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,953評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容