การแยกสตริงวันที่ใน Go


150

ฉันลองแยกวิเคราะห์สตริงวันที่"2014-09-12T11:45:26.371Z"ใน Go

รหัส

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

ฉันได้รับข้อผิดพลาดนี้:

เวลาในการแยกวิเคราะห์ "2014-11-12T11: 47: 39.489Z": เดือนที่อยู่นอกช่วง

ฉันจะแยกวิเคราะห์สตริงวันที่นี้ได้อย่างไร


1
สำหรับผู้อ่านในอนาคตฉันได้เขียนแบบฝึกหัดสำหรับฝึกการแยกวิเคราะห์วันที่github.com/soniah/date_practice
Sonia Hamilton

1
เลย์เอาต์ของคุณควรเป็น2006-01-02T15:04:05.000Zไปตามมาตรฐานที่บ้าคลั่ง
tharinduwijewardane

คำตอบ:


176

ใช้รูปแบบตัวเลขที่แน่นอนที่อธิบายไว้ที่นี่และดี blogpost ที่นี่

ดังนั้น:

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)

ให้:

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

ฉันรู้ว่า. ใจสั่น จับฉันครั้งแรกด้วย Go ไม่ได้ใช้ไวยากรณ์นามธรรมสำหรับคอมโพเนนต์ datetime ( YYYY-MM-DD) แต่เป็นตัวเลขที่แน่นอนเหล่านี้ ( ฉันคิดว่าเวลาของการกระทำครั้งแรกของ go Nope ตามนี้มีใครรู้บ้างไหม)


128
เค้าโครงตัวเลข? อะไร? ทำไม? อ๊าก!
Darth Egregious

35
พวกเขาคิดอะไรอยู่! เหรอ? หรือสูบบุหรี่?
Jishnu Prathap

14
ฉันอาจจะช้าไปหน่อยกับความคิดเห็นที่นี่ แต่อย่ากลัวเลย์เอาต์ที่มีตัวเลขเพียงแค่ใช้ค่าคงที่และรหัสของคุณจะสะอาด :) เช่นเวลา RFC3339
Davyd Dzhahaiev

11
สำหรับผู้ที่ไม่ได้รับหมายเลขเลย์เอาต์ฉันยอมรับว่ามันแปลกมากในตอนแรก แต่เมื่อคุณคุ้นเคยแล้วฉันคิดว่าอย่างน้อยก็สมเหตุสมผลพอ ๆ กับอุปกรณ์เลย์เอาต์ทั่วไป ('ฉันใช้ "D" หรือไม่ "d", "dd", "DD" ฯลฯ ) และน่าจะมีความหมายมากกว่านี้ คุณต้องรู้เรื่องนี้ก่อน
threeve

6
มีไว้เพื่อจุดประสงค์ในการช่วยจำนั่นคือคุณต้องจำ 1, 2, 3, 4, 5, 6, 7 ตัวอักษรเหล่านี้ มีบทความดีๆพูดถึงเรื่องนี้: medium.com/@simplyianm/…
amigcamel

90

รูปแบบการใช้งานที่เป็นจริง " 2006-01-02T15:04:05.000Z" อธิบายไว้ในRickyA 's คำตอบ
ไม่ใช่ "เวลาของการเดินทางครั้งแรก" แต่เป็นวิธีช่วยจำในการจำเค้าโครงดังกล่าว
ดูpkg / เวลา :

เวลาอ้างอิงที่ใช้ในเค้าโครงคือ:

Mon Jan 2 15:04:05 MST 2006

1136239445ซึ่งเป็นเวลาที่ใช้ระบบปฏิบัติการยูนิกซ์
เนื่องจาก MST คือ GMT-0700 เวลาอ้างอิงจึงสามารถคิดเป็น

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

(1,2,3,4,5,6,7 โดยที่คุณจำไว้ว่า 1 สำหรับเดือนนั้นและ 2 สำหรับวันซึ่งไม่ใช่เรื่องง่ายสำหรับคนยุโรปอย่างฉันที่ใช้รูปแบบวันที่แบบวันเดือน)

ดังแสดงใน " time.parse: ทำไม golang แยกเวลาที่ไม่ถูกต้อง " รูปแบบที่ (ใช้ 1,2,3,4,5,6,7) จะต้องได้รับการเคารพว่า


ใช่ที่ดึงดูดชาวออสเตรเลียได้เช่นกัน! MM / DD ไม่ได้คำนวณสำหรับฉันและฉันต้องดูต่อไป
Simon Whitehead

3
@SimonWhitehead ฉันเห็นด้วย อย่างน้อยเมื่อฉันค้นดูฉันรู้ว่า YY, MM, DD, hh, mm, ss หมายถึงอะไรและฉันสามารถสั่งซื้อใหม่ได้อย่างง่ายดาย ด้วย Go แม้ว่าจะมองขึ้นไปแล้วฉันก็ต้องจำสิ่งที่ 1, 2, 3, 4 ...
VonC

1
วิธีที่ฉันจำได้คือขั้นตอนที่ 1)การเรียงลำดับวันที่ "เหมาะสม" คือปี> เดือน> วัน> ชั่วโมง> นาที> วินาที> ฯลฯ (เนื่องจากแบบผสมจะไม่เป็นไปตามอำเภอใจและไม่สอดคล้องกันและสำหรับวันที่มีขนาดใหญ่ endian เป็นที่ต้องการมากกว่า little-endian เพราะเป็นมิตรกับสิ่งแวดล้อม) ซึ่งจะทำให้เราได้ 1 (ปี) 2 (เดือน) 3 (วัน) [... ] ขั้นตอนที่ 2) Go / Google เป็นคนอเมริกันและคนอเมริกัน ปีเมื่อสิ้นสุดวันที่ดังนั้นแทนที่จะเป็น 1 (เดือน), 2 (วัน), [... ], n (ปี) ขั้นตอนที่ 3)เขตเวลาจะตามหลังสิ่งอื่น ๆ เนื่องจากเขตเวลาเป็นเลเยอร์นามธรรมเพิ่มเติม
mtraceur

@mtraceur ใช่ ... ฉันก็คิดถึงweb.archive.org/web/20180501100155/http://… (from github.com/bdotdub/fuckinggodateformat ) ฉันหมายถึงstrftimeFTW
VonC

60

ในฐานะที่ตอบ แต่จะบันทึกการพิมพ์ออกมา"2006-01-02T15:04:05.000Z"สำหรับรูปแบบที่คุณสามารถใช้อย่างต่อเนื่องของแพคเกจRFC3339

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
นอกจากนี้หากคุณดูค่าคงที่ของแพ็คเกจ (ลิงก์ในคำตอบด้านบน) จะมีรูปแบบทั่วไปอื่น ๆ อีกมากมายที่สามารถใช้ได้ หากคุณต้องการสิ่งที่แตกต่างเล็กน้อยให้ใช้เป็นจุดเริ่มต้น
Hugh

คำตอบนี้และหลายคำแนะนำ2006-01-02T15:04:05.000Zและกล่าวถึงว่า Go's time.RFC3339ก็ใช้ได้เช่นกัน แต่time.RFC3339 = "2006-01-02T15:04:05Z07:00". สองรูปแบบนี้เท่ากันทุกประการหรือไม่time.Parseและtime.ParseInLocationจะทำอย่างไร
ไมล์

1
ถูกต้อง @Miles การทดสอบนี้ยืนยันว่า play.golang.org/p/T3dW1kTeAHl
robstarbuck

25

ฉันจะแนะนำให้ใช้เวลาค่าคงที่ RFC3339 จากแพ็คเกจเวลา คุณสามารถตรวจสอบค่าคงที่อื่น ๆ จากแพ็คเกจเวลา 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); 
}

คุณหมายถึงใช้อัฒภาคหรือไม่?
ChristoKiwi

23

สิ่งนี้ค่อนข้างช้าสำหรับงานปาร์ตี้และไม่ได้พูดอะไรที่ยังไม่ได้พูดในรูปแบบใดรูปแบบหนึ่งส่วนใหญ่ผ่านลิงก์ด้านบน แต่ฉันต้องการให้ TL; DR สรุปให้กับผู้ที่มีช่วงความสนใจน้อย:

วันที่และเวลาของสตริงรูปแบบ go มีความสำคัญมาก Go รู้ได้อย่างไรว่าสนามไหนคือสนามไหน โดยทั่วไปจะเป็น 1-9 จากซ้ายไปขวาดังนี้:

  • มกราคม / มกราคม / มกราคม / มกราคม / 01 / _1 (ฯลฯ ) เป็นของเดือน
  • 02 / _2 เป็นวันของเดือน
  • 15/03 / _3 / PM / P / pm / p เป็นชั่วโมง & เที่ยง (15.00 น.)
  • 04 / _4 เป็นนาที
  • 05 / _5 เป็นวินาที
  • 2006/06 เป็นของปี
  • -0700 / 07:00 / MST ใช้สำหรับเขตเวลา
  • .999999999 / .000000000 ฯลฯ เป็นวินาทีบางส่วน (ฉันคิดว่าความแตกต่างคือถ้าเอาเลขศูนย์ต่อท้ายออก)
  • จันทร์ / จันทร์เป็นวันของสัปดาห์ (ซึ่งเป็นวันที่ 01-02-2006)

ดังนั้นอย่าเขียน "01-05-15" เป็นรูปแบบวันที่ของคุณเว้นแต่คุณจะต้องการ "เดือน - วินาที - ชั่วโมง"

(... อีกครั้งนี่เป็นบทสรุปของด้านบน)


6

สิ่งนี้อาจล่าช้าไปมาก แต่สำหรับผู้ที่อาจพบปัญหานี้และอาจต้องการใช้แพ็คเกจภายนอกเพื่อแยกวิเคราะห์สตริงวันที่

ฉันพยายามมองหาห้องสมุดและพบสิ่งนี้:

https://github.com/araddon/dateparse

ตัวอย่างจาก 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
น่าเสียดายที่มีความไม่ชัดเจนในลำดับวันเดือน รูปแบบวันที่ในยุโรปให้วันที่หนึ่งและเดือนที่สองและรูปแบบเวลาของสหรัฐอเมริกาใช้การย้อนกลับ วันที่เช่น 3/5/2004 มีความคลุมเครือ วันที่ถูกต้องในรูปแบบสหรัฐอเมริกาและยุโรป แต่ 3 และ 5 อาจตรงกับวันและเดือนหรือย้อนกลับ dateParseถือว่าเป็นรูปแบบของสหรัฐอเมริกา
chmike

-2

หากคุณเคยใช้การจัดรูปแบบเวลา / วันที่ / การแยกวิเคราะห์ในภาษาอื่น ๆ คุณอาจสังเกตเห็นว่าภาษาอื่นใช้ตัวยึดพิเศษสำหรับการจัดรูปแบบเวลา / วันที่ เช่นการใช้ภาษาทับทิม

%d for day
%Y for year

ฯลฯ Golang แทนที่จะใช้รหัสดังกล่าวข้างต้นใช้ตัวยึดรูปแบบวันที่และเวลาที่มีลักษณะเหมือนวันที่และเวลาเท่านั้น Go ใช้เวลามาตรฐานซึ่งก็คือ:

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

ดังนั้นหากคุณสังเกตเห็น Go ใช้

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

ดังนั้นตัวอย่างเช่นสำหรับการแยกวิเคราะห์ 2020-01-29 สตริงโครงร่างควรเป็น 06-01-02 หรือ 2006-01-02

คุณสามารถดูตารางเลย์เอาต์ตัวยึดแบบเต็มได้ที่ลิงค์นี้ - https://golangbyexample.com/parse-time-in-golang/

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.