2012/05/31

PowerShellでSQLServerのデータベースのバックアップ/リストアを行うスクリプト

SqlCmdコマンドを使用してSQL Serverのデータベースをバックアップ/リストアするスクリプト。

GitHub: Backup-Database.ps1

#Backup-Database.ps1
#simple backup & restore script for SQLServer2008

Param(
  [switch] $backup,
  [switch] $restore,
  [string] $database="",
  [string] $server=$env:computerName,
  [string] $file="",
  [string] $directory="",
  [switch] $help,
  [switch] $debug
)

$usage=@'

名前
    Backup-Database

概要
    SQL Serverデータベースの完全バックアップを作成/復元します。

構文
    Backup-Database {-Backup | -Restore} -Database  [-Server ] [-File  | 
     -Directory ]

説明
    SqlCmdコマンドを使用し、SQL Serverデータベースの完全バックアップを作成/復元します。
    SQL ServerにはWindows認証にて接続します。スクリプト実行アカウントにバックアップ/リストアを実施する
    権限がない場合、エラーとなります。
    
    File オプションを指定した場合、Directory オプションは無視されます。
    保存先とファイル名の両方を指定する場合 ( たとえば、D:\backup に backup.db という名前で作成したい場合 ) は
    File オプションにパスを指定してください。
    
    Backup時に File オプションが未指定の場合は DatabaseName よりバックアップファイル名を作成します。
    (.\_yyyyMMddHHmmss.bak)
    Restor時に File オプションが未指定の場合は例外が発生します。

関連するリンク
    http://msdn.microsoft.com/ja-jp/library/ms186865.aspx
    http://msdn.microsoft.com/ja-jp/library/ms186858.aspx
    http://msdn.microsoft.com/ja-jp/library/ms179313.aspx

'@

$query_backup="BACKUP DATABASE {database_name} TO DISK = '{backup_file}'"
$query_restore="RESTORE DATABASE {database_name} FROM DISK = '{backup_file}' WITH REPLACE"

# Sqlcmdを実行
function Exec-SqlCommand($query){
  Sqlcmd -S $server -Q "$query"
}

# Backupを実施
function Backup-Database(){
  # database未指定→Error
  if( $database.length -eq 0 ){
    throw "対象となる Database を指定してください。"
  }
  # file未指定→database名 + 日付とする
  if( $file.length -eq 0 ){
    #カレントディレクトリを取得
    $current_path = Convert-Path .
    
    if( $directory.length -gt 0 ){
      #ディレクトリ指定時
      if( Test-Path $directory ){
        $current_path = Convert-Path $directory
      }else{
        throw "指定されたディレクトリが存在しません。 [ $directory ]"
      }
    }
    
    #yyyyMMddhhmmss
    $date_time = Get-Date -format 'yyyyMMddHHmmss'
    #結合
    $file = $current_path + '\' + $database + '_' + $date_time + '.bak';
  }

  $now = Get-Date -format 'yyyy/MM/dd HH:mm:ss'
  $msg=@"

Backupを実施します。[ $now ]
`tDatabase:`t$database
`tBackupFile:`t$file

"@
  Write-Host $msg
  
  $q = $query_backup -replace '{database_name}', $database
  $q = $q -replace '{backup_file}', $file
  
  Write-Debug "query= $q"
  
  Exec-SqlCommand $q
  
  $now = Get-Date -format 'yyyy/MM/dd HH:mm:ss'
  
  Write-Host "`nBackupが完了しました。 [ $now ]`n"
}

# Restoreを実施
function Restore-Database(){
  # database未指定→Error
  if( $database.length -eq 0 ){
    throw "対象となる Database を指定してください。"
  }
  # file未指定→Error
  if( $file.length -eq 0 ){
    throw "リストアするファイルを指定してください。"
  }
  # 存在しないfileを指定
  if( Test-Path $file ){
    $file = Convert-Path $file
  }else{
    throw "指定されたファイルが存在しません。 [ $file ]"
  }

  $now = Get-Date -format 'yyyy/MM/dd HH:mm:ss'
  $msg=@"

Restoreを実施します。[ $now ]
`tDatabase:`t$database
`tBackupFile:`t$file

"@
  Write-Host $msg
  
  $q = $query_restore -replace '{database_name}', $database
  $q = $q -replace '{backup_file}', $file
  
  Write-Debug "query= $q"
  
  Exec-SqlCommand $q
  
  $now = Get-Date -format 'yyyy/MM/dd HH:mm:ss'
  
  Write-Host "`nRestoreが完了しました。 [ $now ]`n"
}

# 引数をチェックし、処理を分岐
function Get-CommandLineOptions(){
  if($debug){
    #デバッグ表示を有効にする
    $DebugPreference="continue"
  }

  if( $help ){
    # Helpの表示
    Write-Host $usage
  }elseif( $backup -and $restore ){
    # Backup と Resutore の両方を指定 -> Error
    throw "Backup|Restoreのどちらかを指定してください。"
  }elseif( $backup ){
    Backup-Database
  }elseif( $restore ){
    Restore-Database
  }else{
    # Helpの表示
    Write-Host $usage
  }
}

Get-CommandLineOptions

スイッチオプションの指定

Param に [switch]型の引数を定義すると、スクリプト実行時に該当オプションを引数に渡すと、対応する変数に $true がセットされる。

Param(
  [switch] $backup,
  [switch] $restore,
  [string] $database="",
  [string] $server=$env:computerName,
  [string] $file="",
  [string] $directory="",
  [switch] $help,
  [switch] $debug
)

上のように書いた場合...

PS> .\Backup-Database.ps1 -Backup

↑このように引数を指定すると、$backup に $true がセットされる。引数を指定しなければ $false が入る。

functionでも同じように[switch]を使用可能。

function hoge([switch]$a){
  if( $a ){
    Write-Host "foo"
  }else{
    Write-Host "bar"
  }
}

hoge -a   # >foo
hoge      # >bar

2012/05/30

DbUtilsのサンプル

Apache CommonsのDbUtilsを使用してデータの取得、更新を行うサンプル。

//JdbcTest.java
package pkg.sample;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Iterator;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.log4j.*;
import org.apache.log4j.xml.*;
  
public class JdbcTest {
  static Logger logger = Logger.getLogger(JdbcTest.class);
  
  Connection con = null;
  QueryRunner qr = new QueryRunner();
  
  public void exec(){
    try{
      Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
      try{
        //DB接続
        con = DriverManager.getConnection("jdbc:sqlserver://localhost;database=ASN;integratedSecurity=true;");
        //オートコミットを無効に
        con.setAutoCommit(false);
        logger.info("connect.");
        
        //BeanListHandlerサンプル
        System.out.println("---登録データ---");
        select();
        
        //INSERTサンプル
        insert();
        
        //UPDATEサンプル
        update();
        
        //更新結果確認
        System.out.println("---更新結果---");
        select();
        
        //commit
        DbUtils.commitAndCloseQuietly(con);
        logger.info("commit.");
        
      }catch(SQLException se){
        DbUtils.rollbackAndCloseQuietly(con);
        logger.info("rollback.");
        se.printStackTrace();
        logger.error(se.getMessage());
      }

    }catch(ClassNotFoundException e){
      e.printStackTrace();
      logger.error(e.getMessage());
    }finally{
      //Close処理
      DbUtils.closeQuietly(con);
    }
  }
  
  /**
   * BeanListHandlerのサンプルコード
   * @throws SQLException
   */
  private void select() throws SQLException{
    String query = "SELECT * FROM SampleTable";
    
    ResultSetHandler rsh = new BeanListHandler(PersonInfo.class);
    List list = (List) qr.query(con, query, rsh);
    
    Iterator items = list.iterator();
    while(items.hasNext()){
      PersonInfo p = (PersonInfo) items.next();
      
      System.out.println(p.getName());
      System.out.println(p.getBirth());
      System.out.println(p.getRegist());
    }
  }
  
