AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 497639
Accepted
confetti
confetti
Asked: 2019-01-31 00:30:29 +0800 CST2019-01-31 00:30:29 +0800 CST 2019-01-31 00:30:29 +0800 CST

Subtrair o tempo usando data e bash

  • 772

Todas as outras questões na rede SE tratam de cenários em que a data é assumida como now( Q ) ou onde apenas uma data é especificada ( Q ).

O que eu quero fazer é fornecer uma data e hora e, em seguida, subtrair uma hora disso.
Aqui está o que eu tentei primeiro:

date -d "2018-12-10 00:00:00 - 5 hours - 20 minutes - 5 seconds"

Isso resulta em 2018-12-10 06:39:55- Adicionou 7 horas. Em seguida, subtraiu 20:05 minutos.

Depois de ler a página mane de , pensei ter corrigido com isso:infodate

date -d "2018-12-10T00:00:00 - 5 hours - 20 minutes - 5 seconds"

Mas, mesmo resultado. De onde ele tira as 7 horas?

Tentei outras datas também porque pensei que talvez tivéssemos 7200 segundos bissextos naquele dia, quem sabe lol. Mas mesmos resultados.

Mais alguns exemplos:

$ date -d "2018-12-16T00:00:00 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-17_02:00:00

$ date -d "2019-01-19T05:00:00 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-19_08:55:00

Mas aqui torna-se interessante. Se eu omitir o tempo na entrada, funciona bem:

$ date -d "2018-12-16 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-15_00:00:00

$ date -d "2019-01-19 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-18_21:55:00

$ date --version
date (GNU coreutils) 8.30

o que estou perdendo?

Atualização: adicionei um Zno final e isso mudou o comportamento:

$ date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_04:00:00

Eu ainda estou confuso embora. Não há muito sobre isso na página de informações do GNU sobre data.

Suponho que este seja um problema de fuso horário, mas citando o The Calendar Wiki na ISO 8601 :

Se nenhuma informação de relação UTC for fornecida com uma representação de hora, assume-se que a hora está na hora local.

Que é o que eu quero. Minha hora local também está definida corretamente. Não sei por que date mexeria com o fuso horário neste caso simples de eu fornecer um datetime e querer subtrair algo dele. Não deveria subtrair as horas da string de data primeiro? Mesmo que ele converta para uma data primeiro e depois faça a subtração, se eu deixar de fora qualquer subtração, recebo exatamente o que quero:

$ date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S
2019-01-19_05:00:00

Então, se isso realmente for um problema de fuso horário, de onde vem essa loucura?

date gnu
  • 5 5 respostas
  • 31699 Views

