challenge 文字列型日時ののN秒後時間取得

日時を表す文字列と時間(秒)を受け取り
指定された日時からN秒となる日時を出力する関数 DateEx() を作成してください。

関数の仕様は次の通りです。
1. 入力となる日時の書式は任意である。
    → プログラムの都合に合わせてよい。
2. 入力となる時間(秒)は、負の値も許容すること。
    また、負の値が指定された場合、指定の日時よりも前の日時を出力すること
3. 出力する日時は入力の日時と同じ書式をとる文字列であること
4. 出力する日時は正規化されていること
5. 出力先は標準出力、または、バッファのいずれでもよい。

たとえば、DateEx("20080827235925",40)ならば
出力は
「20080828000005」です。

余力があれば時間を省略可能とし、
省略された場合は「現在時刻」を利用するようにしてみてください。
最近は普通にパーサがある時代になったのですね。
ごめんなさい盲点でした^^;;

ということで、「仕様.3」については、
入力の書式は限定しないこととし、出力の書式を限定するようにしてください。

Posted feedbacks - Nested

Flatten Hidden

このお題は日時のparseをさせたいのでしょうか?それとも差分の計算をさせたいのでしょうか?

1
2
3
4
5
6
7
def dateex(d, delta):
  return d + datetime.timedelta(seconds=delta)

if __name__ == '__main__':
  import time
  day = datetime.date(2008, 8, 31)
  print dateex(day, 100000)
ども、raynstardです。
うーん、どっちにになるのでしょう?(笑

このお題の目的は、文字列という日付とは関係ない入力データで
日付の計算をすることです。

そもそもの発端は、シェルで自分自身を
再スケジュールしようとしたらシェルじゃできねぇってことで
計算部分をperlの呼び出しで妥協してしまったことです。

最近の言語には、大抵パーサがあるのですね。
盲点でしたorz
date = "2008/08/27 23:59:25"
addSeconds = 40
> "2008/08/28 00:00:05"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;

class Program {
    static void Main(string[] args) {
        Console.WriteLine(
            DateEx(Console.ReadLine(), long.Parse(Console.ReadLine()))
            );
        
        Console.WriteLine(
            DateEx(long.Parse(Console.ReadLine()))
            );
        
        Console.ReadLine();
    }

    static string DateEx(string date, long addSeconds) {
        DateTime d;
        DateTime.TryParse(date, out d);
        if(d == null) throw new ArgumentException("日付が正しくありません。", "date");
        return d.AddSeconds((double)addSeconds).ToString();
    }

    static string DateEx(long addSeconds) {
        return DateEx(DateTime.Now.ToString(), addSeconds);
    }
}
1
2
3
4
<?php
function DateEx($time, $diff){
    echo echo date('YmdHis',strtotime($time)+$diff);
}

echoが2つになってしまった・・・。片方いらないです。

1
2
3
DateEx() {
  date -d "$1 $2 seconds"
}

仕様2を満たしていませんでした。

修正と、ついでに1引数に対応。

1
2
3
4
5
6
7
8
DateEx() {
  [ -z "$2" ] && set '' $1
  if [[ $2 == -* ]];then
    date -d "$1 ${2:1} seconds ago"
  else
    date -d "$1 $2 seconds"
  fi
}
てもとのdateコマンドでは動きませんでした。
バージョンていくつでしょう?

$ date --version
date (GNU coreutils) 5.96
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software.  You may redistribute copies of it under the terms of
the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
There is NO WARRANTY, to the extent permitted by law.

Written by David MacKenzie.
確認したdateのバージョンは、GNU coreutilsの6.10と5.97です。
なるほど、少なくともcoreutils版dateじゃないと使えないんですね。

私のところでの実行例は以下のとおりです。

$ DateEx 'Thu Aug 28 23:59:25 JST 2008' 40
Fri Aug 29 00:00:05 JST 2008
あれ?バージョン同じだぞ?とおもって再度やってみたら動きました。
秒の指定を間違えていたみたいです。
失礼しました。
入出力の日付文字列はISO8601形式です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:my="uri:ja.doukaku.org:my-functions"
  exclude-result-prefixes="my"
  >

  <xsl:output method="text" />

  <xsl:template match="/" >
    <xsl:value-of select="my:DateEx('2008-09-01T10:02:38', 10000)"/>
  </xsl:template>


  <xsl:function name="my:DateEx" as="xs:string">
    <xsl:param name="dstr" as="xs:string" />
    <xsl:param name="sec" as="xs:integer" />

    <xsl:sequence select="xs:string(
      xs:dateTime($dstr) +
      xs:dayTimeDuration(fn:concat('PT',xs:string($sec),'S'))
      )" />
  </xsl:function>