  /**
   * QueryRunner.updateのテスト (1)
   * @throws SQLException
   */
  private void insert() throws SQLException{
    //三つ目のパラメータを SYSDATETIME() に置き換えると例外が出る。 "Wrong number of parameters" ???
    String query = "INSERT INTO SampleTable (name, birth, regist) VALUES (?, ?, ?)";
    
    qr.update(con, query, new Object[]{ "サトウ", "1980-01-01", getCurrentDatetime() });
    qr.update(con, query, new Object[]{ "タナカ", null, getCurrentDatetime() });
  }
  
  /**
   * SYSDATETIME()の代わり。 現在日時(ミリ秒単位)の文字列表現を取得
   * @return yyyy-MM-dd HH:mm:ss.SSS
   */
  private String getCurrentDatetime(){
    java.text.DateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    return df.format(new java.util.Date());
  }
  
  /**
   * QueryRunner.updateのテスト (2)
   * @throws SQLException
   */
  private void update() throws SQLException {
    String query = "UPDATE SampleTable SET birth = ?, regist = SYSDATETIME() WHERE name = ?";
    
    qr.update(con, query, new Object[]{ "2000-12-31", "タナカ" });
  }
  
  /**
   * @param args
   */
  public static void main(String[] args) {
    //log4j設定ファイル読み込み
    DOMConfigurator.configure("logging.xml");

    JdbcTest j = new JdbcTest();
    j.exec();
  }
}

データベースから取得した値を格納するクラス。
BeanListHandlerを使用してQueryRunner.queryメソッドを実行すると結果がセットされて返される。

当然、Beanクラスのプロパティ名を取得対象テーブルのフィールド名と合わせる事。
取得対象テーブルのフィールド名とプロパティ名が一致しない場合は無視されるだけなので、Beanクラス側に追加のプロパティがあってもよい。

// Beanクラス(PersonInfo.java)
package pkg.sample;

public class PersonInfo {
  private String name;
  private String birth;
  private String regist;
  /**
   * @return the birth
   */
  public String getBirth() {
    return birth;
  }
  /**
   * @param birth the birth to set
   */
  public void setBirth(String birth) {
    this.birth = birth;
  }
  /**
   * @return the name
   */
  public String getName() {
    return name;
  }
  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }
  /**
   * @return the regist
   */
  public String getRegist() {
    return regist;
  }
  /**
   * @param regist the regist to set
   */
  public void setRegist(String regist) {
    this.regist = regist;
  }
}

2012/05/07

PowerShellでSqlDataReaderを使用するサンプル

PowerShellでSqlDataReaderクラスを使用して データベースから値を取得するサンプル。
#
# 名前
#     Execute-Query
# 概要
#     指定されたQueryを実行し、テキストファイルに出力します。
#
# -Query  SQL文
# -Fields 出力するColumn名
# -File   出力するテキストファイル
function Execute-Query([string]$query, $fields, $file){

 [Data.SqlClient.SqlConnection]$conn = $null
 [Data.SqlClient.SqlCommand]$cmd = $null
 [Data.SqlClient.SqlDataReader]$reader = $null
 
 $funcName = "Execute-Query"
 
 # 改行をスペースに置換
 $tmp = $query -replace "`r`n", " "
 Write-Debug  "QUERY=[$tmp]`t$funcName"
 
 try{
  $conn = New-Object Data.SqlClient.SqlConnection($DB_CON_STR)
  $conn.Open()
  Write-Debug  "Connection Open.`t$funcName"
  
  $cmd = New-Object Data.SqlClient.SqlCommand
  $cmd.CommandText = $query
  $cmd.Connection = $conn
  
  #ExecuteReader
  $reader = $cmd.ExecuteReader()
  $count = 0
  
  #出力バッファ
  [Text.StringBuilder]$sb = New-Object Text.StringBuilder
  while( $reader.Read() ){
   foreach( $column in $fields ){
    $sb.Append($reader[$column].ToString())
   }
   $sb.AppendLine()
   $count++
  }
  $reader.Close()
  Write-Debug "出力行数= $count`t$funcName"
  
  #ファイルに出力
  Add-Content $file ($sb.ToString())
  
 }catch [Exception]{
  throw $Error[0]
 }finally{
  if(!([String]::IsNullOrEmpty($conn))){
   $conn.Close()
   Write-Debug  "Connection Close.`t$funcName"
  }
 }
}

2012/03/23

ソフトバンクで店頭に行かずに機種変更した





DELL Streak Pro 101DLをソフトバンクオンラインで購入して、Web上で機種変更手続きをやってみた。

全部オンラインで完結するので、非常に便利でした。






参考までに、購入~手続きの大まかな流れをまとめておきます。
店頭に行くと、訳の解らん契約を勧められたりする場合もあるので、
これからもオンラインでやってこうと思います。



  1. ソフトバンクオンラインで端末購入
    いろんな規約の確認がありますが、店頭で要領の得ない話を聞くより断然マシです。
    フツーのオンラインショップのように購入手続きできました。
  2. すぐに「発送準備完了のお知らせ」とかいうメールが来ます。
    そこに機種変更手続きに関する説明も記載されています。
  3. 2日くらいすると、「出荷完了のお知らせ」というメールが来ます。
    このメールの後半に、端末切り替えに必要な「お申し込み受付番号」が記載されているので破棄しないように注意。
  4. 端末が届いたら、同梱されているUSIMカードを新しい端末に挿します。
    (カードを挿すとき、端末の電源はOFFにしとけとマニュアルにありました。)
  5. 「出荷完了のお知らせ」メールにWebで端末切り替えるURLが記載されているので、おもむろにクリックします。
    先ほどの受付番号と生年月日、メールアドレスを入力します。

    処理の完了に30分くらいかかるようです。
    先に端末の電源をONにし、Wi-Fi環境でGoogleアカウントのセットアップをしておきます。
  6. しばらくすると「手続き完了のお知らせ」が届きます。

    が、一向に新しい端末のアンテナが立たない。
    どうやら切り替えの反映には、端末の再起動が必要な模様です。
    (30分くらい悩んでしまった・・・)

    端末を再起動すると無事3G通信出来るようになり、電話番号も認識してました。
    旧端末も再起動すると、3G通信が無効になりました。

    起動しっぱなしだと、アンテナは立っているように見えましたが、おそらく使用できない状態なんでしょう。
    確認してませんが。

作業自体は非常に簡単でした。
店頭に行くと待ち時間の方が長いなんてこともザラなので、
オンラインで全て完結するのは嬉しいです。


2012/03/05

[Android] 連絡先の取得


getContentResolver()を使用する。


android.database.Cursor c = getContentResolver().query(
 Data.CONTENT_URI,
 new String[]{...}, //取得するColumnを指定。 nullだと全Column
 ".....", //where句に該当、条件を指定しない場合はnull
 new String[]{...}, //プレースホルダに設定する値。上記where句の?に順にセットされる
 "....." //ORDER BY句。nullだと順序を指定しない。
);


getContentResolverメソッドはandroid.content.ContextWrapperクラスに定義されていて、 android.app.ActivityはContextWrapperを継承している。