5 respostas

  • Voted
  1. Best Answer
    Olorin
    2019-01-31T00:51:30+08:002019-01-31T00:51:30+08:00

    Esse último exemplo deve ter esclarecido as coisas para você: fusos horários .

    $ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
    2019-01-19_03:00:00
    $ TZ=Asia/Colombo date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S 
    2019-01-19_08:30:00
    

    Como a saída varia claramente de acordo com o fuso horário, suspeito que algum padrão não óbvio seja usado para uma string de tempo sem um fuso horário especificado. Testando alguns valores, parece ser UTC-05:00 , embora eu não tenha certeza do que é isso.

    $ TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
    2019-01-19_08:00:00UTC
    $ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
    2019-01-19_03:00:00UTC
    $ TZ=UTC date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S%Z           
    2019-01-19_05:00:00UTC
    

    É usado apenas ao realizar aritmética de data.


    Parece que o problema aqui é que não- 2 hours é tomado como aritmética, mas como um especificador de fuso horário :

    # TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
    date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
    date: parsed relative part: +1 hour(s)
    date: input timezone: parsed date/time string (-02)
    date: using specified time as starting value: '05:00:00'
    date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
    date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 epoch-seconds
    date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
    date:     new time = 1547884800 epoch-seconds
    date: timezone: TZ="UTC" environment value
    date: final: 1547884800.000000000 (epoch-seconds)
    date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC)
    date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC+00)
    2019-01-19_08:00:00UTC
    

    Portanto, não apenas não há aritmética sendo feita, mas parece haver um ajuste de 1 hora no horário de verão , levando a um tempo um tanto sem sentido para nós.

    Isso também vale para adição:

    # TZ=UTC date -d "2019-01-19T05:00:00 + 5:30 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
    date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC+05:30
    date: parsed relative part: +1 hour(s)
    date: input timezone: parsed date/time string (+05:30)
    date: using specified time as starting value: '05:00:00'
    date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30'
    date: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30' = 1547854200 epoch-seconds
    date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
    date:     new time = 1547857800 epoch-seconds
    date: timezone: TZ="UTC" environment value
    date: final: 1547857800.000000000 (epoch-seconds)
    date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC)
    date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC+00)
    2019-01-19_00:30:00UTC
    

    Depurando um pouco mais, a análise parece ser: 2019-01-19T05:00:00 - 2( -2sendo o fuso horário) e hours(= 1 hora), com uma adição implícita. Fica mais fácil ver se você usar minutos em vez disso:

    # TZ=UTC date -d "2019-01-19T05:00:00 - 2 minutes" +%Y-%m-%d_%H:%M:%S%Z --debug
    date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
    date: parsed relative part: +1 minutes
    date: input timezone: parsed date/time string (-02)
    date: using specified time as starting value: '05:00:00'
    date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
    date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 epoch-seconds
    date: after time adjustment (+0 hours, +1 minutes, +0 seconds, +0 ns),
    date:     new time = 1547881260 epoch-seconds
    date: timezone: TZ="UTC" environment value
    date: final: 1547881260.000000000 (epoch-seconds)
    date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC)
    date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC+00)
    2019-01-19_07:01:00UTC
    

    Então, bem, a aritmética de datas está sendo feita, mas não a que pedimos. ¯\(ツ)/¯

    • 28
  2. pLumo
    2019-01-31T00:47:41+08:002019-01-31T00:47:41+08:00

    Funciona corretamente quando você converte a data de entrada para ISO 8601 primeiro:

    $ date -d "$(date -Iseconds -d "2018-12-10 00:00:00") - 5 hours - 20 minutes - 5 seconds"
    So 9. Dez 18:39:55 CET 2018
    
    • 8
  3. mr.spuratic
    2019-02-01T04:26:44+08:002019-02-01T04:26:44+08:00

    O GNU datesuporta aritmética de data simples, embora os cálculos de tempo de época, como mostrado na resposta do @sudodus, sejam às vezes mais claros (e mais portáteis).

    O uso de +/- quando não há fuso horário especificado no carimbo de data/hora aciona uma tentativa de corresponder a um fuso horário a seguir, antes que qualquer outra coisa seja analisada.

    Aqui está uma maneira de fazer isso, use "ago" em vez de "-":

    $ date -d "2018-12-10 00:00:00 5 hours ago 20 minutes ago 5 seconds ago"
    Sun Dec  9 18:39:55 GMT 2018
    

    ou

    $ date -d "2018-12-10 00:00:00Z -5 hours -20 minutes -5 seconds"
    Sun Dec  9 18:39:55 GMT 2018
    

    (Embora você não possa usar "Z" arbitrariamente, ele funciona na minha zona, mas isso o torna um carimbo de data/hora da zona UTC/GMT - use sua própria zona ou %z/%Z anexando ${TZ:-$(date +%z)}ao carimbo de data/hora.)

    Adicionando termos de tempo extra destes formulários ajusta o tempo:

    • "5 horas atrás" subtrair 5 horas
    • "4 horas" adicionar (implícito) 4 horas
    • "3 horas a partir de agora" adicionar (explícito) 3 horas (não suportado em versões mais antigas)

    Muitos ajustes complexos, em qualquer ordem, podem ser usados ​​(embora termos relativos e variáveis ​​como "14 semanas a partir da última segunda-feira" estejam pedindo problemas ;-)

    (Tem outra pequena armadilha de urso aqui também, datesempre dará uma data válida, então date -d "2019-01-31 1 month"dá 2019-03-03, assim como "próximo mês")

    Dada a grande variedade de formatos de hora e data que são suportados, a análise de fuso horário é necessariamente desleixada: pode ser um sufixo de uma ou várias letras, um deslocamento de hora ou hora:minuto, um nome "América/Denver" (ou mesmo um nome de arquivo no caso da TZvariável).

    Sua 2018-12-10T00:00:00versão não funciona porque "T" é apenas um delimitador, não um fuso horário, adicionar "Z" no final faz com que funcione (sujeito à correção da zona escolhida) conforme o esperado também.

    Veja: https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html e em particular a seção 7.7.

    • 6
  4. nxnev
    2019-01-31T01:47:11+08:002019-01-31T01:47:11+08:00

    TLDR: Isso não é um bug. Você acabou de encontrar um dos comportamentos sutis, mas documentados do date. Ao realizar aritmética de tempo com date, use um formato independente de fuso horário (como a hora do Unix) ou leia com muito cuidado a documentação para saber como usar corretamente este comando.


    O GNU dateusa as configurações do seu sistema (a TZvariável de ambiente ou, se não definida, o padrão do sistema) para determinar o fuso horário da data alimentada com a opção -d/ --datee a data relatada pelo +formatargumento. A --dateopção também permite que você substitua o fuso horário por seu próprio argumento de opção, mas não substitui o fuso horário de +format. Essa é a raiz da confusão, IMHO.

    Considerando que meu fuso horário é UTC-6, compare os seguintes comandos:

    $ date -d '1970-01-01 00:00:00' '+Normal: %F %T %:z%nUnix: %s'
    Normal: 1970-01-01 00:00:00 -06:00
    Unix: 21600
    
    $ date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
    Normal: 1969-12-31 18:00:00 -06:00
    Unix: 0
    
    $ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
    Normal: 1970-01-01 00:00:00 +00:00
    Unix: 0
    

    O primeiro usa meu fuso horário para -de +format. O segundo usa UTC para -dmas meu fuso horário para +format. O terceiro usa UTC para ambos.

    Agora, compare as seguintes operações simples:

    $ date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
    Normal: 1970-01-01 18:00:00 -06:00
    Unix: 86400
    
    $ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
    Normal: 1970-01-02 00:00:00 +00:00
    Unix: 86400
    

    Mesmo que o horário do Unix esteja me dizendo a mesma coisa, o horário "Normal" difere por causa do meu próprio fuso horário.

    Se eu quisesse fazer a mesma operação, mas usando exclusivamente meu fuso horário:

    $ TZ='CST+6' date -d '1970-01-01 00:00:00 -06:00 +1 day' '+Normal: %F %T %:z%nUnix: %s'
    Normal: 1970-01-02 00:00:00 -06:00
    Unix: 108000
    
    • 5
  5. sudodus
    2019-01-31T05:53:43+08:002019-01-31T05:53:43+08:00

    Esta solução é fácil de entender, mas um pouco mais complicada, então eu a mostro como um shellscript.

    • converter para 'segundos desde 1970-01-01 00:00:00 UTC'
    • adicionar ou subtrair a diferença
    • converter de volta para um formato legível por humanos com uma datelinha de comando final

    Shellscript:

    #!/bin/bash
    
    startdate="2018-12-10 00:00:00"
    
    ddif="0"          # days
    diff="-5:-20:-5"  # hours:minutes:seconds
    
    #-----------------------------------------------------------------------------
    
    ss1970in=$(date -d "$startdate" "+%s")  # seconds since 1970-01-01 00:00:00 UTC
    printf "%11s\n" "$ss1970in"
    
    h=${diff%%:*}
    m=${diff#*:}
    m=${m%:*}
    s=${diff##*:}
    difs=$(( (((ddif*24+h)*60)+m)*60+s ))
    printf "%11s\n" "$difs"
    
    ss1970ut=$((ss1970in + difs))  # add/subtract the time difference
    printf "%11s\n" "$ss1970ut"
    
    date -d "@$ss1970ut" "+%Y-%m-%d %H:%M:%S"
    
    • 1

relate perguntas

  • Como remover uma parte de uma coluna usando awk

  • A data pode formatar a hora atual para o fuso horário GMT? [duplicado]

  • Obter todas as datas acessadas de um arquivo

  • bsdtar: Como evitar sobrescrever as informações do arquivo existente?

  • gpg — o comando list-keys gera uid [ desconhecido ] depois de importar a chave privada para uma instalação limpa

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve