Mengurai string tanggal di Go


150

Saya mencoba mengurai string tanggal "2014-09-12T11:45:26.371Z"di Go.

Kode

layout := "2014-09-12T11:45:26.371Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout , str)

Saya mendapat kesalahan ini:

waktu parsing "2014-11-12T11: 47: 39.489Z": bulan di luar rentang

Bagaimana cara mengurai string tanggal ini?


1
Untuk pembaca selanjutnya, saya menulis beberapa latihan untuk mempraktikkan penguraian tanggal github.com/soniah/date_practice
Sonia Hamilton

1
Tata letak Anda harus benar-benar sesuai dengan 2006-01-02T15:04:05.000Zstandar gila
tharinduwijewardane

Jawaban:


176

Gunakan nomor tata letak persis seperti yang dijelaskan di sini dan posting blog yang bagus di sini .

begitu:

layout := "2006-01-02T15:04:05.000Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout, str)

if err != nil {
    fmt.Println(err)
}
fmt.Println(t)

memberikan:

>> 2014-11-12 11:45:26.371 +0000 UTC

Aku tahu. Pikiran boggling. Juga menangkap saya pertama kali. Go hanya tidak menggunakan sintaks abstrak untuk komponen datetime ( YYYY-MM-DD), tetapi angka-angka ini tepat ( saya pikir waktu komit pertama go Tidak, menurut ini . Adakah yang tahu?).


128
Nomor tata letak? Apa? Mengapa? Argh!
Darth Egregious

35
Apa yang mereka pikirkan! ? atau merokok?
Jishnu Prathap

14
Saya mungkin sedikit terlambat dengan komentar di sini, tapi jangan takut tata letak dengan angka, cukup gunakan konstanta dan kode Anda akan bersih :) misalnya waktu.RFC3339
Davyd Dzhahaiev

11
Bagi mereka yang tidak mendapatkan nomor tata letak, saya akui itu sangat asing pada pandangan pertama, tetapi setelah Anda terbiasa, saya pikir itu setidaknya masuk akal seperti perangkat tata letak pada umumnya ('Apakah saya menggunakan "D", "d", "dd", "DD", dll?), dan mungkin lebih masuk akal. Anda hanya perlu mengetahuinya dulu.
threeve

6
Ini untuk tujuan mnemonik, yaitu, Anda hanya perlu mengingat 1, 2, 3, 4, 5, 6, 7 huruf ini. Ada artikel bagus yang membahas ini: medium.com/@simplyianm/…
amigcamel

90

Tata letak penggunaan memang " 2006-01-02T15:04:05.000Z" dijelaskan dalam RickyA 's jawaban .
Ini bukan "waktu komit pertama untuk pergi", melainkan cara mnemonik untuk mengingat tata letak tersebut.
Lihat pkg / waktu :

Waktu referensi yang digunakan dalam tata letak adalah:

Mon Jan 2 15:04:05 MST 2006

yang merupakan waktu Unix 1136239445.
Karena MST adalah GMT-0700, waktu referensi dapat dianggap sebagai

 01/02 03:04:05PM '06 -0700

(1,2,3,4,5,6,7, asalkan Anda ingat bahwa 1 untuk bulan itu, dan 2 untuk hari itu, yang tidak mudah bagi orang Eropa seperti saya, terbiasa dengan format tanggal hari-bulan)

Seperti yang diilustrasikan dalam " time.parse: mengapa golang salah mengurai waktu? ", Tata letak itu (menggunakan 1,2,3,4,5,6,7) harus dipatuhi dengan tepat .


Ya, itu juga menarik perhatian orang Australia! MM / DD tidak menghitung untuk saya dan saya harus terus melihatnya.
Simon Whitehead

3
@SonWhead Saya setuju. Setidaknya, setelah saya mencarinya, saya tahu apa kepanjangan YY, MM, DD, hh, mm, ss dan saya dapat memesan ulang dengan mudah. Dengan Go, bahkan setelah mencarinya, saya perlu mengingat arti 1, 2, 3, 4 ....
VonC

1
Cara saya mengingatnya adalah: Langkah 1) Pengurutan tanggal yang "tepat" adalah tahun> bulan> hari> jam> menit> detik> dll. (Karena mixed-endian hanya akan berubah-ubah dan tidak konsisten dan untuk tanggal besar- endian lebih disukai daripada little-endian karena mudah disortir.) Ini akan memberi kita 1 (tahun), 2 (bulan), 3 (hari), [...] Langkah 2) Go / Google adalah orang Amerika dan orang Amerika menggunakan tahun di akhir tanggal mereka, jadi bukan 1 (bulan), 2 (hari), [...], n (tahun) Langkah 3) Zona waktu mengikuti yang lainnya karena zona waktu adalah lapisan abstraksi tambahan.
mtraceur

@mtraceur Ya ... Saya juga merindukan web.archive.org/web/20180501100155/http://… (dari github.com/bdotdub/fuckinggodateformat ). Maksudku, strftimeFTW.
VonC

60

Sebagai jawaban tetapi untuk menghemat pengetikan "2006-01-02T15:04:05.000Z"untuk tata letak, Anda dapat menggunakan konstanta RFC3339 dari paket .

str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(time.RFC3339, str)

if err != nil {
    fmt.Println(err)
}
fmt.Println(t)

https://play.golang.org/p/Dgu2ZvHwTh


1
Selain itu, jika Anda melihat konstanta paket (ditautkan dalam jawaban di atas) ada banyak format umum lain yang tersedia yang dapat digunakan. Jika Anda membutuhkan sesuatu yang sedikit berbeda, gunakan itu sebagai titik awal.
Hugh

Ini dan beberapa jawaban merekomendasikan 2006-01-02T15:04:05.000Zdan menyebutkan bahwa Go time.RFC3339juga akan bekerja. Tapi time.RFC3339 = "2006-01-02T15:04:05Z07:00". Apakah kedua format ini persis sama sejauh apa time.Parsedan time.ParseInLocationakan dilakukan?
Miles

1
Benar @Miles, tes ini menegaskannya play.golang.org/p/T3dW1kTeAHl
robstarbuck

25

Saya akan menyarankan menggunakan konstanta time.RFC3339 dari paket waktu. Anda dapat memeriksa konstanta lain dari paket waktu. https://golang.org/pkg/time/#pkg-constants

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("Time parsing");
    dateString := "2014-11-12T11:45:26.371Z"
    time1, err := time.Parse(time.RFC3339,dateString);
    if err!=nil {
    fmt.Println("Error while parsing date :", err);
    }
    fmt.Println(time1); 
}

Apakah Anda bermaksud menggunakan titik koma?
ChristoKiwi

23

Ini agak terlambat untuk pesta, dan tidak benar-benar mengatakan apa pun yang belum dikatakan dalam satu bentuk atau lainnya, sebagian besar melalui tautan di atas, tetapi saya ingin memberikan rekap TL; DR kepada mereka yang kurang perhatian:

Tanggal dan waktu string format go sangat penting. Begitulah cara Go mengetahui bidang yang mana. Mereka umumnya 1-9 dari kiri ke kanan sebagai berikut:

  • Januari / Jan / januari / jan / 01 / _1 (dll) adalah untuk bulan
  • 02 / _2 adalah untuk hari dalam sebulan
  • 15/03 / _3 / PM / P / pm / p adalah untuk jam & meridian (3pm)
  • 04 / _4 adalah untuk menit
  • 05 / _5 untuk detik
  • 2006/06 adalah untuk tahun
  • -0700 / 07:00 / MST adalah untuk zona waktu
  • .999999999 / .000000000 dll adalah untuk detik parsial (menurut saya perbedaannya adalah jika nol di belakangnya dihilangkan)
  • Sen / Senin adalah hari dalam seminggu (yang sebenarnya adalah 01-02-2006),

Jadi, Jangan tulis "01-05-15" sebagai format tanggal Anda, kecuali Anda menginginkan "Bulan-Jam-Kedua"

(... sekali lagi, ini pada dasarnya adalah ringkasan di atas.)


6

Ini mungkin sangat terlambat, tetapi ini untuk orang-orang yang mungkin tersandung pada masalah ini dan mungkin ingin menggunakan paket eksternal untuk mengurai string tanggal.

Saya sudah mencoba mencari perpustakaan dan saya menemukan yang ini:

https://github.com/araddon/dateparse

Contoh dari README:

package main

import (
    "flag"
    "fmt"
    "time"

    "github.com/apcera/termtables"
    "github.com/araddon/dateparse"
)

var examples = []string{
    "May 8, 2009 5:57:51 PM",
    "Mon Jan  2 15:04:05 2006",
    "Mon Jan  2 15:04:05 MST 2006",
    "Mon Jan 02 15:04:05 -0700 2006",
    "Monday, 02-Jan-06 15:04:05 MST",
    "Mon, 02 Jan 2006 15:04:05 MST",
    "Tue, 11 Jul 2017 16:28:13 +0200 (CEST)",
    "Mon, 02 Jan 2006 15:04:05 -0700",
    "Thu, 4 Jan 2018 17:53:36 +0000",
    "Mon Aug 10 15:44:11 UTC+0100 2015",
    "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
    "12 Feb 2006, 19:17",
    "12 Feb 2006 19:17",
    "03 February 2013",
    "2013-Feb-03",
    //   mm/dd/yy
    "3/31/2014",
    "03/31/2014",
    "08/21/71",
    "8/1/71",
    "4/8/2014 22:05",
    "04/08/2014 22:05",
    "4/8/14 22:05",
    "04/2/2014 03:00:51",
    "8/8/1965 12:00:00 AM",
    "8/8/1965 01:00:01 PM",
    "8/8/1965 01:00 PM",
    "8/8/1965 1:00 PM",
    "8/8/1965 12:00 AM",
    "4/02/2014 03:00:51",
    "03/19/2012 10:11:59",
    "03/19/2012 10:11:59.3186369",
    // yyyy/mm/dd
    "2014/3/31",
    "2014/03/31",
    "2014/4/8 22:05",
    "2014/04/08 22:05",
    "2014/04/2 03:00:51",
    "2014/4/02 03:00:51",
    "2012/03/19 10:11:59",
    "2012/03/19 10:11:59.3186369",
    // Chinese
    "2014年04月08日",
    //   yyyy-mm-ddThh
    "2006-01-02T15:04:05+0000",
    "2009-08-12T22:15:09-07:00",
    "2009-08-12T22:15:09",
    "2009-08-12T22:15:09Z",
    //   yyyy-mm-dd hh:mm:ss
    "2014-04-26 17:24:37.3186369",
    "2012-08-03 18:31:59.257000000",
    "2014-04-26 17:24:37.123",
    "2013-04-01 22:43",
    "2013-04-01 22:43:22",
    "2014-12-16 06:20:00 UTC",
    "2014-12-16 06:20:00 GMT",
    "2014-04-26 05:24:37 PM",
    "2014-04-26 13:13:43 +0800",
    "2014-04-26 13:13:44 +09:00",
    "2012-08-03 18:31:59.257000000 +0000 UTC",
    "2015-09-30 18:48:56.35272715 +0000 UTC",
    "2015-02-18 00:12:00 +0000 GMT",
    "2015-02-18 00:12:00 +0000 UTC",
    "2017-07-19 03:21:51+00:00",
    "2014-04-26",
    "2014-04",
    "2014",
    "2014-05-11 08:20:13,787",
    // mm.dd.yy
    "3.31.2014",
    "03.31.2014",
    "08.21.71",
    //  yyyymmdd and similar
    "20140601",
    // unix seconds, ms
    "1332151919",
    "1384216367189",
}

var (
    timezone = ""
)

func main() {
    flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone")
    flag.Parse()

    if timezone != "" {
        // NOTE:  This is very, very important to understand
        // time-parsing in go
        loc, err := time.LoadLocation(timezone)
        if err != nil {
            panic(err.Error())
        }
        time.Local = loc
    }

    table := termtables.CreateTable()

    table.AddHeaders("Input", "Parsed, and Output as %v")
    for _, dateExample := range examples {
        t, err := dateparse.ParseLocal(dateExample)
        if err != nil {
            panic(err.Error())
        }
        table.AddRow(dateExample, fmt.Sprintf("%v", t))
    }
    fmt.Println(table.Render())
}

2
Sayangnya, ada ambiguitas dalam urutan hari bulan. Format tanggal Eropa memberikan hari pertama dan bulan kedua, dan format waktu AS menggunakan kebalikannya. Tanggal seperti 3/5/2004 bersifat ambigu. Tanggal berlaku dalam format AS dan Eropa, tetapi 3 dan 5 mungkin sesuai dengan hari dan bulan atau sebaliknya. dateParse menganggap format AS.
chmike

-2

Jika Anda telah bekerja dengan pemformatan / penguraian waktu / tanggal dalam bahasa lain, Anda mungkin telah memperhatikan bahwa bahasa lain menggunakan placeholder khusus untuk pemformatan waktu / tanggal. Untuk misalnya penggunaan bahasa ruby

%d for day
%Y for year

dll. Golang, alih-alih menggunakan kode seperti di atas, menggunakan placeholder format tanggal dan waktu yang terlihat seperti tanggal dan waktu saja. Go menggunakan waktu standar, yaitu:

Mon Jan 2 15:04:05 MST 2006  (MST is GMT-0700)
or 
01/02 03:04:05PM '06 -0700

Jadi jika Anda memperhatikan Go menggunakan

01 for the day of the month,
02 for the month
03 for hours,
04 for minutes
05 for second
and so on

Oleh karena itu, misalnya untuk penguraian 2020-01-29, string tata letak harus 06-01-02 atau 2006-01-02.

Anda dapat merujuk ke tabel tata letak placeholder lengkap di tautan ini - https://golangbyexample.com/parse-time-in-golang/

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.