</xsl:stylesheet>
条件2を満たしてなかった。ので、修正。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:my="uri:ja.doukaku.org:my-functions"
  exclude-result-prefixes="my"
  >

  <xsl:output method="text" />

  <xsl:template match="/" >
    <xsl:value-of select="my:DateEx('2008-09-01T10:02:38', -1)"/>
  </xsl:template>

  <xsl:function name="my:DateEx" as="xs:string">
    <xsl:param name="dstr" as="xs:string" />
    <xsl:param name="sec" as="xs:integer" />

    <xsl:sequence select="xs:string(
      xs:dateTime($dstr) +
      xs:dayTimeDuration(
        fn:concat(
          (if($sec&lt;0)then'-'else''),
          'PT', xs:string(fn:abs($sec)), 'S'
          )
        )
      )" />
  </xsl:function>

</xsl:stylesheet>

フォーマットの指定がなかったので、とりあえずサンプルと同じにしてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class Sample204 {
    private static final DateFormat FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");

    public static String dateEx(String time, int seconds) throws ParseException {
        Date date = FORMAT.parse(time);
        return dateEx(date, seconds);
    }
    public static String dateEx(int seconds) {
        return dateEx(new Date(), seconds);
    }

    private static String dateEx(Date date, int seconds) {
        Calendar cal = Calendar.getInstance();
        cal.setTime( date );
        cal.add(Calendar.SECOND, seconds);
        return FORMAT.format(cal.getTime());
    }


    public static void main(String[] args) {
        try {
            System.out.println(dateEx("20080827235925",40));
            System.out.println(dateEx(0));
        } catch (ParseException ex) {
            ex.printStackTrace();
        }
    }
}
DateTimeと関連モジュールを使って。

入力はコマンドライン引数で、ISO8601形式の日付と秒数を指定します。
日付を省略すると実行時の時刻を使います。

# 入力はISO8601形式じゃなくてもparseしてくれます。ので、お題の条件3.が微妙な感じ
1
2
3
4
5
6
7
8
9
use strict;
use warnings;

use DateTime;
use DateTime::Format::DateParse;

my $d = @ARGV==2 ? DateTime::Format::DateParse->parse_datetime(shift)
                 : DateTime->now(time_zone => q{Asia/Tokyo});
print $d->add(seconds => shift||0)->iso8601,$/;

しまった、お題は「関数を作れ」だった。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use strict;
use warnings;

use DateTime;
use DateTime::Format::DateParse;

print DateEx(@ARGV),$/;

sub DateEx
{
  (@_>=2 ? DateTime::Format::DateParse->parse_datetime(shift)
         : DateTime->now(time_zone => q{Asia/Tokyo}))
  ->add(seconds => shift||0)->iso8601;
}
Perlにまでパーサっがあったのですね。。。
僕が妥協した時はPOSIXを使ったべた書きでした。
# ほとんどCとかわりません(笑
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env perl
use strict;
use POSIX qw/strftime mktime/;

sub DateEx($$)
{
        my $outDate = '';
        my @t = ($_[0] =~ m/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/);

        $t[0] -= 1900; $t[1] -= 1; @t = reverse @t;
        $t[0] += $_[1];
        mktime( @t );
        return strftime("%Y%m%d%H%M%S", @t);
}

print DateEx("20080827235925",40) . "\n";

__END__
#EOF
Boost.Date_Time で。
入出力の日付形式はコード中に書いてある通り(例:"2008-Sep-01 12:34:56")です。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <string>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>

std::string
DateEx(
    const std::string& dstr,
    int                sec
    )
{
  using namespace boost::posix_time;
  return boost::lexical_cast<std::string>(
      ptime(dstr.empty() ? second_clock::local_time()
                         : time_from_string(dstr)
      ) + seconds(sec)
    );
}

int main(int c, char** v)
{
  if ( c == 1 ) {
    std::cout << "usage: " << v[0] << " [<datetime{YYYY-Mon-DD hh:mm:ss}>] <seconds>\n";
    return 0;
  }

  std::cout << DateEx(
      c>=3?v[1]:"",
      boost::lexical_cast<int>(c>=3?v[2]:v[1])
      ) << "\n";

  return 0;
}

Squeak Smalltalk では日時オブジェクト同士の演算が可能です。

1
2
3
4
5
"文字列指定した日時の場合"
'2008-09-01T12:00:00+09:00' asDateAndTime - 10 seconds   "=> 2008-09-01T11:59:50+09:00 "

