LINUX.ORG.RU

golang time.Parse - очень странное поведение...

 


2

3
package main

import (
  "fmt"
  "time"
)

const (
	layout = "2006-01-02 15:04:05"
//  	layout = "2006-01-02 14:04:05"
)

func main() {
  dt := "2022-12-13 14:15:16"
  tm, err := time.Parse(layout, dt)
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(tm)
}

Ответ:

2022-12-13 14:15:16 +0000 UTC
Меняю layout.
Было:
layout на "2006-01-02 15:04:05"
Стало:
layout на "2006-01-02 14:04:05"
Т.е. всего одна цифра изменена!
package main

import (
  "fmt"
  "time"
)

const (
//      layout = "2006-01-02 15:04:05"
  	layout = "2006-01-02 14:04:05"
)

func main() {
  dt := "2022-12-13 14:15:16"
  tm, err := time.Parse(layout, dt)
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(tm)
}
Ответ:
parsing time "2022-12-13 14:15:16": month out of range
0001-01-01 00:00:00 +0000 UTC
А почему?!! :)

Пример нашёл здесь, но внятного объяснения так и не получил, не понял...

★★★

Ответ на: комментарий от firkax

А там и нет объяснения. А ты пробовал компилировать и запускать? Может там просто враньё?

Конечно, я копировал код из своей программы, где меняю всего одну строчку, а результат разный. Может это баг golang какой-то?

Windows ★★★
() автор топика

нифига не знаю go, но полюбопытствовал через песочницу https://go.dev/play/

очевидно что в layout - «магическое» числа :-) всегда 15:04:05 пятнадцать часов, 4 минуты 5 секунд..можешь менять местами, задавать другие разделители, убирать нули ; но не можешь указать иные значения

у нормальных людей hh:mm:ss а в golang так вот, им так «читабельнее» :-)

MKuznetsov ★★★★★
()
Последнее исправление: MKuznetsov (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

у нормальных людей hh:mm:ss а в golang так вот, им так «читабельнее» :-)

Я тоже так подумал :) но это не так :) ... А может и так, но маловероятно :) И самое главное - это не объясняет вопрос нашей темы. Там же загвоздка, как я понял в следующем:

2022-12-13 - где здесь год, месяц и дата, точнее где год - понятно. А где месяц и дата? Кто из них первый сразу за годом?

Когда

layout = "2006-01-02 14:04:05"
golang почему-то думает, что число 13 из блока
dt := "2022-12-13 14:15:16"
- это месяц...

или...

golang почему-то думает, что дата представлена блоком цифр
14:15:16
, в котором все цифры не могут быть месяцем.

Windows ★★★
() автор топика
Последнее исправление: Windows (всего исправлений: 9)
Ответ на: комментарий от Windows

«2006-01-02 15:04:05» - это магическое число, точнее - строка, точнее - дата.

и даже хуже того..шаблон 2006-02-01 (день и месяц поменять местами) НЕ ВОСПРИНИМАЕТСЯ..

в общем - надо парсить руками

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Не, там всё нормально - https://go.dev/play/p/28qqts2ArxC
2006-01-02 - это тоже магическая дата, где 01 - всегда месяц.
Все популярные магические даты можно посмотреть здесь (прокрутить вниз)

Windows ★★★
() автор топика
Последнее исправление: Windows (всего исправлений: 1)
Ответ на: комментарий от Windows

Не, там всё нормально - https://go.dev/play/p/28qqts2ArxC 2006-01-02 - это тоже магическая дата, где 01 - всегда месяц.

меняем в шаблоне 2006-01-02 на 2006-02-01 и вуалля:

[code] ./prog.go:16:24: 2006-02-01 should be 2006-01-02 [/code]

то есть в go физически нельзя задать шаблон yyyy-dd-mm; это с какими мега-скилами надо быть архитекторам библиотек чтобы придумать такую хрень через магические цифры

с такими закидонами идея «а не попробовать ли где-нить go» пропала наглухо :-)

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Да, как-то странно. Выходит не всё можно определить через магические числа даты.

В golang только следующие константы:

DateTime = «2006-01-02 15:04:05»
DateOnly = «2006-01-02»
TimeOnly = «15:04:05»

Всё остальное через нотации типа - %Y%m%d.

Наверное, это ограничение для чтобы не плодить обильное количество магических констант.

Golang - хороший, не бойтесь его :) Можете пробовать :)

Windows ★★★
() автор топика
Последнее исправление: Windows (всего исправлений: 1)

чтобы минимизировать затраты на парсинг в го сделали фиксированный layout с конкретным значением даты.

