HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

各言語で日時文字列の解析

手元にある各言語で、標準入力から日時文字列を読み込んで、標準出力にUNIX TIME値を吐き出すプログラムを書いてみようと思ったメモ。

要件は以下の通り。

  • 入力される日時文字列は1つのみ
    • 不正入力のチェックは不要とする
  • OS等のタイムゾーンJST
  • 対応するUNIX TIME値を出力
    • 例えば、「2017/12/31 12:34:56 +0900」が入力されたら「1514723696」を出力

環境

手元にあるものということで、環境は以下のものに限定する。

入力ファイルの例

  • 001.txt
2017/12/31 12:34:56 +0900

期待される出力の例

  • 001.txt
1514723696

Java

import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Scanner;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        try (Scanner in = new Scanner(System.in);
            PrintWriter out = new PrintWriter(System.out)
        ) {
            String str = in.nextLine();

            DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss ZZZZZ");
            Date date = df.parse(str);

            out.println(date.getTime() / 1000);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

C

#define _XOPEN_SOURCE
#include <stdio.h>
#include <time.h>

int main(int argc, char** argv) {
    char str[32];
    fgets(str, sizeof(str) - 1, stdin);

    struct tm timeinfo;
    strptime(str, "%Y/%m/%d %H:%M:%S %z", &timeinfo);
    long second = mktime(&timeinfo);

    printf("%d\n", second);

    return 0;
}

strptimeを使ったソースを警告も出さないようにコンパイルするためには、マクロ定数を定義してやる必要がある。

上記を見ると、%zについては、「glibc での注意」として書いてあるが、「サポートさせようとしているが、多くの場合、tmフィールドは変更されない」らしい、つまり無視される、と。 実際、「+0000」を与えて試してみたら、見事に無視されて、tmフィールドのタイムゾーン情報には"JST"が入っていた・・・

C++

#include <ctime>
#include <iostream>

using namespace std;

int main(int argc, char** argv) {
    char str[32];
    cin.getline(str, sizeof(str));

    struct tm timeinfo;
    strptime(str, "%Y/%m/%d %H:%M:%S %z", &timeinfo);
    long second = mktime(&timeinfo);

    cout << second << endl;

    return EXIT_SUCCESS;
}

上記を見ると、%zについては、「glibc での注意」として書いてあるが、「サポートさせようとしているが、多くの場合、tmフィールドは変更されない」らしい、つまり無視される、と。 実際、「+0000」を与えて試してみたら、見事に無視されて、tmフィールドのタイムゾーン情報には"JST"が入っていた・・・

PHP

(2017/12/07)DateTimeクラス版を書いてみたので追記。大して変わらないけど(ぉ

strtotime()版

<?php

$lines = file('php://stdin');
$str = $lines[0];

date_default_timezone_set("Asia/Tokyo");
$second = strtotime($str);

printf("%d\n", $second);

DateTimeクラス版

<?php

$lines = file('php://stdin');
$str = $lines[0];

date_default_timezone_set("Asia/Tokyo");
$dt = new DateTime($str);
$second = $dt->getTimestamp();

printf("%d\n", $second);

Python 2

import sys
import re
from datetime import datetime
import time

str = sys.stdin.readline()
str = re.sub(r' [+][0-9][0-9][0-9][0-9][\r]*\n', "", str)

dt = datetime.strptime(str, "%Y/%m/%d %H:%M:%S")
second = int(time.mktime(dt.timetuple()))

print second

最初、%zを入れたら、ValueError: 'z' is a bad directive in format '%Y/%m/%d %H:%M:%S %z'って怒られた‥

次に、%zを外したら、ValueError: unconverted data remains: +0900って怒られた‥

なので、時差を指定している部分を正規表現による文字列置換で削除‥当然、JST扱いされるので、「+0000」を与えたテストケースは通らない‥

Python 3

import sys
import re
from datetime import datetime
import time

str = sys.stdin.readline()
str = re.sub(r'[\r]*\n', "", str)

dt = datetime.strptime(str, "%Y/%m/%d %H:%M:%S %z")
second = int(dt.timestamp())

print(second)

Ruby

require 'time'

str = STDIN.gets

second = Time.parse(str).to_i

print second,"\n"

Perl

DateTime::Format::Strptime版

use DateTime::Format::Strptime;

my $str = readline(STDIN);

my $strp = DateTime::Format::Strptime->new(
    pattern => "%Y/%m/%d %H:%M:%S %z"
);
my $second = $strp->parse_datetime($str)->epoch;

print $second,"\n";

これをやるには、「yum install perl-DateTime-Format-Strptime」をしておく必要がある。

Time::Piece版

use Time::Piece;

my $str = readline(STDIN);
chomp($str);

my $dt = Time::Piece->strptime($str, "%Y/%m/%d %H:%M:%S %z");
my $second = $dt->epoch;

print $second,"\n";

これをやるには、「yum install perl-Time-Piece」をしておく必要がある。

Go

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "time"
)

func main() {
    stdin := bufio.NewReader(os.Stdin)
    buf := make([]byte, 0, 1024)
    str := ""
    for {
        line, prefix, err := stdin.ReadLine()
        if err == io.EOF {
            break
        }
        buf = append(buf, line...)
        if prefix {
            continue
        }
        str = string(buf)
        break
    }

    t, err := time.Parse("2006/01/02 15:04:05 -0700", str)
    if err != nil {
        panic(err)
    }

    fmt.Println(t.Unix())
}

Go言語の日時のフォーマット指定子の仕様は何度見ても謎だ‥

bash

#! /bin/bash

IFS=$'\n' read str

date -d "${str}" +"%s"
  • man date(ぉ