"現在日時の場合"
DateAndTime now + 15 seconds
D 2.0 + Phobos。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import std.conv;
import std.date;
import std.stdio;

string DateEx(string datetime, int tdelta_in_secs)
{
    d_time t = std.date.parse(datetime);
    d_time new_datetime = t + tdelta_in_secs * std.date.TicksPerSecond;
    return std.date.toString(new_datetime);
}

void main(string[] args)
{
    string datetime;
    int tdelta_in_secs;
    if (args.length == 3) {
        datetime = args[1];
        tdelta_in_secs = toInt(args[2]);
    }
    else {
        d_time now = std.date.getUTCtime;
        datetime = std.date.toString(now);
        tdelta_in_secs = toInt(args[1]);
    }
    writefln("%s", DateEx(datetime, tdelta_in_secs));
}

// eof

使用例も書かないとダメですね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import std.conv;
import std.date;
import std.stdio;

string DateEx(string datetime, int tdelta_in_secs)
{
    d_time t = std.date.parse(datetime);
    d_time new_datetime = t + tdelta_in_secs * std.date.TicksPerSecond;
    return std.date.toString(new_datetime);
}
unittest {
    assert(DateEx("Mon Sep 01 13:00:00 GMT+0900 2008", -3600) ==
        "Mon Sep 01 12:00:00 GMT+0900 2008");
    assert(DateEx("Mon Sep 01 13:00:00 GMT+0900 2008", 3600) ==
        "Mon Sep 01 14:00:00 GMT+0900 2008");
}

void main(string[] args)
{
    string datetime;
    int tdelta_in_secs;
    if (args.length == 3) {
        datetime = args[1];
        tdelta_in_secs = toInt(args[2]);
    }
    else {
        d_time now = std.date.getUTCtime;
        datetime = std.date.toString(now);
        tdelta_in_secs = toInt(args[1]);
    }
    writefln("%s", DateEx(datetime, tdelta_in_secs));
}

// eof
3.の条件を無視すればこれでもOKです。
(Time.parseはさまざまな書式を受け付けるため)

def date_ex(diff, date = Time.now.to_s)
  puts Time.parse(date) + diff
end

1
2
3
4
5
6
7
8
9
require 'time'

def date_ex(diff, date = Time.now.strftime("%Y/%m/%d %H:%M:%S"))
  raise unless date =~ /\d+\/\d+\/\d+\s+\d+:\d+:\d+/
  puts (Time.parse(date) + diff).strftime("%Y/%m/%d %H:%M:%S")
end

date_ex(100, '2008/09/01 14:06:25') #=> 2008/09/01 14:08:05
date_ex(100) #=> 「現在時刻」から100秒後

VBA for Excel (2003)

1
2
3
4
Function DateEx(str, sec)
  p = InStr(str, " ")
  DateEx = DateValue(Left(str, p - 1)) + TimeValue(Right(str, Len(str) - p)) + sec / 86400
End Function
関数に出力機能まで含めるとなんか使いにくそう。

   '2008 8 27 23 59 25' DateEx 40
2008 8 28 0 0 5

   '2008 8 27 23 59 25' DateEx _40
2008 8 27 23 58 45

   DateEx 100000000
2011 11 3 0 12 46.731
1
2
3
4
5
6
7
8
9
load 'dates'

now =: 6!:0

DateEx =: 3 : 0
(": now '') DateEx y
:
wd 1 tsrep (y * 1000) + tsrep ". x
)

日時の書式は ISO-8601 の「西暦-月-日T時:分:秒」形式です。時間を省略する場合には空文字列を渡すことにしてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(use srfi-13)
(use srfi-19)

(define (date-ex str delta)
  (let ((t (if (string-null? str)
               (current-time time-utc)
               (date->time-utc
                (string->date str "~Y-~m-~dT~H:~M:~S")))))
    (date->string
     (time-utc->date
      (add-duration t (make-time time-duration 0 delta)))
     "~5")))

(define (main args)
  (print (date-ex (cadr args) (string->number (caddr args)))))
net-telent-dateを使ってRFC 2822形式で出力するようにしてみました。
他にも様々な形式をパーズしてくれるライブラリです。
CLの引数の順番はお題の例とは逆にしています。
(date-ex 40 "Thu, 28 Aug 2007 23:59:25 +0900")
;=> "Thu, 30 Aug 2007 00:00:05 +0900" 

(date-ex -3600)
;=> "Mon, 01 Sep 2008 17:56:51 +0900" 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(require :net-telent-date)
(require :lw-compat)

(defpackage :doukaku-204 (:use :cl :date :lispworks))
(in-package :doukaku-204)

(defun date-ex (sec &optional time-string)
  (when-let (ut (if time-string
                    (parse-time time-string)
                    (get-universal-time)))
    (universal-time-to-rfc2822-date (+ ut sec))))
フォーマットが任意だと楽ちんでいいですね。

> DateEx("2008-09-02 00:00:00 JST", 30)
[1] "2008-09-02 00:00:30 JST"
> DateEx("2008-09-02 00:00:00 JST", -30)
[1] "2008-09-01 23:59:30 JST"
1
2
3
DateEx <- function(d1=Sys.time(), d2=0){
  as.POSIXct(d1) + d2
}

 Javaと余り変わりませんがscala版を。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import    java.text.DateFormat
import    java.text.ParseException
import    java.text.SimpleDateFormat
import    java.util.Calendar

object DateEx {
    val    formatter:DateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
    def DateEx(date:String,diff:Int):String = {
        val    calendar:Calendar = Calendar.getInstance
        calendar.setTime(formatter.parse(date))
        DateEx(calendar,diff)
    }
    def DateEx(diff:Int):String = DateEx(Calendar.getInstance,diff)
    def DateEx(calendar:Calendar,diff:Int):String = {
        calendar.add(Calendar.SECOND,diff)
        formatter.format(calendar.getTime)
    }
    def main(args:Array[String]):Unit = {
        try {
            args.length match {
                case 2 => println(DateEx(args(0),args(1).toInt))
                case 1 => println(DateEx(args(0).toInt))
                case _ => println(DateEx("20080827235925",40))
            }
        } catch {
            case ex:ParseException => println("invalid date format.")
            case ex => ex.printStackTrace
        }
    }
}
JavaScriptのパーサを使用。Firebugで確認。
出力形式にあわせるとこんな入力になった。
toLocaleStringメソッドを使えば"2008年9月2日 8:50:34"のように出力するが、パーサが解釈してくれない。
>>> DateEx('Tue Sep 02 2008 8:30:00',1234)
"Tue Sep 02 2008 08:50:34 GMT+0900"
1
function DateEx(t,s) new Date(Date.parse(t)+s*1000).toString();

こんなのもあたーよ。

なんの工夫もないコードです。
1
2
3
4
5
6
7
import Data.Time

addDate time sec = show $ addUTCTime sec $ read time

main = do
    putStrLn $ addDate "2008-09-02 10:20:30 UTC" 15
    putStrLn $ addDate "2008-09-02 10:20:30 UTC" (-5)

なでしこで。下の二つの関数はほかから流用です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
DateEx("2008/08/27 23:59:25",40)を表示
DateEx("2008/08/27",40)を表示

●DateEx(str,n)
    dateとは配列=区切(str," ")
    もし(date[1]="")ならば、date[1]=今
    配列結合(UNIX2date(date2UNIX(date,0)+n)," ")
●date2UNIX(date,tzd)
    戻(日数差("1970/1/1",date[0])*(60*60*24)+秒差("0:0:0",date[1])+tzd)
●UNIX2date(unix)
    tmpとは配列
    dayとは整数=unix/86400%86400
    unix=unix-day*86400
    tmp[0]=日付加算("1970/1/1","//{day}")
    tmp[1]="{ゼロ埋め(unix/3600%24,2)}:{ゼロ埋め(unix/60%60,2)}:{ゼロ埋め(unix%60,2)}"
    tmpで戻る

モジュールなしで書いた

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#! /usr/bin/perl
use strict;
use warnings;

my $date_string = 20080827235925;
my $add_seconds = 40;

print DateEx($date_string,$add_seconds);