https://pkg.go.dev/time#Parse

есть предопределенные лейауты-константы

https://pkg.go.dev/time#pkg-constants

главный смысл в том, чтобы использовать конкретную дату-время в описании лейаута.

другими словами, чтобы ты понял смысл…

при парсинге проверяется значение числа

  • если = 1 или 01, то это месяц
  • если = 2 или 02, то это число месяца
  • если = 4 или 04, то это минуты

и тд

ergo ★★★
()
Последнее исправление: ergo (всего исправлений: 2)
Ответ на: комментарий от MKuznetsov

с такими закидонами идея «а не попробовать ли где-нить go» пропала наглухо :-)

это не «закидоны», а вполне объяснимый подход - вместо использований мнемоник для каждого элемента даты они взяли числа

01/02 03:04:05PM '06 -0700
1   2  3  4  5     6   7 
M   D  h  m  s  year  zone/offset
ergo ★★★
()
Последнее исправление: ergo (всего исправлений: 2)
Ответ на: комментарий от ergo

при парсинге проверяется значение числа: если = 1 или 01, то это месяц, если = 2 или 02, то это число месяца, если = 4 или 04, то это минуты

Да, с этим разобрался, спасибо. Но непонятна ошибка из первого сообщения. Почему там layout = «2006-01-02 15:04:05» - всё хорошо,
а layout = «2006-01-02 14:04:05» - плохо. Я же как-бы говорю - блок 2006-01-02 - это дата (есть 01 - месяц, 02 - число), следовательно - всё остальное время. А он ругается и даёт «странный» ответ - «parsing time „2022-12-13 14:15:16“: month out of range 0001-01-01 00:00:00 +0000 UTC».

Почему он «подумал», что 13 из переменной dt - это месяц? Или на что он вообще ругается? На 14:15:16? Или на layout = «2006-01-02 14:04:05»?

Windows ★★★
() автор топика
Ответ на: комментарий от ergo

Когда вы поменяли час 15 на 14, вы сломали лейаут. Парсер не понимает что с этим делать

вот есть же у людей чуйка прекрасного :-)

и опыт написания столь стабильных парсеров…

мне не понять :-(

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Если на формат в данном контексте смотреть не как на string, а как на SuperDuperFormat то концептуально все получается то же самое как в Яве, например — у тебя есть возможность самому построить SuperDuperFormat/string и есть пару предопределенных экземпляров (констант). Автор темы просто построил неправильный SuperDuperFormat/string.

urxvt ★★★★★
()

Вообще нелогично сделали, я считаю. Год нужно делать или 99 или 1999. Тут разночтений не будет. Месяц должен быть «12». День месяца должен быть «31». Час должен быть или «11» (PM) или «23». Вот минуты и секунды - тут да, разделить логически не выйдет, поэтому «58», «59» сделать. Миллисекунды - «999», микросекунды - «999999». То бишь каждое значение это максимальное или примерно максимальное значение соответствующего поля. Запутаться можно только между часами и месяцами, но обычно из контекста понятно, ну а парсер точно не запутается.

31.12.1999 23:58:59.999
1999-12-31 11:58:59.999999 PM

По сути идея хорошая, а реализация плохая.

Хотя тут я не продумывал, что будет, если писать слитно. Бывает и такое. Лень думать.

vbr ★★★
()
Ответ на: комментарий от urxvt

Если на формат в данном контексте смотреть не как на string, а как на SuperDuperFormat то концептуально все получается то же самое как в Яве, например — у тебя есть возможность самому построить SuperDuperFormat/string и есть пару предопределенных экземпляров (констант). Автор темы просто построил неправильный SuperDuperFormat/string.

а какая к хрену разница что думал автор или что на него снисходило, если сиё вошло в «стандратную библиотеку» и RTL ?

оно всей сутью своего существования намекает что дальше+глубже будет только хуже и темнее. Что думала принимающая в RTL сторона кроме секс-наркотики-рокнрол :-)

Какая-нить процедура принятия решений наверное была ведь. Вот она дала сбой. Она вообще известна ?; Принята практика с неочевидным поведением и неведомыми последствиями. Дальше копать страшно аж жуть..

MKuznetsov ★★★★★
()

А еще можно брать и читать документацию вместо Васянов.

https://pkg.go.dev/time

These are predefined layouts for use in Time.Format and time.Parse. The reference time used in these layouts is the specific time stamp:

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

(January 2, 15:04:05, 2006, in time zone seven hours west of GMT).

GFORGX ★★★
()
Последнее исправление: GFORGX (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

и даже хуже того..шаблон 2006-02-01 (день и месяц поменять местами) НЕ ВОСПРИНИМАЕТСЯ..

Шаблон «2006-02-01» спокойно работает. Это go vet бросает ошибку. Но никто ведь не заставляет его запускать.

thegoldone
()
Ответ на: комментарий от Windows

Почему он «подумал», что 13 из переменной dt - это месяц?

Потому что 14-ти нет. И он рассматривает 14 как 1 и 4. Потому что это не запрещено. Получается месяц и минуты ожидаются на входе. И соотв. ошибка. В общем-то всё верно.

Собственно – https://go.dev/play/p/eDPL3in5-F0
И – https://go.dev/play/p/eTk3xra_LmH

thegoldone
()
Последнее исправление: thegoldone (всего исправлений: 2)
Ответ на: комментарий от thegoldone

Потому что 14-ти нет. И он рассматривает 14 как 1 и 4. Потому что это не запрещено. Получается месяц и минуты ожидаются на входе. И соотв. ошибка. В общем-то всё верно.

Так... ещё раз...

- если = 1 или 01, то это месяц
- если = 2 или 02, то это число месяца
- если = 4 или 04, то это минуты

layout «2006-01-02 15:04:05» - OK
layout «2006-01-02 14:04:05» - not OK

dt := «2022-12-13 14:15:16»

Допустим, 14 он рассматривает, как 1 - месяц, 4 - минуты. Хорошо, пусть так, но!.. Текст ошибки:

«parsing time „2022-12-13 14:15:16“: month out of range 0001-01-01 00:00:00 +0000 UTC».

В этом случае, он ругается на число 13. Верно? И думает, что это месяц. Верно?

Если он воспринял, что месяц - это то, что в числе layout 14 (первая цифра 1), почему он думает, что 13 (число в анализируемой дате dt) - это тоже месяц?

Или он думает, что какое-то число в анализируемой дате dt (14, 15, 16) - это месяц? По Вашей логике на первое число 14 он не должен так думать, потому что 4 - это минуты. По поводу 15, 16 - я не знаю, может ли он подумать, что это месяц?..

В общем вопрос в следующем - на какую цифру он подумал месяц и поэтому ругнулся?

Windows ★★★
() автор топика
Последнее исправление: Windows (всего исправлений: 12)
Ответ на: комментарий от Windows

В этом случае, он ругается на число 13. Верно? И думает, что это месяц. Верно?

Ну конечно же нет. 13 – это день (число). Которому соотв. 02.

thegoldone
()
Последнее исправление: thegoldone (всего исправлений: 1)
Ответ на: комментарий от Windows

Если совсем подробно, то так

layout = 2006-01-02 14:04:05
input  = 2022-12-13 14:15:16

Слева направо

2006 -> 2022
01   -> 12
02   -> 13
1    -> 14 -- month out of range ([1; 12])

В остатке

layoutTail = 4:04:05
inputTail  = :15:16

Ожидает месяц. Берёт 14. Возвращает ошибку. Всё как и должно быть.

thegoldone
()

Просто какой-то чудак решил что нужно придумать более тупой способ описания формата даты.

В go формат «2006-01-02 15:04:05» то же что в питоне «%Y-%m-%d %H:%M:%S».

go / python
2006 == %Y == год
01 == %m == месяц
02 == %d == день
15 == %H == час
04 == %M == минута
05 == %S == секунда

https://pkg.go.dev/time#pkg-constants

karaien ★★
()
Ответ на: комментарий от Windows

Может это баг golang какой-то?

О, я тоже с этой фигней сталкивался. Долго не мог понять, че не так, а я там дату написал текущую по сути, а не какой-то условный 2006 год, и все упало. Просто в итоге вернул формат на тот, который был в доках.

Причина — да хрен знает. Я тогда подумал, что он там просто прибит гвоздями.

Zhbert ★★★★★
()
Ответ на: комментарий от thegoldone

Получается, он допускает такой вариант, что в input может дважды встречаться цифра, обозначающая месяц? А такое вообще в природе бывает? Или он тупо думает: единичка - значит месяц! И пофиг, что месяц будет дважды!?

Windows ★★★
() автор топика
Последнее исправление: Windows (всего исправлений: 3)
Ответ на: комментарий от thegoldone

Да, дважды, трижды, сколько угодно. Будет последнее значение в итоге. Но все они д.б. подходящими.

Понял! Думаю, уж теперь-то всё ясно - пазлы сложились :) Благодарю!

Windows ★★★
() автор топика
6 сентября 2023 г.