取得例 [http://developer.android.com/intl/ja/reference/android/provider/ContactsContract.Data.html]
Cursor c = getContentResolver().query(Data.CONTENT_URI,
 new String[] {Data._ID, Phone.NUMBER, Phone.TYPE, Phone.LABEL},
 Data.CONTACT_ID + "=?" + " AND "
  + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'",
 new String[] {String.valueOf(contactId)},
 null);



アプリケーションでContactへの参照を保持する場合は、android.provider.ContactsConstract.Contacts.LOOKUP_KEYを使用する。

//String lookupKey <- 保持しているLOOKUP_KEY
//lookupKeyからURIを組み上げる
android.net.Uri lookupUri = Uri.withAppendedPath(Contents.CONTENT_LOOKUP_URI, lookupKey);

//URIからContactを取得
android.database.Cursor c = getContentResolver().query(
 lookupUri, new String[]{ Contacts.DISPLAY_NAME }, ....
);

try{
 c.moveToFirst();
 String displayName = c.getString(0);
}finally{
 c.close();
}



パフォーマンスに配慮するなら、LOOKUP_KEYのほかにCONTACT_IDを保持し、 android.provider.ContactsConstract.Contacts.getLookupUri()を使用して URIを取得する。


getLookupUri()では、最初に CONTACT_IDを使用してContactを探し、ヒットしなければLOOKUP_KEYを使用する。


Uri lookupUri = getLookupUri(contactId, lookupKey);


Contactの集約により、CONTACT_IDは変化する可能性があるため、データの参照には常にURIを使用する必要がある。




2012/02/20

JavaScript (WSH) でExcelファイル読み込み

Excelのテーブル定義書を元に、Java Beansの元となるテキストを作成するJavaScriptを書きました。
14行目からのCELL objectの定義を少し修正すれば、汎用的に使えると思います。

行が結合されてたりすると、途端に破綻してしまいますが...。

/*
 * Excelファイル読み込み
 * テーブル定義書から必要な情報を取得し、classの元データを生成する
 */

/** Excelファイル */
var XLS_FILES = [
 "3.1. テーブル定義\\テーブル定義書_1.xls",
    :
 "3.1. テーブル定義\\テーブル定義書_N.xls"
];

/** Cell位置定義 */
var CELL = {};
/** テーブル名 (物理名) */
CELL.TABLE_NAME_ROW = 8;
CELL.TABLE_NAME_COL = 1;
/** テーブル名 (正式名) */
CELL.TABLE_DESC_ROW = 8;
CELL.TABLE_DESC_COL = 12;

/** フィールド情報 開始位置定義 */
CELL.START_ROW = 10;
CELL.FIELD_NAME_COL = 4;  //フィールド名
CELL.FIELD_TYPE_COL = 12; //種類
CELL.FIELD_DESC_COL = 18; //概要

/** テーブル定義 シート名 */
var SHEET_NAME = 'フィールド';

var fso = new ActiveXObject('Scripting.FileSystemObject');


/**
 * Excelファイルを開く
 */
function readExcel(xlsFile){
 //ファイル存在チェック
 if( ! fso.FileExists(xlsFile) ){
  return;
 }
 
 var xls = null;
 var book = null;
 var fields = [];
 
 try{
  //Excel起動
  xls = new ActiveXObject('Excel.Application');
  //Excelファイル open
  book = xls.Workbooks.Open(xlsFile);
  //シート指定
  var sheet = book.WorkSheets(SHEET_NAME);

  // sheet.Cells(row, col);
  var tableName = sheet.Cells(CELL.TABLE_NAME_ROW, CELL.TABLE_NAME_COL).Value;
  var tableDesc = sheet.Cells(CELL.TABLE_DESC_ROW, CELL.TABLE_DESC_COL).Value;
  /*
   table = {
   name:
   description: 
   }
   */
  fields = [{ 'name':tableName, 'description':tableDesc }]; //1要素目はtable名
  /*
  field = {
   name:
   type:
   length:
   description:
  }
  */

  var reg = /\([0-9]+\)/;
  var row = CELL.START_ROW;
  while(1){
   var c = sheet.Cells(row, CELL.FIELD_NAME_COL).Value;
   if( c == null || c.length == 0){
    //フィールド名定義がない→ループ終了
    break;
   }
   //フィールド名
   var name = c;
   //種別
   var type = sheet.Cells(row, CELL.FIELD_TYPE_COL).Value;
   //長さ
   var length = 0;
   if( reg.test(type) ){
    var m = type.match(reg).join('');
    m = m.replace(/\(|\)/, '');
    length = parseInt(m);
   }
   //概要
   var desc = sheet.Cells(row, CELL.FIELD_DESC_COL).Value;
   if( desc == null || desc.length == 0 ){
    desc = '';
   }
   fields[fields.length] = {
     'name':name,
     'type':type,
     'length':length,
     'description': desc
    };

   row++;
  }
  
 }catch(e){
  //例外発生 -> 無視
 }finally{
  try{
   //Excelファイル close
   book.Close();
  }catch(e){}
  try{
   //Excel終了
   xls.Quit(); //きちんとQuitしないとプロセスが残る
   xls = null;
  }catch(e){}
 }
 return fields;
}

/**
 * アンダーバーを除去し、lowerCamelCaseに変換する
 */
function toLowerCamelCase(str){
 var tmp = str.toLowerCase();
 var arr = tmp.split('_');
 var ret = arr[0];
 for(var i=1; i<arr.length; i++){
  ret += arr[i].charAt(0).toUpperCase() + arr[i].slice(1);
 }
 return ret;
}

/**
 * アンダーバーを除去し、UpperCamelCaseに変換する
 */
function toUpperCamelCase(str){
 var tmp = str.toLowerCase();
 var arr = tmp.split('_');
 var ret = '';
 for(var i=0; i<arr.length; i++){
  ret += arr[i].charAt(0).toUpperCase() + arr[i].slice(1);
 }
 return ret;
}

/** JavaDocコメント文字列 出力 */
function javadocString(msg){
 return ['/**',msg,'*/'].join(' ');
}

/** DBMSタイプ→Java型に変換 */
function getType(t){
 var ret = 'String';
 
 if( /VARCHAR/i.test(t) ){
  ret = 'String';
 }else if( /NUMBER/i.test(t) ){
  ret = 'int';
 }else if( /DATE/i.test(t) ){
  ret = 'String';
 }
 
 return ret;
}


(function(){
 for( var i=0; i<XLS_FILES.length; i++ ){
  //Excelファイル読み込み
  var fields = readExcel(XLS_FILES[i]);
  //1つめの要素はテーブル定義
  var tableName = fields[0].name;
  var tableDesc = fields[0].description;
  //結果出力ファイル
  var fileName = tableName + '_' + tableDesc + '.txt';
  WScript.Echo(fileName);
  
  //出力ファイルデータ
  var dat = [];
  
  dat[dat.length] = javadocString(tableDesc);
  dat[dat.length] = 'final public static String TABLE_NAME = "' + tableName + '";';
  dat[dat.length] = '';
  
  //フィールド順の定義
  for( var j=1; j<fields.length; j++ ){
   dat[dat.length] = javadocString(fields[j].description);
   dat[dat.length] = 'final public static int FIELD_' + fields[j].name.toUpperCase() + ' = ' + j + ';';
  }
  dat[dat.length] = '';
  
  //フィールド変数
  for( var j=1; j<fields.length; j++ ){
   dat[dat.length] = javadocString(fields[j].description);
   var type = getType(fields[j].type);
   dat[dat.length] = ['private', type, fields[j].name.toLowerCase()].join(' ') + ';';
  }
  
  //ファイル出力
  var f = fso.CreateTextFile(fileName, true);
  f.WriteLine(dat.join(String.fromCharCode(13) + String.fromCharCode(10)));
  f.Close();
 }
})();

2012/02/10

【shell】 ファイルを指定ファイル数ごとにサブディレクトリに移動する

何年か前に書いたShell Script。
cutコマンドの使い方がすごい強引だったので、アップしてみます。
またどこかで使えるかも・・・


  • ファイルを指定されたディレクトリ以下に移動する
  • ファイル数が指定された数を越えないように、サブディレクトリに分割する


#! /bin/sh
# mv_files.sh
# ファイルを別ディレクトリに移動する
# 指定ファイル数ごとにサブディレクトリを作成する

FILE_NUM=30
TO_DIR=/work/mv_test/to_dir
FROM_DIR=/work/mv_test/from_dir

count=`find ${FROM_DIR} -type f | wc -l`
until [ $count -eq 0 ]; do
 if [ $count -le $FILE_NUM ]; then
  n=$count
 else
  n=$FILE_NUM
 fi
 # フォルダ作成
 dir_name=`date "+%Y%m%d%H%M%S%N"`
 mkdir "${TO_DIR}/${dir_name}"

 echo ${n}

 # ファイル移動
 files=`find ${FROM_DIR} -type f | sort | cut -d "
" -f 1-${n}`
 mv ${files} "${TO_DIR}/${dir_name}"

 count=`find ${FROM_DIR} -type f | wc -l`
done

2012/02/09

【PowerShell】 日時の処理 (get-date, new-timespan)


get-date


DateTimeオブジェクトを作成する。
System.DateTime。
Get-Date [[-date] <DateTime>] [-displayHint {<Date> | <Time> | <DateTime>}]
[-format <string>] [-year <int>] [-month <int>] [-day <int>] [-hour <int>]
[-minute <int>] [-second <int>] [<CommonParameters>]
Get-Date [[-date] <DateTime>] [-displayHint {<Date> | <Time> | <DateTime>}]
[-uFormat <string>] [-year <int>] [-month <int>] [-day <int>] [-hour <int>]
[-minute <int>] [-second <int>] [<CommonParameters>]

  • 来月の日時を取得する
(get-date).AddMonths(1)

  • 30日後を取得
$ts = new-timespan -days 30
(get-date).add($ts)


new-timespan

TimeSpanオブジェクトを作成する。
System.TimeSpan。
New-TimeSpan [[-start] <DateTime>] [[-end] <DateTime>] [<CommonParameters>]
New-TimeSpan [-days <int>] [-hours <int>] [-minutes <int>] [-seconds <int>] [<CommonParameters>]

  • 指定日から今日までの日数を取得する
(new-timespan (get-date -year 2000 -month 4 -day 1)).days

2012/02/04

「新しいタブ」の拡張



先日作成した「新しいタブ」に日時を表示するGoogle Chrome拡張を修正。


  • TwitterのBootstrapが2.0にバージョンアップしていたので、試してみる
  • CSS3のプロパティも使用して、それなりに映える見栄えを目指す



履歴検索機能を追加したものの、イマイチ使い勝手が良くない。
標準の「新しいタブ」に表示されている "よくアクセスするページ" はどうやって取得するんだろう・・・


https://github.com/Kazunori-Kimura/ClockNewTab

2012/01/31

カスタマイズの幅が広がるアプリ - Minimalistic Text

AndroidのWidgetアプリ。

文字で時間や天気、簡単なシステム情報を表示するWidgetを自由に作成、配置できる。

スクリーンショットみたいにかっこ良く文字色やレイアウトを行うのは結構な手間がかかるけど、フォントと文字サイズをいじるだけでもソレナリに良い雰囲気になった。

ホームアプリとかロック画面とか、Androidはいろいろカスタマイズできるけど、チマチマ触ってるとあっという間に時間が過ぎちゃうんだよね。


Minimalistic Text[https://market.android.com/details?id=de.devmil.minimaltext]

Chromeの新規タブに時間を表示する

スクリーンショット

Google Chromeの拡張機能開発手順の習得も兼ねて、新規タブに日時を表示するだけの拡張を作成してみた。
https://github.com/Kazunori-Kimura/ClockNewTab

ホントは日時の表示だけやるつもりだったんだけど、Webアプリの起動/アンインストールが新規タブからしかできない(?)ので、それだけは実装。

これくらいの機能であれば、1日で出来るくらい簡単。
ブックマークとか履歴の検索機能をつけたら、Chrome Web Storeにアップしてみよう。


元々のアイデアはこちら[http://www.lifehacker.jp/2012/01/post_1739.html]から。