sub DateEx{
    my ($date_string, $add_seconds) = @_;
    
    return 'Error!' unless $date_string =~ /^\d{14}$/;
    return 'Error!' unless $add_seconds =~ /^-?\d+$/;
    
    my ($year,$month,$day,$hour,$minutes,$seconds)
        = $date_string =~ /^(\d{1,4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/;
    my @result = add_seconds($year,$month,$day,$hour,$minutes,$seconds,$add_seconds);
    
    return sprintf("%04d%02d%02d%02d%02d%02d",@result);
}

sub add_seconds{
    my ($year,$month,$day,$hour,$minutes,$seconds,$add_seconds) = @_;
    
    #時間、秒を先に出す
    my ($tmp_day,$result_hour,$result_minutes,$result_seconds)
        = seconds_to_day($add_seconds + day_to_seconds($day-1,$hour,$minutes,$seconds));
    
    #日付を計算
    my ($result_year, $result_month, $result_day)
        = date_add($year,$month,$tmp_day);
    
    return ($result_year,$result_month,$result_day,$result_hour,$result_minutes,$result_seconds);
}

sub day_to_seconds{#何日、何時間、何分の形式を総秒数に変換
    my ($day,$hour,$minutes,$seconds) = @_;
    
    my $SECONDS_PER_MINUTES = 60;
    my $SECONDS_PER_HOUR = $SECONDS_PER_MINUTES * 60;
    my $SECONDS_PER_DAY = $SECONDS_PER_HOUR * 24;
    
    my $total_second = $day     * $SECONDS_PER_DAY
                     + $hour    * $SECONDS_PER_HOUR
                     + $minutes * $SECONDS_PER_MINUTES
                     + $seconds
                     ;
    return $total_second;
}

sub seconds_to_day{#秒数を 何日、何時間、何分の形式に変換
    my ($total_seconds) = @_;
    
    my $SECONDS_PER_MINUTES = 60;
    my $SECONDS_PER_HOUR = $SECONDS_PER_MINUTES * 60;
    my $SECONDS_PER_DAY = $SECONDS_PER_HOUR * 24;
    
    my ($day,$hour,$minutes,$seconds) = (0,0,0,0);
    
    if($total_seconds > 0){
        $day = int($total_seconds / $SECONDS_PER_DAY) + 1;
        $hour = int($total_seconds / $SECONDS_PER_HOUR) % 24;
        $minutes = int($total_seconds / $SECONDS_PER_MINUTES) % 60;
        $seconds = $total_seconds % 60;
    }
    elsif($total_seconds < 0){
        $day = (int(($total_seconds * (-1))/$SECONDS_PER_DAY) + 1) * (-1);
        $total_seconds += $day * $SECONDS_PER_DAY * (-1);
        $hour = int($total_seconds / $SECONDS_PER_HOUR) % 24;
        $minutes = int($total_seconds / $SECONDS_PER_MINUTES) % 60;
        $seconds = $total_seconds % 60;
    }
    return ($day,$hour,$minutes,$seconds);
}

sub date_add{#2008年8月33日は2008年9月2日のような考え方
    my ($year,$month,$day) = @_;
    
    #400年以上のスパンがある場合
    while(abs($day) > (my $DAYS_IN_400YEARS = 365*400+97)){
        if($day > 0){
            $day -= $DAYS_IN_400YEARS;
            $year += 400;
        }
        else{
            $day += $DAYS_IN_400YEARS;
            $year -= 400;
        }
    }
    #1年以上のスパンがある場合
    while(abs($day) > 366){
        if($day > 0){
            if($month <= 2){
                while($day > 366){
                    _uru_or_not($year) ? $day -= 366 : $day -= 365;
                    $year++;
                }
            }
            else{
                while($day > 366){
                    _uru_or_not($year+1) ? $day -= 366 : $day -= 365;
                    $year++;
                }
            }
        }
        else{
            if($month <= 2){
                while($day < -366){
                    _uru_or_not($year-1) ? $day += 366 : $day += 365;
                    $year--
                }
            }
            else{
                while($day < -366){
                    _uru_or_not($year) ? $day += 366 : $day += 365;
                    $year--
                }
            }
        }
    }
    #一月づつ、足すもしくはさかのぼる
    while(!(($day > 0) && (_get_days_of_month($year,$month) >= $day))){
        if($day > 0){
            $day -= _get_days_of_month($year,$month);
            $month++;
            if($month == 13){
                $year++;
                $month = 1;
            }
        }
        else{
            $month--;
            if($month == 0){
                $year--;
                $month = 12;
            }
            $day += (_get_days_of_month($year,$month) + 1);
        }
    }
    return ($year,$month,$day);
}

sub _uru_or_not{#閏年か否かの判定
    my ($year) = @_;
    return 1 if($year % 400 == 0);
    return 0 if($year % 100 == 0);
    return 1 if($year % 4   == 0);
    return 0;
}

sub _get_days_of_month{#年と月を与えるとその日数を返す
    my ($year,$month) = @_;
    
    my @days_of_months = qw{0 31 28 31 30 31 30 31 31 30 31 30 31};
    if(($month == 2) && (_uru_or_not($year))){
        return 29;
    }
    return $days_of_months[$month];
}

初めて投稿します。よろしくお願いします。new Date(String s)を使えばもっと短く書けると思うけど、deprecatedだから反則ですよね...

1
2
3
4
5
6
7
8
9
format = new java.text.SimpleDateFormat('yyyyMMddHHmmss')

def dateEx(delta, date = null) {
  d = (date) ? format.parse(date) : new Date()
  format.format(new Date(d.time + delta * 1000))
}

println dateEx(40, "20080827235925")
println dateEx(100)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#load "unix.cma";;

let scan_time s =
  Scanf.sscanf s "%4d%2d%2d%2d%2d%2d" (fun year mon day h m s ->
    { Unix.tm_sec = s;  tm_min = m;  tm_hour = h;
      tm_mday = day;  tm_mon = mon;  tm_year = year-1900;
      tm_wday = 0;  tm_yday = 0;  tm_isdst = false
    } ) ;;

let print_time { 
  tm_year = yy; tm_mon = mm; tm_mday = dd;
  tm_hour = h; tm_min = m; Unix.tm_sec = s } =
  Printf.printf "%d%02d%02d%02d%02d%02d" (yy+1900) mm dd h m s;;
  
let date_ex s sec =
  let {Unix.tm_sec = tsec} as tm = scan_time s in
  print_time (snd (Unix.mktime {tm with Unix.tm_sec=tsec+sec}));;

(* date_ex "20080827235925" 40;; *)
PostScript で。
すっかり泥沼化してしまったので、まだバグがあるかもしれません。
対応は日本時間のみで、1970-2050年に対応。
ただし、2009年7月1日以降、1/1, 7/1 の午前9時を含む計算で各1秒のずれが
生じる可能性があります(仕様)。

(20080927000010) -40 DateEx ==> (20080926235930)
(20080101085900) 60 DateEx  ==> (20080101090000)
(20080101085900) 120 DateEx ==> (20080101090100)
(20090101085900) 60 DateEx  ==> (20090101085960) (うるう秒)
(20090101085900) 120 DateEx ==> (20090101090059) (うるう秒)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
%!PS

/JST 9 60 60 mul mul def
/LDAY 24 60 60 mul mul def
/MOFF [
    0 31 dup 28 add dup 31 add dup 30 add dup 31 add dup 30 add dup 31 add
    dup 31 add dup 30 add dup 31 add dup 30 add dup 31 add
] def

/StrToUTC0 { % (Date String)  StrToUTC  MJD  UTC_L
    % Get UTC / Ignore TZ & leap secs.
    dup 8 2 getinterval cvi 3600 mul exch
    dup 10 2 getinterval cvi 60 mul exch
    dup 12 2 getinterval cvi exch
    4 1 roll add add
    exch

    dup 0 4 getinterval cvi exch
    dup 4 2 getinterval cvi
    dup 2 le {
        3 -1 roll 1 sub 3 1 roll
    12 add
    } if
    exch
    6 2 getinterval cvi
    exch 2 sub 30.59 mul cvi
    add exch
    dup 365.25 mul cvi exch
    dup 400 div cvi exch
    100 div cvi sub add add
    678912 sub
    exch
} bind def

/LeapSecs0 [
   [  (19720630235960) StrToUTC0 ]
   [  (19721231235960) StrToUTC0 ]
   [  (19731231235960) StrToUTC0 ]
   [  (19741231235960) StrToUTC0 ]
   [  (19751231235960) StrToUTC0 ]
   [  (19761231235960) StrToUTC0 ]
   [  (19771231235960) StrToUTC0 ]
   [  (19781231235960) StrToUTC0 ]
   [  (19791231235960) StrToUTC0 ]
   [  (19810630235960) StrToUTC0 ]
   [  (19820630235960) StrToUTC0 ]
   [  (19830630235960) StrToUTC0 ]
   [  (19850630235960) StrToUTC0 ]
   [  (19871231235960) StrToUTC0 ]
   [  (19891231235960) StrToUTC0 ]
   [  (19901231235960) StrToUTC0 ]
   [  (19920630235960) StrToUTC0 ]
   [  (19930630235960) StrToUTC0 ]
   [  (19940630235960) StrToUTC0 ]
   [  (19951231235960) StrToUTC0 ]
   [  (19970630235960) StrToUTC0 ]
   [  (19981231235960) StrToUTC0 ]
   [  (20051231235960) StrToUTC0 ]
   [  (20081231235960) StrToUTC0 ]
] def

/LeapSecs 30 dict def
LeapSecs0 {
    LeapSecs exch aload pop put
} forall

/IsLeapDay { % MJD  IsLeapDay  bool
    LeapSecs exch known
} bind def

/Normalize { % MJD secs  Normalize   MJD' secs'
    {
        dup 0 lt {
            LDAY add
            exch 1 sub exch
            LeapSecs 2 index known { 1 add } if
        } {
            dup LDAY eq {
                LeapSecs 2 index known { exit } if
            } if
            dup LDAY lt {
                exit
            } if
            LeapSecs 2 index known {
                1 sub
            } if
            LDAY sub
            exch 1 add exch
        } ifelse
    } loop
} bind def

/StrToUTC { % (Date String)  StrToUTC  MJD  secs
    dup
    StrToUTC0
    JST sub
% (Date) MJD secs'

   dup 0 lt {
       exch 1 sub exch LDAY add
   } if

    dup 0 eq {
       1 index 1 sub IsLeapDay
       {
           2 index 8 6 getinterval (085960) eq
           {
               exch 1 sub exch LDAY add
           } if
       } if
   } if
   Normalize
   3 -1 roll pop
} bind def

/cvs2 { % int  cvs2  (dd)
    2 string dup dup 3 index
    % int () () 
    10 idiv 1 string cvs 0 exch putinterval
    3 -1 roll 10 mod 1 string cvs 1 exch putinterval
} bind def


/CompTime {
    % MJD sec () MJD' sec'  CompTime  MJD sec ()   -1/0/1
    % LocalFunction
    3 index sub exch
    4 index sub
    % MJD sec () sec'' MJD''
    dup 0 lt {
        pop pop -1
    } {
        0 gt {
            pop 1
        }{
            dup 0 lt {
                pop -1
            } {
                0 gt {
                    1
                } {
                    0
                } ifelse
            } ifelse
        } ifelse
    } ifelse
} bind def


/UTCToStr { %  MJD secs  UTCToStr  (Date String)
    14 string dup 0 (20080101000000) putinterval
    2050 -1 1970 {
        4 string cvs 1 index 0 3 -1 roll putinterval
        dup StrToUTC CompTime
        0 le { exit } if
    } for
    12 -1 1 {
        cvs2 1 index 4 3 -1 roll putinterval
        dup StrToUTC CompTime
        0 le { exit } if
    } for
    31 -1 1 {
        cvs2 1 index 6 3 -1 roll putinterval
        dup StrToUTC CompTime
        0 le { exit } if
    } for
    23 -1 0 {
    cvs2 1 index 8 3 -1 roll putinterval
    dup StrToUTC CompTime
    0 le { exit } if
    } for
    % MJD secs (Date)
    dup StrToUTC exch pop
    % MJD secs (Date) secs'
    4 2 roll exch pop sub neg
    % (Date) secs
    dup 3600 eq { % leap second
    pop dup 10 (5960) putinterval
    } {
           dup 60 idiv
    % (Date) secs min
    cvs2 2 index 10 3 -1 roll putinterval
    % (Date) secs
    60 mod cvs2 1 index 12 3 -1 roll putinterval
    } ifelse
} bind def

/DateEx { % (Date String)  secs   DateEx   (New Date String)
    exch StrToUTC
    3 -1 roll add
    Normalize
    UTCToStr
} bind def

% =========================================================

% Test Code

(20080927000010) -40 DateEx ==
(20080101085900) 60 DateEx  ==
(20080101085900) 120 DateEx ==
(20090101085900) 60 DateEx  ==
(20090101085900) 120 DateEx ==

これはUnixタイムへの統一を行うと簡単にデータを加工(秒単位で追加)できます。 問題は一番面倒な文字列からUnixタイムの作成、Unixタイムから文字列の作成です。 時間の構造体やtime_tを使ってUnixタイムを実現しました。 面倒で雑ですが、一応汎用的な使い方ができるようにしています。

これはT216という独自ソフトウェアの作成の一貫として作ったコードです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <time.h>
#include <stdio.h>

/* どう書く.org #7401
   http://ja.doukaku.org/204/ */
time_t str_time(char *t)
{
    struct tm tm;
    time_t tt;
    char s[5];
    int i;

    strncpy(s, t, 4);
    i = atoi(s);
    tm.tm_year = i - 1900;
    s[2] = 0;
    strncpy(s, t + 4, 2);
    i = atoi(s);
    tm.tm_mon = i - 1;
    strncpy(s, t + 6, 2);
    i = atoi(s);
    tm.tm_mday = i - 1;
    strncpy(s, t + 8, 2);
    i = atoi(s);
    tm.tm_hour = i;
    strncpy(s, t + 10, 2);
    i = atoi(s);
    tm.tm_min = i;
    strncpy(s, t + 12, 2);
    i = atoi(s);
    tm.tm_sec = i;
    tt = mktime(&tm);
    return tt;
}

void time_str(time_t tt, char *s)
{
    struct tm *tp;
    char sc[5];

    tp = localtime(&tt);
    sprintf(sc, "%d", tp->tm_year + 1900);
    strcpy(s, sc);
    sprintf(sc, "%02d", tp->tm_mon + 1);
    strcat(s, sc);
    sprintf(sc, "%02d", tp->tm_mday + 1);
    strcat(s, sc);
    sprintf(sc, "%02d", tp->tm_hour);
    strcat(s, sc);
    sprintf(sc, "%02d", tp->tm_min);
    strcat(s, sc);
    sprintf(sc, "%02d", tp->tm_sec);
    strcat(s, sc);
    return;
}

void org_7401(char *t, int n)
{
    time_t tm;

    tm = str_time(t);
    tm += (time_t) n;
    time_str(tm, t);
    return;
} 

int main(void)
{
    char *s = malloc(15);

    strcpy(s, "20080827235925");
    org_7401(s, 40);
    printf("%s\n", s);
    free(s);
    return 0;
}
適当だけど、こんな感じで良くない?
それに、
tm.tm_isdst = 0;
を忘れていたし、
tm.tm_mday = i - 1;
は-1する必要がないので、答え間違っていたよ(汗

(書式にセパレートとかがあるとstrptimeとかも使えることがあるよ。)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <time.h>
#include <stdio.h>

/* どう書く.org #7401
   http://ja.doukaku.org/204/ */
time_t str_time(char *t)
{
    struct tm tm;

/*    strptime(t,"%Y%m%d%H%M%S",&tm);*/
    sscanf(t, "%04d%02d%02d%02d%02d%02d", 
        &tm.tm_year, 
        &tm.tm_mon, 
        &tm.tm_mday, 
        &tm.tm_hour, 
        &tm.tm_min, 
        &tm.tm_sec);
    tm.tm_year -= 1900;
    tm.tm_mon--;
    tm.tm_isdst = 0;
    return mktime(&tm);
}

void time_str(time_t tt, char *s)
{
    struct tm *tp;

    tp = localtime(&tt);
    strftime(s, 15, "%Y%m%d%H%M%S", tp);
    return;
}

void org_7401(char *t, int n)
{
    time_t tm;

    tm = str_time(t);
    tm += (time_t) n;
    time_str(tm, t);
    return;
} 

int main(void)
{
    char *s = malloc(15);

    strcpy(s, "20080827235925");
    org_7401(s, 40);
    puts(s);
    return 0;
}
ちゃんとこの問題の仕様にのっとるなら。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <time.h>
#include <stdio.h>

/* どう書く.org #7401
   http://ja.doukaku.org/204/ */
time_t str_time(char *t)
{
    struct tm tm;

//    strptime(t,"%Y%m%d%H%M%S",&tm);
    sscanf(t, "%04d%02d%02d%02d%02d%02d", 
        &tm.tm_year, 
        &tm.tm_mon, 
        &tm.tm_mday, 
        &tm.tm_hour, 
        &tm.tm_min, 
        &tm.tm_sec);
    tm.tm_year -= 1900;
    tm.tm_mon--;
    tm.tm_isdst = 0;
    return mktime(&tm);
}

void time_str(time_t tt, char *s)
{
    struct tm *tp;

    tp = localtime(&tt);
    strftime(s, 15, "%Y%m%d%H%M%S", tp);
    return;
}

void DateEx(char *t, int n)
{
    char s[15];
    time_t tm;

    tm = str_time(t);
    tm += (time_t) n;
    time_str(tm, s);
    puts(s);
    return;
} 

int main(void)
{
    DateEx("20080827235925", 40);
    return 0;
}

java.util.Calendar#addを使ってみました。

これといった捻りは特になしです。

horiuchiさん(#7513)がJavaで投稿したもののGroovy版といった位置づけですかね。

1
2
3
4
5
6
7
def dateEx(date = null, delta) {
   def format = new java.text.SimpleDateFormat('yyyyMMddHHmmss')
   def cal = Calendar.instance
   if (date) { cal.time = format.parse(date) }
   cal.add(Calendar.SECOND, delta)
   return format.format(cal.time)
}
1
2
3
4
fun addDate (time, sec) = Date.toString (Date.fromTimeLocal (Time.+ (Time.fromSeconds sec, Date.toTime (valOf (Date.fromString time)))));

print (addDate ("Tue Sep 02 10:20:30 2008", 15) ^ "\n");
print (addDate ("Tue Sep 02 10:20:30 2008", ~5) ^ "\n");

SQL Server 2008 で確認しました。

1
2
3
4
5
6
7
8
WITH
  Input(d, n) AS (
    SELECT '2008-8-27 23:59:25', 40
  )
, DateEx(d) AS (
    SELECT DATEADD(S, COALESCE(n, 0), d) FROM Input 
  )
SELECT d FROM DateEx

Index

Feed

Other

Link

Pathtraq

loading...